JS實現國密算法SM2加密,後端Java解密

項目涉及保密傳輸,要求使用國密算法,一般遇到類似問題首先想到的就是使用非對稱加密,後端生成密鑰對,將公鑰交給前端,前端用公鑰加密數據,後端用私鑰對數據解密。項目的複雜度在於國密的非對稱加密算法SM2的Java及JS實現。

Java版比較好辦,較新版本的bouncycastle就支持了SM2/SM3/SM4,麻煩在於JS版,找了很多都有問題,直到遇到了這個項目:https://github.com/Saberization/SM2,感謝作者。分別整理下前端後端的實現過程:

 

後端首先引入bouncycastle,Maven配置如下:

 

<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bcprov-jdk15on</artifactId>
  <version>1.65</version>
</dependency>

 

後端Java代碼如下:

//生成密鑰對
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();

//私鑰,16進制格式,自己保存,格式如a2081b5b81fbea0b6b973a3ab6dbbbc65b1164488bf22d8ae2ff0b8260f64853
BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
String privateKeyHex = privatekey.toString(16);

//公鑰,16進制格式,發給前端,格式如04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba
ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));

 

前端Javascript示例代碼,寫了個頁面:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>SM2-TEST</title>
  <script src="crypto-js.js"></script>
  <script src="sm2.js"></script>
  <script>
  function encrypt() {
    //公鑰,16進制格式,由後端生成
    var pubkeyHex = "04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba";
    var encryptData = sm2Encrypt("SM2 Encryption Test", pubkeyHex, 0);
    console.log(encryptData);
  }
  </script>
</head>
<body onload="encrypt()">
</body>
</html>

 

執行會生成密文,每次生成都會不同,比如我的環境某次生成如下:

 

04be17bf6fe47da1f34a01ad0ff67901241b72d103e998f2f7cc78a004703bdfb8d2c6e3939f4f708f3a57d872d58ec5c41bbe5976666bcb01acea43f5a1c68a62cc117c24821d17c3023035641894d7c978a5521f8dc6798515550c73071f9703602e0ee490157729b648c1cc3eb929c1a0501e12a216d42461117402

 

後端嘗試解密:

//JS加密產生的密文
String cipherData = "04be17bf6fe47da1f34a01ad0ff67901241b72d103e998f2f7cc78a004703bdfb8d2c6e3939f4f708f3a57d872d58ec5c41bbe5976666bcb01acea43f5a1c68a62cc117c24821d17c3023035641894d7c978a5521f8dc6798515550c73071f9703602e0ee490157729b648c1cc3eb929c1a0501e12a216d42461117402";
byte[] cipherDataByte = Hex.decode(cipherData);

//剛纔的私鑰Hex,先還原私鑰
String privateKey = "a2081b5b81fbea0b6b973a3ab6dbbbc65b1164488bf22d8ae2ff0b8260f64853";
BigInteger privateKeyD = new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

//用私鑰解密
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(false, privateKeyParameters);

//processBlock得到Base64格式,記得解碼
byte[] arrayOfBytes = Base64.getDecoder().decode(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));

//得到明文:SM2 Encryption Test
String data = new String(arrayOfBytes);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章