原文:http://www.jonllen.cn/jonllen/work/162.aspxjava
前段時間將系統的RSA算法所有升級爲SM2國密算法,密碼機和UKey硬件設備大都同時支持RSA和SM2算法,只是應用系統的加解密簽名驗證須要修改,這個更改底層調用的加密動態庫來,原來RSA用的對稱加密算法DES(AES)和摘要MD5(SHA1)也相應改變,分別對應SM一、SM3算法,SM1算法基於硬件實現,SM二、SM3算法已公開。算法
SM2簽名驗證算法數據結構
SM2簽名一樣也是須要先摘要原文數據,即先使用SM3密碼雜湊算法計算出32byte摘要。SM3須要摘要簽名方ID(默認1234567812345678)、曲線參數a,b,Gx,Gy、共鑰座標(x,y)計算出Z值,而後再雜湊原文得出摘要數據。這個地方要注意曲線參數和座標點都是32byte,在轉換爲BigInteger大數計算轉成字節流時要去掉空補位,不然可能會出現摘要計算不正確的問題。SM2簽名實現以下:dom
- public static BigInteger[] Sm2Sign(byte[] md, AsymmetricCipherKeyPair keypair)
- {
- SM3Digest sm3 = new SM3Digest();
-
- ECPublicKeyParameters ecpub = (ECPublicKeyParameters)keypair.Public;
-
- byte[] z = SM2CryptoServiceProvider.Sm2GetZ(Encoding.Default.GetBytes(SM2CryptoServiceProvider.userId), ecpub.Q);
- sm3.BlockUpdate(z, 0, z.Length);
-
- byte[] p = md;
- sm3.BlockUpdate(p, 0, p.Length);
-
- byte[] hashData = new byte[32];
- sm3.DoFinal(hashData, 0);
-
-
- BigInteger e = new BigInteger(1, hashData);
-
- BigInteger k = null;
- ECPoint kp = null;
- BigInteger r = null;
- BigInteger s = null;
- BigInteger userD = null;
-
- do
- {
- do
- {
-
- ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)keypair.Private;
- k = ecpriv.D;
- kp = ecpub.Q;
-
- userD = ecpriv.D;
-
-
- r = e.Add(kp.X.ToBigInteger());
- r = r.Mod(ecc_n);
- }
- while (r.Equals(BigInteger.Zero) || r.Add(k).Equals(ecc_n));
-
-
- BigInteger da_1 = userD.Add(BigInteger.One);
- da_1 = da_1.ModInverse(ecc_n);
-
- s = r.Multiply(userD);
- s = k.Subtract(s).Mod(ecc_n);
- s = da_1.Multiply(s).Mod(ecc_n);
- }
- while (s.Equals(BigInteger.Zero));
-
- byte[] btRS = new byte[64];
- byte[] btR = r.ToByteArray();
- byte[] btS = s.ToByteArray();
- Array.Copy(btR, btR.Length - 32, btRS, 0, 32);
- Array.Copy(btS, btS.Length - 32, btRS, 32, 32);
-
- return new BigInteger[] { r, s };
- }
SM2算法是基於ECC算法的,簽名一樣返回2個大數,共64byte。因爲原來RSA算法已很廣泛支持,要實現RSA的簽名驗籤都有標準庫的實現,而SM2是國密算法在國際上尚未標準通用,算法Oid標識在X509標準中是沒定義的。在.Net或Java中能夠基於使用BouncyCastle加密庫實現,開源的也比較好學習擴展。SM2算法驗籤可使用軟驗籤,便可以不須要使用硬件設備,一樣使用原始數據、簽名、證書(公鑰)來實現對簽名方驗證,保證數據完整性未被篡改。驗證過程一樣需先摘要原文數據,公鑰在證書中是以一個66byte的BitString,去掉前面標記位即64byte爲共鑰座標(x,y),中間分割截取再以Hex方式轉成BigInteger大數計算,驗籤代碼以下:ide
- public static bool Verify(byte[] msg, byte[] signData, byte[] certData)
- {
-
- X509Certificate2 x5092 = new X509Certificate2(certData);
- byte[] certPK = x5092.GetPublicKey();
-
- certPK = SubByte(certPK, 1, 64);
-
- byte[] certPKX = SubByte(certPK, certPK.Length - 32 - 32, 32);
- byte[] certPKY = SubByte(certPK, certPK.Length - 32, 32);
-
-
- System.String strcertPKX = ByteToHexStr(certPKX);
- System.String strcertPKY = ByteToHexStr(certPKY);
- BigInteger biX = new BigInteger(strcertPKX, 16);
- BigInteger biY = new BigInteger(strcertPKY, 16);
-
-
- ECFieldElement x = new FpFieldElement(ecc_p, biX);
- ECFieldElement y = new FpFieldElement(ecc_p, biY);
-
- ECPoint userKey = new FpPoint(ecc_curve, x, y);
-
-
- SM3Digest sm3 = new SM3Digest();
- byte[] z = Sm2GetZ(Encoding.Default.GetBytes(userId), userKey);
- sm3.BlockUpdate(z, 0, z.Length);
-
-
- byte[] p = msg;
- sm3.BlockUpdate(p, 0, p.Length);
-
- byte[] md = new byte[32];
- sm3.DoFinal(md, 0);
-
-
- byte[] btR = SubByte(signData, 0, 32);
- byte[] btS = SubByte(signData, 32, 32);
-
-
- System.String strR = ByteToHexStr(btR);
- System.String strS = ByteToHexStr(btS);
- BigInteger r = new BigInteger(strR, 16);
- BigInteger s = new BigInteger(strS, 16);
-
-
- BigInteger e = new BigInteger(1, md);
-
- BigInteger t = r.Add(s).Mod(ecc_n);
-
- if (t.Equals(BigInteger.Zero))
- return false;
-
-
- ECPoint x1y1 = ecc_point_g.Multiply(s);
- x1y1 = x1y1.Add(userKey.Multiply(t));
-
-
- BigInteger R = e.Add(x1y1.X.ToBigInteger()).Mod(ecc_n);
-
- return r.Equals(R);
- }
製做SM2證書學習
基於BouncyCastle開源庫,能夠輕鬆製做X509證書、CRL、pkcs十、pkcs12,支持國際通用的RSA、ECC算法。製做SM2證書能夠經過擴展BouncyCastle庫來實現,需加入SM2簽名算法DerObjectIdentifier標識1.2.156.10197.1.501(基於SM3的SM2算法簽名),密鑰對的生成使用國密推薦曲線參數,而後如上所示自行實現SM2簽名驗證算法。X509證書由證書主體、證書籤名算法標識、簽名組成,和RSA證書主要不一樣的是SM2證書的簽名算法標識和簽名,及證書公鑰使用ECKeyParameters。生成自簽名SM2證書代碼以下:ui
SM2證書生成
- public static Org.BouncyCastle.X509.X509Certificate MakeRootCert(string filePath, IDictionary subjectNames)
- {
- AsymmetricCipherKeyPair keypair = SM2CryptoServiceProvider.SM2KeyPairGenerator.GenerateKeyPair();
- ECPublicKeyParameters pubKey = (ECPublicKeyParameters)keypair.Public;
- ECPrivateKeyParameters priKey = (ECPrivateKeyParameters)keypair.Private;
-
-
-
- X509Name issuerDN = new X509Name(GetDictionaryKeys(subjectNames), subjectNames);
- X509Name subjectDN = issuerDN;
-
- SM2X509V3CertificateGenerator sm2CertGen = new SM2X509V3CertificateGenerator();
-
- sm2CertGen.SetSerialNumber(new BigInteger(128, new Random()));
- sm2CertGen.SetIssuerDN(issuerDN);
- sm2CertGen.SetNotBefore(DateTime.UtcNow.AddDays(-1));
- sm2CertGen.SetNotAfter(DateTime.UtcNow.AddDays(365 * 10));
- sm2CertGen.SetSubjectDN(subjectDN);
- sm2CertGen.SetPublicKey(pubKey);
-
-
- sm2CertGen.SetSignatureAlgorithm("SM3WITHSM2");
-
- sm2CertGen.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
- sm2CertGen.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(pubKey));
- sm2CertGen.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(pubKey));
- sm2CertGen.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(6));
-
-
- Org.BouncyCastle.X509.X509Certificate sm2Cert = sm2CertGen.Generate(keypair);
-
- sm2Cert.CheckValidity();
- sm2Cert.Verify(pubKey);
-
- return sm2Cert;
- }
X509證書使用ASN1語法進行編碼,是用類型標識、長度和值序列來描述數據結構的。SM2證書在製做設置公鑰時,默認會帶ECKeyParameters參數,並無SM2的公鑰參數1.2.156.10197.1.301,所以須要本身寫個SM2橢圓曲線密碼算法標識對象,這樣在生成的證書中就能夠看到公鑰參數字段,以下所示:this
SM2證書公鑰標識
- using System;
-
- using Org.BouncyCastle.Asn1.X509;
- using Org.BouncyCastle.Asn1;
-
-
- namespace Common.Security
- {
- public class SM2AlgorithmIdentifier
- : AlgorithmIdentifier
- {
-
- private readonly bool parametersDefined;
-
- public SM2AlgorithmIdentifier(
- DerObjectIdentifier objectID):base(objectID)
- {
-
- }
-
-
- public SM2AlgorithmIdentifier(
- DerObjectIdentifier objectID,
- Asn1Encodable parameters)
- : base(objectID, parameters)
- {
- this.parametersDefined = true;
- }
-
-
- public override Asn1Object ToAsn1Object()
- {
- DerObjectIdentifier sm2Identifier = new DerObjectIdentifier("1.2.156.10197.1.301");
- Asn1EncodableVector v = new Asn1EncodableVector(base.ObjectID, sm2Identifier);
- return new DerSequence(v);
- }
-
- }
- }
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