• C# SM2算法 加密,解密,签名,验签


    最近时间在整SM2算法,在网上看到不少代码,基本都是使用BouncyCastle库,现在这个版本算比较好的拿来分享给大家。

    首先引入包 Portable.BouncyCastle

     完整代码见Gitee:https://gitee.com/xuzhongye/CryptoHelper/blob/master/src/CryptoHelper/SM2CryptoUtil.cs

    public class SM2CryptoUtil
    {
        public SM2CryptoUtil(byte[] pubkey, byte[] privkey, Mode mode)
        {
            this.pubkey = pubkey;
            this.privkey = privkey;
            this.mode = mode;
        }
        public SM2CryptoUtil(string pubkey, string privkey, Mode mode = Mode.C1C2C3, bool isPkcs8 = false) 
        {
            if (!isPkcs8)
            {
                if (pubkey != null) this.pubkey = Decode(pubkey);
                if (privkey != null) this.privkey = Decode(privkey);
            }
            else
            {
                if (pubkey != null) this.pubkey = ((ECPublicKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(pubkey))).Q.GetEncoded();
                if (privkey != null) this.privkey = ((ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privkey))).D.ToByteArray();
            }
            this.mode = mode;
        }
        byte[] pubkey;
        byte[] privkey;
        Mode mode;
        ICipherParameters _privateKeyParameters;
        ICipherParameters PrivateKeyParameters
        {
            get
            {
                var r = _privateKeyParameters;
                if (r == null) r = _privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, privkey), new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));
                return r;
            }
        }
        ICipherParameters _publicKeyParameters;
        ICipherParameters PublicKeyParameters
        {
            get
            {
                var r = _publicKeyParameters;
                if (r == null)
                {
                    var x9ec = GMNamedCurves.GetByName("SM2P256V1");
                    r = _publicKeyParameters = new ECPublicKeyParameters(x9ec.Curve.DecodePoint(pubkey), new ECDomainParameters(x9ec));
                }
                return r;
            }
        }
    
        public static void GenerateKeyHex(out string pubkey, out string privkey)
        {
            GenerateKey(out var a, out var b);
            pubkey = Hex.ToHexString(a);
            privkey = Hex.ToHexString(b);
        }
        public static void GenerateKey(out byte[] pubkey, out byte[] privkey)
        {
            var g = new ECKeyPairGenerator();
            g.Init(new ECKeyGenerationParameters(new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")), new SecureRandom()));
            var k = g.GenerateKeyPair();
            pubkey = ((ECPublicKeyParameters)k.Public).Q.GetEncoded(false);
            privkey = ((ECPrivateKeyParameters)k.Private).D.ToByteArray();
        }
    
        public byte[] Decrypt(byte[] data)
        {
            if (mode == Mode.C1C3C2) data = C132ToC123(data);
            var sm2 = new SM2Engine(new SM3Digest());
            sm2.Init(false, this.PrivateKeyParameters);
            return sm2.ProcessBlock(data, 0, data.Length);
        }
        public byte[] Encrypt(byte[] data)
        {
            var sm2 = new SM2Engine(new SM3Digest());
            sm2.Init(true, new ParametersWithRandom(PublicKeyParameters));
            data = sm2.ProcessBlock(data, 0, data.Length);
            if (mode == Mode.C1C3C2) data = C123ToC132(data);
            return data;
        }
        public byte[] Sign(byte[] msg, byte[] id = null)
        {
            var sm2 = new SM2Signer(new SM3Digest());
            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(new ParametersWithRandom(PrivateKeyParameters), id);
            else cp = new ParametersWithRandom(PrivateKeyParameters);
            sm2.Init(true, cp);
            sm2.BlockUpdate(msg, 0, msg.Length);
            return sm2.GenerateSignature();
        }
        public bool VerifySign(byte[] msg, byte[] signature, byte[] id = null)
        {
            var sm2 = new SM2Signer(new SM3Digest());
            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(PublicKeyParameters, id);
            else cp = PublicKeyParameters;
            sm2.Init(false, cp);
            sm2.BlockUpdate(msg, 0, msg.Length);
            return sm2.VerifySignature(signature);
        }
        static byte[] C123ToC132(byte[] c1c2c3)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1; 
            int c3Len = 32; 
            byte[] result = new byte[c1c2c3.Length];
            Array.Copy(c1c2c3, 0, result, 0, c1Len); //c1
            Array.Copy(c1c2c3, c1c2c3.Length - c3Len, result, c1Len, c3Len); //c3
            Array.Copy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.Length - c1Len - c3Len); //c2
            return result;
        }
        static byte[] C132ToC123(byte[] c1c3c2)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
            int c3Len = 32; 
            byte[] result = new byte[c1c3c2.Length];
            Array.Copy(c1c3c2, 0, result, 0, c1Len); //c1: 0->65
            Array.Copy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.Length - c1Len - c3Len); //c2
            Array.Copy(c1c3c2, c1Len, result, c1c3c2.Length - c3Len, c3Len); //c3
            return result;
        }
        static byte[] Decode(string key)
        {
            return Regex.IsMatch(key, "^[0-9a-f]+$", RegexOptions.IgnoreCase) ? Hex.Decode(key) : Convert.FromBase64String(key);
        }
        public enum Mode
        {
            C1C2C3, C1C3C2
        }
    }

    踩坑记录:

    1. 网上有不少教程也是使用 Portable.BouncyCastle库类,但SM2算法写了很多代码(我也没看懂,估计是自己计算椭圆曲线),但加密起来通过在线工具 有时可以解密有时又不行。估计也是使用旧版本。

    2. 由于客户提供的密钥是pkcs8,我使用的在线工具是 https://aks.jd.com/tools/sec/。其他的在线工具不一定很解密。估计也是使用旧版本。

    3. 客户签名后在我这里验签一直不通过,后来客户把 org.bouncycastle的jar包从1.58升级到1.62才解决这个问题,他们自己也说1.58的jar包签名的用1.62的jar包是验签不通过的。建议大家使用时都使用最新版本。

    4. 签名和验签时 id 如果不传会使用默认值 1234567812345678,下面代码里面前两种写法是等效的

    signTool.VerifySign(dataBytes, signBytes); //true
    signTool.VerifySign(dataBytes, signBytes, Encoding.ASCII.GetBytes("1234567812345678")); // true
    signTool.VerifySign(dataBytes, signBytes, new byte[] { }); // false

     5. SM2签名就是 SM3WithSM2签名。

  • 相关阅读:
    对模拟器虚假设备识别能力提升15%!每日清理大师App集成系统完整性检测
    教你实现华为快应用深色主题适配
    如何实现一个malloc
    如何提高团队管理能力?
    大型网站架构演变和知识体系
    call_user_func 和 call_user_func_array用法
    源码研究:php变量
    http协议简介
    用c语言实现http请求
    用socket写一个简单的客户端和服务端程序
  • 原文地址:https://www.cnblogs.com/Cxiaoao/p/15170345.html
Copyright © 2020-2023  润新知