前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >国密算法sm2-.Net实现

国密算法sm2-.Net实现

原创
作者头像
西门呀在吹雪
修改2021-08-18 14:27:15
1.8K0
修改2021-08-18 14:27:15
举报
文章被收录于专栏:架构之巅

国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。

SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。

SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。

SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。

SM4 无线局域网标准(WAPI)的分组数据算法。对称加密,密钥长度和分组长度均为128位。

由于SM1、SM4加解密的分组大小为128bit,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。

重点:国密算法只是公布了算法,并没有给出具体语言的实现,因此需要各个语言根据算法去自己实现。

依赖类库:

talk is cheap,show me the code

代码语言:javascript
复制
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
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.RegularExpressions;

public class SM2Service
{
    public SM2Service(string pubkey, string privkey, Mode mode)
    {
        if (pubkey != null) this.pubkey = Decode(pubkey);
        if (privkey != null) this.privkey = Decode(privkey);
        this.mode = mode;
    }
    public SM2Service(byte[] pubkey, byte[] privkey, Mode mode)
    {
        this.pubkey = pubkey;
        this.privkey = privkey;
        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; //sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。
        int c3Len = 32; //new SM3Digest().getDigestSize();
        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; //new SM3Digest().getDigestSize();
        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
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档