java实现sm2证书基于BouncyCastle

SM2签名同样也是需要先摘要原文数据,即先使用SM3密码杂凑算法计算出32byte摘要。SM3需要摘要签名方ID(默认1234567812345678)、曲线参数a,b,Gx,Gy、共钥坐标(x,y)计算出Z值,然后再杂凑原文得出摘要数据。这个地方要注意曲线参数和坐标点都是32byte,在转换为BigInteger大数计算转成字节流时要去掉空补位,否则可能会出现摘要计算不正确的问题。SM2签名实现如下:

 
  
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 SM2签名
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 public static BigInteger[] Sm2Sign( byte [] md, AsymmetricCipherKeyPair keypair) java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 { java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 SM3Digest sm3 = new SM3Digest(); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 ECPublicKeyParameters ecpub = (ECPublicKeyParameters)keypair.Public; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客byte[] z = SM2CryptoServiceProvider.Sm2GetZ(Encoding.Default.GetBytes(SM2CryptoServiceProvider.userId), ecpub.Q); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm3.BlockUpdate(z, 0, z.Length); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客byte[] p = md; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm3.BlockUpdate(p, 0, p.Length); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客byte[] hashData = new byte[32]; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm3.DoFinal(hashData, 0); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客// e java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 BigInteger e = new BigInteger(1, hashData); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客// k java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 BigInteger k = null; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 ECPoint kp = null; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 BigInteger r = null; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 BigInteger s = null; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 BigInteger userD = null; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客do java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客{ java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客do java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客{ java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)keypair.Private; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 k = ecpriv.D; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 kp = ecpub.Q; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 userD = ecpriv.D; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客// r java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 r = e.Add(kp.X.ToBigInteger()); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 r = r.Mod(ecc_n); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 } java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客while (r.Equals(BigInteger.Zero) || r.Add(k).Equals(ecc_n)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客// (1 + dA)~-1 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 BigInteger da_1 = userD.Add(BigInteger.One); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 da_1 = da_1.ModInverse(ecc_n); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客// s java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 s = r.Multiply(userD); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 s = k.Subtract(s).Mod(ecc_n); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 s = da_1.Multiply(s).Mod(ecc_n); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 } java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客while (s.Equals(BigInteger.Zero)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客byte[] btRS = new byte[64]; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客byte[] btR = r.ToByteArray(); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客byte[] btS = s.ToByteArray(); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 Array.Copy(btR, btR.Length - 32, btRS, 0, 32); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 Array.Copy(btS, btS.Length - 32, btRS, 32, 32); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客return new BigInteger[] { r, s }; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客}

SM2算法是基于ECC算法的,签名同样返回2个大数,共64byte。由于原来RSA算法已很普遍支持,要实现RSA的签名验签都有标准库的实现,而SM2是国密算法在国际上还没有标准通用,算法Oid标识在X509标准中是没定义的。在.Net或Java中可以基于使用BouncyCastle加密库实现,开源的也比较好学习扩展。SM2算法验签可以使用软验签,即可以不需要使用硬件设备,同样使用原始数据、签名、证书(公钥)来实现对签名方验证,保证数据完整性未被篡改。验证过程同样需先摘要原文数据,公钥在证书中是以一个66byte的BitString,去掉前面标记位即64byte为共钥坐标(x,y),中间分割截取再以Hex方式转成BigInteger大数计算,验签代码如下:

java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 SM2验签
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 public static bool Verify( byte [] msg, byte [] signData, byte [] certData)
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
{
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            X509Certificate2 x5092
= new X509Certificate2(certData);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] certPK = x5092.GetPublicKey();
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            certPK
= SubByte(certPK, 1, 64);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] certPKX = SubByte(certPK, certPK.Length - 32 - 32, 32);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] certPKY = SubByte(certPK, certPK.Length - 32, 32);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            System.String strcertPKX
= ByteToHexStr(certPKX);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            System.String strcertPKY
= ByteToHexStr(certPKY);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            BigInteger biX
= new BigInteger(strcertPKX, 16);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            BigInteger biY
= new BigInteger(strcertPKY, 16);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            ECFieldElement x
= new FpFieldElement(ecc_p, biX);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            ECFieldElement y
= new FpFieldElement(ecc_p, biY);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            ECPoint userKey
= new FpPoint(ecc_curve, x, y);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            SM3Digest sm3
= new SM3Digest();
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] z = Sm2GetZ(Encoding.Default.GetBytes(userId), userKey);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            sm3.BlockUpdate(z,
0, z.Length);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] p = msg;
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            sm3.BlockUpdate(p,
0, p.Length);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] md = new byte[32];
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            sm3.DoFinal(md,
0);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] btR = SubByte(signData, 0, 32);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
byte[] btS = SubByte(signData, 32, 32);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            System.String strR
= ByteToHexStr(btR);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            System.String strS
= ByteToHexStr(btS);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            BigInteger r
= new BigInteger(strR, 16);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            BigInteger s
= new BigInteger(strS, 16);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
// e_
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
            BigInteger e = new BigInteger(1, md);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
