本文不對SM2做過多的介紹,主要介紹java bouncycastle庫關於SM2的相關API的使用及注意事項
1. SM2 簽名:
注意:
1)簽名格式ASN1(描述了一種對數據進行表示、編碼、傳輸和解碼的數據格式),包括兩個大整數。
2)注意USER_ID的一致性(規范默認是"1234567812345678"),否則影響驗簽。
主要代碼
SM2Signer localSM2Signer = new SM2Signer(); Security.addProvider(new BouncyCastleProvider()); PublicKey publicKey = cert.getPublicKey(); ECPublicKeyParameters param = null; if (publicKey instanceof BCECPublicKey) { BCECPublicKey localECPublicKey = (BCECPublicKey)publicKey; ECParameterSpec localECParameterSpec = localECPublicKey.getParameters(); ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(), localECParameterSpec.getG(), localECParameterSpec.getN()); param = new ECPublicKeyParameters(localECPublicKey.getQ(),localECDomainParameters); } ByteArrayInputStream inStream = new ByteArrayInputStream(signdatebyte); ASN1InputStream asnInputStream = new ASN1InputStream(inStream); ASN1Primitive derObject = asnInputStream.readObject(); BigInteger R = null; BigInteger S = null; if (derObject instanceof ASN1Sequence) { ASN1Sequence signSequence = (ASN1Sequence) derObject; Enumeration<ASN1Integer> enumer = signSequence.getObjects(); R = ((ASN1Integer)enumer.nextElement()).getValue(); S = ((ASN1Integer)enumer.nextElement()).getValue(); } ParametersWithID parametersWithID = new ParametersWithID(param,SM2_USER_ID); localSM2Signer.init(false, parametersWithID); boolean res = localSM2Signer.verifySignature(databyte, BigIntegerUtil.toPositiveInteger(R.toByteArray()), BigIntegerUtil.toPositiveInteger(S.toByteArray())); return res;
2. SM2 驗簽:
注意:同簽名
主要代碼
SM2Signer localSM2Signer = new SM2Signer(); Security.addProvider(new BouncyCastleProvider()); PublicKey publicKey = cert.getPublicKey(); ECPublicKeyParameters param = null; if (publicKey instanceof BCECPublicKey) { BCECPublicKey localECPublicKey = (BCECPublicKey)publicKey; ECParameterSpec localECParameterSpec = localECPublicKey.getParameters(); ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(), localECParameterSpec.getG(), localECParameterSpec.getN()); param = new ECPublicKeyParameters(localECPublicKey.getQ(),localECDomainParameters); } ByteArrayInputStream inStream = new ByteArrayInputStream(signdatebyte); ASN1InputStream asnInputStream = new ASN1InputStream(inStream); ASN1Primitive derObject = asnInputStream.readObject(); BigInteger R = null; BigInteger S = null; if (derObject instanceof ASN1Sequence) { ASN1Sequence signSequence = (ASN1Sequence) derObject; Enumeration<ASN1Integer> enumer = signSequence.getObjects(); R = ((ASN1Integer)enumer.nextElement()).getValue(); S = ((ASN1Integer)enumer.nextElement()).getValue(); } ParametersWithID parametersWithID = new ParametersWithID(param,SM2_USER_ID); localSM2Signer.init(false, parametersWithID); boolean res = localSM2Signer.verifySignature(databyte, BigIntegerUtil.toPositiveInteger(R.toByteArray()), BigIntegerUtil.toPositiveInteger(S.toByteArray()));
3. 加解密
public static String encrypt(String data, PublicKey publicKey) { ECPublicKeyParameters localECPublicKeyParameters = null; if (publicKey instanceof BCECPublicKey) { BCECPublicKey localECPublicKey = (BCECPublicKey)publicKey; ECParameterSpec localECParameterSpec = localECPublicKey.getParameters(); ECDomainParameters localECDomainParameters = new ECDomainParameters( localECParameterSpec.getCurve(), localECParameterSpec.getG(), localECParameterSpec.getN()); localECPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), localECDomainParameters); } SM2Engine localSM2Engine = new SM2Engine(); localSM2Engine.init(true, new ParametersWithRandom(localECPublicKeyParameters, new SecureRandom())); byte[] arrayOfByte2; try { arrayOfByte2 = localSM2Engine.processBlock(data.getBytes(), 0, data.getBytes().length); return new String(Base64.encode(arrayOfByte2)); } catch (InvalidCipherTextException e) { e.printStackTrace(); return null; } } public static String decrypt(String encodedata, PrivateKey privateKey) { byte[] encodedataByte = Base64.decode(encodedata.getBytes()); SM2Engine localSM2Engine = new SM2Engine(); BCECPrivateKey sm2PriK = (BCECPrivateKey)privateKey; ECParameterSpec localECParameterSpec = sm2PriK.getParameters(); ECDomainParameters localECDomainParameters = new ECDomainParameters( localECParameterSpec.getCurve(), localECParameterSpec.getG(), localECParameterSpec.getN()); ECPrivateKeyParameters localECPrivateKeyParameters = new ECPrivateKeyParameters( sm2PriK.getD(), localECDomainParameters); localSM2Engine.init(false, localECPrivateKeyParameters); try { byte[] arrayOfByte3 = localSM2Engine.processBlock(encodedataByte, 0, encodedataByte.length); return new String(arrayOfByte3); } catch (InvalidCipherTextException e) { e.printStackTrace(); return null; } }
4. pkcs#7(CMS)格式
可以使用bouncycastle的CMS包下的API進行封裝,或者自己實現,或使用j4sign庫(基於bouncycastle)實現。這里就不貼代碼了。
注意:相關OID
國密標准GM/T 0010定義的oid如下: 數據類型data 1.2.156.10197.6.1.4.2.1 簽名數據類型signedData 1.2.156.10197.6.1.4.2.2 數字信封數據類型envelopedData 1.2.156.10197.6.1.4.2.3 簽名及數字信封數據類型signedAndEnvelopedData 1.2.156.10197.6.1.4.2.4 加密數據類型encryptedData 1.2.156.10197.6.1.4.2.5 密鑰協商類型keyAgreementInfo 1.2.156.10197.6.1.4.2.6
參考資料:
2.https://tools.ietf.org/html/rfc2315
3.http://j4sign.sourceforge.net/
4.http://gmssl.org/docs/oid.html
5.https://stackoverflow.com/questions/39925946/generate-cmssigneddata-with-no-private-key-in-java
6.https://github.com/bcgit/bc-java
7.https://www.zhihu.com/question/62639301/answer/214184309
轉載請注明原博客地址http://www.cnblogs.com/jeffreyluo/p/sm2forjava.html)