关于bouncycastle下国密SM2 API的使用
本文不对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)