// t
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
            BigInteger t = r.Add(s).Mod(ecc_n);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
if (t.Equals(BigInteger.Zero))
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客               
return false;
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
// x1y1
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
            ECPoint x1y1 = ecc_point_g.Multiply(s);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客            x1y1
= x1y1.Add(userKey.Multiply(t));
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
// R
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
            BigInteger R = e.Add(x1y1.X.ToBigInteger()).Mod(ecc_n);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客           
return r.Equals(R);
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客}

制作SM2证书

基于BouncyCastle开源库,可以轻松制作X509证书、CRL、pkcs10、pkcs12,支持国际通用的RSA、ECC算法。制作SM2证书可以通过扩展BouncyCastle库来实现,需加入SM2签名算法DerObjectIdentifier标识1.2.156.10197.1.501(基于SM3的SM2算法签名),密钥对的生成使用国密推荐曲线参数,然后如上所示自行实现SM2签名验证算法。X509证书由证书主体、证书签名算法标识、签名组成,和RSA证书主要不同的是SM2证书的签名算法标识和签名,及证书公钥使用ECKeyParameters。生成自签名SM2证书代码如下:

 
   
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 SM2证书生成
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 public static Org.BouncyCastle.X509.X509Certificate MakeRootCert( string filePath, IDictionary subjectNames) java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 { java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 AsymmetricCipherKeyPair keypair = SM2CryptoServiceProvider.SM2KeyPairGenerator.GenerateKeyPair(); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 ECPublicKeyParameters pubKey = (ECPublicKeyParameters)keypair.Public; //CA公钥 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 ECPrivateKeyParameters priKey = (ECPrivateKeyParameters)keypair.Private; //CA私钥 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 X509Name issuerDN = new X509Name(GetDictionaryKeys(subjectNames), subjectNames); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 X509Name subjectDN = issuerDN; //自签证书,两者一样 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 SM2X509V3CertificateGenerator sm2CertGen = new SM2X509V3CertificateGenerator(); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客//X509V3CertificateGenerator sm2CertGen = new X509V3CertificateGenerator(); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.SetSerialNumber(new BigInteger(128, new Random())); //128位 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.SetIssuerDN(issuerDN); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.SetNotBefore(DateTime.UtcNow.AddDays(-1)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.SetNotAfter(DateTime.UtcNow.AddDays(365 * 10)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.SetSubjectDN(subjectDN); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.SetPublicKey(pubKey); //公钥 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.SetSignatureAlgorithm("SM3WITHSM2"); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(pubKey)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(pubKey)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2CertGen.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(6)); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 Org.BouncyCastle.X509.X509Certificate sm2Cert = sm2CertGen.Generate(keypair); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2Cert.CheckValidity(); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 sm2Cert.Verify(pubKey); java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客return sm2Cert; java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客}

X509证书使用ASN1语法进行编码,是用类型标识、长度和值序列来描述数据结构的。SM2证书在制作设置公钥时,默认会带ECKeyParameters参数,并没有SM2的公钥参数1.2.156.10197.1.301,因此需要自己写个SM2椭圆曲线密码算法标识对象,这样在生成的证书中就可以看到公钥参数字段,如下所示:

 
   
java实现sm2证书基于BouncyCastle - 崔江威 - 崔江威的博客 SM2证书公钥标识

SM2算法是国密局公布的公钥密码算法,在相当强度下密钥比RSA短,在使用智能卡有限空间存储时非常可贵。目前国内很多CA大都升级支持SM2算法证书,相信以后会慢慢地推广更多应用,也期望之后能与国际标准接轨。


附:

国密推荐256位曲线参数

  • p=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
  • a=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC
  • b=28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93
  • n=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123
  • Gx=32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7
  • Gy=BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0

你可能感兴趣的:(java实现sm2证书基于BouncyCastle)