• C# .NET 云南农信国密签名(SM2)简要解析


    BouncyCastle库(BC库)与云南农信最大的区别是 :

    BC库 SM2Signer.Init()  方法比云南农信多了最后3行代码:

    digest.Reset();
                z = GetZ(userID);
    
                digest.BlockUpdate(z, 0, z.Length);

    云南农信这3行是没有的。

    多了这3行会导致云南农信,验证签名不同过。

    我们需要新写一个SM2Signer名为:SM2Signer1,继承 ISigner,然后在Init 方法里去掉这3行。

    我觉得原有的 GenerateSignature() 对 r,s 的包装太复杂,就新写了个 GenerateSignatureWay2()。

    云南农信的JAVA DEMO 中是先用 SHA256 HASH,注释中说是再用 SM3withSM2 签名。但看起来,直接就是 SHA256withSM2 签名,不是 (SHA256 HASH  +  SM3withSM2)。

    Nuget 中安装 BouncyCastle 库,1.8.9 及以上版本。

    SM2Signer1 类:

    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Crypto.Signers;
    using Org.BouncyCastle.Math;
    using Org.BouncyCastle.Math.EC;
    using Org.BouncyCastle.Math.EC.Multiplier;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Utilities;
    using Org.BouncyCastle.Utilities.Encoders;
    using System;
    
    namespace TheSM2.Utils
    {
        public class SM2Signer1 : ISigner
        {
    
            private readonly IDsaKCalculator kCalculator = new RandomDsaKCalculator();
    
            private readonly IDigest digest;
    
            private readonly IDsaEncoding encoding;
    
            private ECDomainParameters ecParams;
    
            private ECPoint pubPoint;
    
            private ECKeyParameters ecKey;
    
            private byte[] z;
    
            public virtual string AlgorithmName
            {
                get { return "SM2Sign"; }
            }
    
            public SM2Signer1(IDsaEncoding encoding, IDigest digest)
            {
                this.encoding = encoding;
                this.digest = digest;
            }
    
            public SM2Signer1(IDigest digest)
                : this(StandardDsaEncoding.Instance, digest)
            {
            }
    
            public virtual void Init(bool forSigning, ICipherParameters parameters)
            {
                byte[] userID;
    
                ICipherParameters cipherParameters;
                if (parameters is ParametersWithID)
                {
                    cipherParameters = ((ParametersWithID)parameters).Parameters;
                    userID = ((ParametersWithID)parameters).GetID();
                    if (((ParametersWithID)parameters).GetID().Length >= 8192)
                    {
                        throw new ArgumentException("SM2 user ID must be less than 2^16 bits long");
                    }
                }
                else
                {
                    cipherParameters = parameters;
                    userID = Hex.DecodeStrict("1234567812345678");
                }
    
                if (forSigning)
                {
                    if (cipherParameters is ParametersWithRandom)
                    {
                        ParametersWithRandom parametersWithRandom = (ParametersWithRandom)cipherParameters;
                        ecKey = (ECKeyParameters)parametersWithRandom.Parameters;
                        ecParams = ecKey.Parameters;
                        kCalculator.Init(ecParams.N, parametersWithRandom.Random);
                    }
                    else
                    {
                        ecKey = (ECKeyParameters)cipherParameters;
                        ecParams = ecKey.Parameters;
                        kCalculator.Init(ecParams.N, new SecureRandom());
                    }
                    pubPoint = CreateBasePointMultiplier().Multiply(ecParams.G, ((ECPrivateKeyParameters)ecKey).D).Normalize();
                }
    
                else
                {
                    ecKey = (ECKeyParameters)cipherParameters;
                    ecParams = ecKey.Parameters;
                    pubPoint = ((ECPublicKeyParameters)ecKey).Q;
                }
    
    
                //BC的 SM2Signer 是带下面3行的。而云南农信则不带。
                //digest.Reset();
                //z = GetZ(userID);
                //digest.BlockUpdate(z, 0, z.Length);
            }
    
            protected virtual ECMultiplier CreateBasePointMultiplier()
            {
                return new FixedPointCombMultiplier();
            }
    
            public virtual void Update(byte b)
            {
                digest.Update(b);
            }
            public virtual void BlockUpdate(byte[] buf, int off, int len)
            {
                digest.BlockUpdate(buf, off, len);
            }
            protected virtual BigInteger CalculateE(BigInteger n, byte[] message)
            {
                // TODO Should hashes larger than the order be truncated as with ECDSA?
                return new BigInteger(1, message);
            }
    
    
            /// <summary>
            /// 返回了N,r,s 并用StandardDsaEncoding 做了包装。需要反向解析出 r,s 。
            /// </summary>
            /// <returns></returns>
            /// <exception cref="CryptoException"></exception>
            public virtual byte[] GenerateSignature()
            {
                byte[] eHash = DigestUtilities.DoFinal(digest);
    
                BigInteger n = ecParams.N;
                BigInteger e = CalculateE(n, eHash);
                BigInteger d = ((ECPrivateKeyParameters)ecKey).D;
    
                BigInteger r, s;
    
                ECMultiplier basePointMultiplier = CreateBasePointMultiplier();
    
                // 5.2.1 Draft RFC:  SM2 Public Key Algorithms
                do // generate s
                {
                    BigInteger k;
                    do // generate r
                    {
                        // A3
                        k = kCalculator.NextK();
    
                        // A4
                        ECPoint p = basePointMultiplier.Multiply(ecParams.G, k).Normalize();
    
                        // A5
                        r = e.Add(p.AffineXCoord.ToBigInteger()).Mod(n);
                    }
                    while (r.SignValue == 0 || r.Add(k).Equals(n));
    
                    // A6
                    BigInteger dPlus1ModN = BigIntegers.ModOddInverse(n, d.Add(BigIntegers.One));
    
                    s = k.Subtract(r.Multiply(d)).Mod(n);
                    s = dPlus1ModN.Multiply(s).Mod(n);
                }
                while (s.SignValue == 0);
    
                // A7
                try
                {
                    return encoding.Encode(ecParams.N, r, s);
                }
                catch (Exception ex)
                {
                    throw new CryptoException("unable to encode signature: " + ex.Message, ex);
                }
            }
    
            private bool VerifySignature(BigInteger r, BigInteger s)
            {
                BigInteger n = ecParams.N;
    
                // 5.3.1 Draft RFC:  SM2 Public Key Algorithms
                // B1
                if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
                    return false;
    
                // B2
                if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0)
                    return false;
    
                // B3
                byte[] eHash = DigestUtilities.DoFinal(digest);
    
                // B4
                BigInteger e = CalculateE(n, eHash);
    
                // B5
                BigInteger t = r.Add(s).Mod(n);
                if (t.SignValue == 0)
                    return false;
    
                // B6
                ECPoint q = ((ECPublicKeyParameters)ecKey).Q;
                ECPoint x1y1 = ECAlgorithms.SumOfTwoMultiplies(ecParams.G, s, q, t).Normalize();
                if (x1y1.IsInfinity)
                    return false;
    
                // B7
                return r.Equals(e.Add(x1y1.AffineXCoord.ToBigInteger()).Mod(n));
            }
    
            public virtual bool VerifySignature(byte[] signature)
            {
                try
                {
                    BigInteger[] rs = encoding.Decode(ecParams.N, signature);
    
                    return VerifySignature(rs[0], rs[1]);
                }
                catch (Exception)
                {
                }
    
                return false;
            }
    
            public virtual void Reset()
            {
                if (z != null)
                {
                    digest.Reset();
                    digest.BlockUpdate(z, 0, z.Length);
                }
            }
    
            private byte[] GetZ(byte[] userID)
            {
                AddUserID(digest, userID);
    
                AddFieldElement(digest, ecParams.Curve.A);
                AddFieldElement(digest, ecParams.Curve.B);
                AddFieldElement(digest, ecParams.G.AffineXCoord);
                AddFieldElement(digest, ecParams.G.AffineYCoord);
                AddFieldElement(digest, pubPoint.AffineXCoord);
                AddFieldElement(digest, pubPoint.AffineYCoord);
    
                return DigestUtilities.DoFinal(digest);
            }
            private void AddUserID(IDigest digest, byte[] userID)
            {
                int len = userID.Length * 8;
                digest.Update((byte)(len >> 8));
                digest.Update((byte)len);
                digest.BlockUpdate(userID, 0, userID.Length);
            }
            private void AddFieldElement(IDigest digest, ECFieldElement v)
            {
                byte[] p = v.GetEncoded();
                digest.BlockUpdate(p, 0, p.Length);
            }
    
            /// <summary>
            /// 直接返回 R,S BigInteger数组
            /// </summary>
            /// <returns></returns>
            /// <exception cref="CryptoException"></exception>
            public virtual BigInteger[] GenerateSignatureWay2()
            {
                byte[] eHash = DigestUtilities.DoFinal(digest);
    
                BigInteger n = ecParams.N;
                BigInteger e = CalculateE(n, eHash);
                BigInteger d = ((ECPrivateKeyParameters)ecKey).D;
    
                BigInteger r, s;
    
                ECMultiplier basePointMultiplier = CreateBasePointMultiplier();
    
                // 5.2.1 Draft RFC:  SM2 Public Key Algorithms
                do // generate s
                {
                    BigInteger k;
                    do // generate r
                    {
                        // A3
                        k = kCalculator.NextK();
    
                        // A4
                        ECPoint p = basePointMultiplier.Multiply(ecParams.G, k).Normalize();
    
                        // A5
                        r = e.Add(p.AffineXCoord.ToBigInteger()).Mod(n);
                    }
                    while (r.SignValue == 0 || r.Add(k).Equals(n));
    
                    // A6
                    BigInteger dPlus1ModN = BigIntegers.ModOddInverse(n, d.Add(BigIntegers.One));
    
                    s = k.Subtract(r.Multiply(d)).Mod(n);
                    s = dPlus1ModN.Multiply(s).Mod(n);
                }
                while (s.SignValue == 0);
    
                // A7
                try
                {
                    //return encoding.Encode(ecParams.N, r, s);
                    BigInteger[] biRS = new BigInteger[] { r,s};
                    return biRS;
                }
                catch (Exception ex)
                {
                    throw new CryptoException("unable to encode signature: " + ex.Message, ex);
                }
            }
    
        }
    }

    SM2SignUtil 类:

    using Org.BouncyCastle.Asn1.GM;
    using Org.BouncyCastle.Asn1.X9;
    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Digests;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Crypto.Signers;
    using Org.BouncyCastle.Math;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Utilities.Encoders;
    using System;
    using System.Text;
    
    namespace TheSM2.Utils
    {
        public class SM2SignUtil
        {
            public static string SM2Sign(string data,string userId,string priKey)
            {            
    
                byte[] msgBytes = Encoding.UTF8.GetBytes(data);             
    
                byte[] byUserId = Encoding.UTF8.GetBytes(userId);
    
                ICipherParameters param = getCipherParam(priKey, userId);
    
                Sha256Digest sha256Digest = new Sha256Digest();
    
                //SM2Signer signer = new SM2Signer(sha256Digest); // BC 自带
                SM2Signer1 signer = new SM2Signer1(sha256Digest); // 参照云南农信
    
                //BouncyCastle SM2Signer 自带的Init 方法比 云南农信JAVA 代码多了3行,需要新写一个类 继承ISigner,去掉那3行代码,否则云南农信验证签名不通过。
                signer.Init(true, param);
                signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
    
                //GenerateSignature 返回值用StandardDsaEncoding做了包装,包含 N、r、s 3个值。需要配合decodeSignResNRS2RS 反向解析出r、s,再拼接。
                var generateSignature = signer.GenerateSignature();
    
                var sign = decodeSignResNRS2RS(generateSignature);
                 return sign;
            }
    
            /// <summary>
            /// 只是对signer.GenerateSignature()方法返回值做了改动,直接返回BigInteger 数组。
            /// </summary>
            /// <param name="data"></param>
            /// <param name="userId"></param>
            /// <param name="priKey"></param>
            /// <returns></returns>
            public static string SM2SignWay2(string data, string userId, string priKey)
            {
    
                byte[] msgBytes = Encoding.UTF8.GetBytes(data);
    
                byte[] byUserId = Encoding.UTF8.GetBytes(userId);
    
                ICipherParameters param = getCipherParam(priKey, userId);
    
                Sha256Digest sha256Digest = new Sha256Digest();
    
                //SM2Signer signer = new SM2Signer(sha256Digest); // BC 自带
                SM2Signer1 signer = new SM2Signer1(sha256Digest); // 参照云南农信
    
                // BouncyCastle SM2Signer 自带的Init 方法比 云南农信JAVA 代码多了3行,需要新写一个类 继承ISigner,去掉那3行代码,否则云南农信验证签名不通过。
                signer.Init(true, param);
                signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
    
                var bigIntegers = signer.GenerateSignatureWay2();
    
                byte[] rBytes = modifyRSFixedBytes(bigIntegers[0].ToByteArray());
                byte[] sBytes = modifyRSFixedBytes(bigIntegers[1].ToByteArray());
    
                byte[] signBytes = concatenate(rBytes, sBytes);
                String sign = Hex.ToHexString(signBytes).ToUpper();
                return sign;
            }
    
            public static ICipherParameters getCipherParam(String privateKey, String userId)
            {
                // 反序列化私钥
                ParametersWithRandom parameters = getParameterRandom(privateKey);
                if (userId != null)
                {
                    ICipherParameters param = new ParametersWithID(parameters, Encoding.UTF8.GetBytes(userId));
                    return param;
                }
                return parameters;
            }
    
            private static ParametersWithRandom getParameterRandom(String privateKey)
            {
                BigInteger privateKeyD = new BigInteger(privateKey, 16);
                X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
                // //构造domain参数
                ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.Curve, sm2ECParameters.G,
                        sm2ECParameters.N);
                ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
                ParametersWithRandom parameters = new ParametersWithRandom(privateKeyParameters,
                        SecureRandom.GetInstance("SHA1PRNG"));
                ICipherParameters param = parameters;
                return parameters;
            }
    
            public static String decodeSignResNRS2RS(byte[] generateSignature)
    
    
            {
                X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
                StandardDsaEncoding dsaEncoding = StandardDsaEncoding.Instance;
                BigInteger[] bigIntegers = dsaEncoding.Decode(sm2ECParameters.N, generateSignature);
                // BigInteger[] bigIntegers = sm2Signer.generateSignature(message);
    
                byte[] rBytes = modifyRSFixedBytes(bigIntegers[0].ToByteArray());
                byte[] sBytes = modifyRSFixedBytes(bigIntegers[1].ToByteArray());
    
                byte[] signBytes = concatenate(rBytes, sBytes);
                String sign = Hex.ToHexString(signBytes).ToUpper();
                return sign;
            }
    
            private static byte[] modifyRSFixedBytes(byte[] rs)
            {
                int length = rs.Length;
                int fixedLength = 32;
                byte[] result = new byte[fixedLength];
                if (length < 32)
                {
                    Array.Copy(rs, 0, result, fixedLength - length, length);
                }
                else
                {
                    Array.Copy(rs, length - fixedLength, result, 0, fixedLength);
                }
                return result;
            }
    
            public static byte[] concatenate(byte[] var0, byte[] var1)
            {
                byte[] var2 = new byte[var0.Length + var1.Length];
                Array.Copy(var0, 0, var2, 0, var0.Length);
                Array.Copy(var1, 0, var2, var0.Length, var1.Length);
                return var2;
            }
    
        }
    }

    使用:

    String data = "123";
                    string userId = "1234567812345678";
                    String priKey = "BDD9D484017EB3CB03D48579680EEF038BD7F4419980EE08AEACEA2A8A039062";
    
                    textBox1.Text = SM2SignUtil.SM2SignWay2(data, userId, priKey);

    THE END

  • 相关阅读:
    换个角度思考问题
    云南印象
    子网掩码划分实例
    子网掩码划分工具下载
    实景地图
    AutoCAD图像输出(输出图像)技巧
    两种消费观念
    子网掩码划分计算方法及实例
    C/C++从入门到高手所有必备PDF书籍收藏
    WINCE6.0添加特定的软件键盘
  • 原文地址:https://www.cnblogs.com/runliuv/p/16486898.html
Copyright © 2020-2023  润新知