基于BouncyCastle的国密SM2、SM3、SM4算法C#语言实现

【为什么写这篇文章】

  1. 关于国密SM2、SM3、SM4算法的文章,网上非常多,但是时间先后不一,知识点也非常散落,不便于理解和掌握。
  2. 作为普通的编程爱好者,自己的水平也就一般般,更多关心的是怎么使用;对于算法原理,知道个大概即可

【需要引用的Nuget包】

本次实现以WPF类库为基础,需要额外引用的Nuget包,就一个:BouncyCastle.Cryptography (2.4.0)

【3个国密算法的C#语言实现】

一、SM2

public class MySM2v2  
{
    private static readonly X9ECParameters x9 = GMNamedCurves.GetByName("SM2P256V1");
    private const int RS_LEN = 32; 

    #region ASN.1 DER编码格式转换 
    public static byte[] RsAsn1ToPlainByteArray(byte[] signDer)
    {
        Asn1Sequence seq = Asn1Sequence.GetInstance(signDer);
        byte[] r = BigIntToFixexLengthBytes(DerInteger.GetInstance(seq[0]).Value);
        byte[] s = BigIntToFixexLengthBytes(DerInteger.GetInstance(seq[1]).Value);
        byte[] result = new byte[RS_LEN * 2];
        Buffer.BlockCopy(r, 0, result, 0, r.Length);
        Buffer.BlockCopy(s, 0, result, RS_LEN, s.Length);
        return result;
    }

    public static byte[] RsAsn1FromPlainByteArray(byte[] sign)
    {
        if (sign.Length != RS_LEN * 2) throw new ArgumentException("err rs. ");
        BigInteger r = new(1, Arrays.CopyOfRange(sign, 0, RS_LEN));
        BigInteger s = new(1, Arrays.CopyOfRange(sign, RS_LEN, RS_LEN * 2));
        Asn1EncodableVector v = [new DerInteger(r), new DerInteger(s)];
        try
        {
            return new DerSequence(v).GetEncoded("DER");
        }
        catch (IOException e)
        {
            return [];
        }
    }
    private static byte[] BigIntToFixexLengthBytes(BigInteger rOrS)
    {
        byte[] rs = rOrS.ToByteArray();
        if (rs.Length == RS_LEN) return rs;
        else if (rs.Length == RS_LEN + 1 && rs[0] == 0) return Arrays.CopyOfRange(rs, 1, RS_LEN + 1);
        else if (rs.Length < RS_LEN)
        {
            byte[] result = new byte[RS_LEN];
            Arrays.Fill(result, (byte)0);
            Buffer.BlockCopy(rs, 0, result, RS_LEN - rs.Length, rs.Length);
            return result;
        }
        else
        {
            throw new ArgumentException("err rs: " + Hex.ToHexString(rs));
        }
    }
    #endregion

    #region 密钥生成
    public static (byte[] PublicKey, byte[] PrivateKey) SM2KeyGen()
    {
        ECKeyPairGenerator eCKeyPairGenerator = new();
        eCKeyPairGenerator.Init(new ECKeyGenerationParameters(new ECDomainParameters(x9), new SecureRandom()));
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = eCKeyPairGenerator.GenerateKeyPair();
        var publicKey = ((ECPublicKeyParameters)asymmetricCipherKeyPair.Public).Q.GetEncoded(compressed: false);
        var privateKey = ((ECPrivateKeyParameters)asymmetricCipherKeyPair.Private).D.ToByteArray();
        return (publicKey, privateKey);
    }
    #endregion

    #region 签名和验证
    public static byte[] SM2Sign(byte[] data, byte[] privateKey, byte[]? userId = null)
    {
        SM2Signer sM2Signer = new(new SM3Digest());
        ICipherParameters parameters = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(1, privateKey), new ECDomainParameters(x9)));
        if (userId != null)
        {
            parameters = new ParametersWithID(parameters, userId);
        }

        sM2Signer.Init(forSigning: true, parameters);
        sM2Signer.BlockUpdate(data, 0, data.Length);

        var dataSignedDer = sM2Signer.GenerateSignature();
        var dataSigned = RsAsn1ToPlainByteArray(dataSignedDer);
        return dataSigned;
    }

    public static bool SM2VerifySign(byte[] data, byte[] publicKey, byte[] dataSigned, byte[]? userId = null)
    {
        var dataSignedDer = RsAsn1FromPlainByteArray(dataSigned);

        SM2Signer sM2Signer = new(new SM3Digest());
        ICipherParameters parameters = new ECPublicKeyParameters(x9.Curve.DecodePoint(publicKey), new ECDomainParameters(x9));
        if (userId != null)
        {
            parameters = new ParametersWithID(parameters, userId);
        }

        sM2Signer.Init(forSigning: false, parameters);
        sM2Signer.BlockUpdate(data, 0, data.Length);
        return sM2Signer.VerifySignature(dataSignedDer);
    }

    public static byte[] SM2SignByDataHash(byte[] data, byte[] privateKey, byte[]? userId = null)
    {
        var dataHash = MySM3.SM3HashData(data);

        SM2Signer sM2Signer = new(new SM3Digest());
        ICipherParameters parameters = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(1, privateKey), new ECDomainParameters(x9)));
        if (userId != null)
        {
            parameters = new ParametersWithID(parameters, userId);
        }

        sM2Signer.Init(forSigning: true, parameters);
        sM2Signer.BlockUpdate(dataHash, 0, dataHash.Length);

        var dataSignedDer = sM2Signer.GenerateSignature();
        var dataSigned = RsAsn1ToPlainByteArray(dataSignedDer);
        return dataSigned;
    }

    public static bool SM2VerifySignByDataHash(byte[] data, byte[] publicKey, byte[] dataSigned, byte[]? userId = null)
    {
        var dataSignedDer = RsAsn1FromPlainByteArray(dataSigned);

        var dataHash = MySM3.SM3HashData(data);

        SM2Signer sM2Signer = new(new SM3Digest());
        ICipherParameters parameters = new ECPublicKeyParameters(x9.Curve.DecodePoint(publicKey), new ECDomainParameters(x9));
        if (userId != null)
        {
            parameters = new ParametersWithID(parameters, userId);
        }

        sM2Signer.Init(forSigning: false, parameters);
        sM2Signer.BlockUpdate(dataHash, 0, dataHash.Length);
        return sM2Signer.VerifySignature(dataSignedDer);
    }
    #endregion

    #region 加密和解密
    public static byte[] SM2Encrypt(byte[] data, byte[] publicKey, byte[]? userId = null, SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2)
    {
        SM2Engine sM2Engine = new(new SM3Digest(), SM2Engine.Mode.C1C3C2);
        ICipherParameters cipherParameters = new ParametersWithRandom(new ECPublicKeyParameters(x9.Curve.DecodePoint(publicKey), new ECDomainParameters(x9)));
        if (userId != null)
        {
            cipherParameters = new ParametersWithID(cipherParameters, userId);
        }

        sM2Engine.Init(forEncryption: true, cipherParameters);
        data = sM2Engine.ProcessBlock(data, 0, data.Length);
        if (mode == SM2Engine.Mode.C1C2C3)
        {
            data = C132ToC123(data);
        }

        return data;
    }

    public static byte[] SM2Decrypt(byte[] dataEncrypted, byte[] privateKey, byte[]? userId = null, SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2)
    {
        if (mode == SM2Engine.Mode.C1C2C3)
        {
            dataEncrypted = C123ToC132(dataEncrypted);
        }

        SM2Engine sM2Engine = new(new SM3Digest(), SM2Engine.Mode.C1C3C2);
        ICipherParameters cipherParameters = new ECPrivateKeyParameters(new BigInteger(1, privateKey), new ECDomainParameters(x9));
        if (userId != null)
        {
            cipherParameters = new ParametersWithID(cipherParameters, userId);
        }

        sM2Engine.Init(forEncryption: false, cipherParameters);
        return sM2Engine.ProcessBlock(dataEncrypted, 0, dataEncrypted.Length);
    }

    private static byte[] C123ToC132(byte[] c1c2c3)
    {
        int num = (x9.Curve.FieldSize + 7 >> 3 << 1) + 1;
        byte[] array = new byte[c1c2c3.Length];
        Array.Copy(c1c2c3, 0, array, 0, num);
        Array.Copy(c1c2c3, c1c2c3.Length - 32, array, num, 32);
        Array.Copy(c1c2c3, num, array, num + 32, c1c2c3.Length - num - 32);
        return array;
    }
    private static byte[] C132ToC123(byte[] c1c3c2)
    {
        int num = (x9.Curve.FieldSize + 7 >> 3 << 1) + 1;
        byte[] array = new byte[c1c3c2.Length];
        Array.Copy(c1c3c2, 0, array, 0, num);
        Array.Copy(c1c3c2, num + 32, array, num, c1c3c2.Length - num - 32);
        Array.Copy(c1c3c2, num, array, c1c3c2.Length - 32, 32);
        return array;
    }
    #endregion
}

二、SM3

 public class MySM3  
{
    public static byte[] SM3KeyGen(int keySizeBit = 512)
    {
        int keySize = keySizeBit / 8;
        var keyRandom = RandomNumberGenerator.GetBytes(keySize);
        return keyRandom;
    }

    public static byte[] SM3HashData(byte[] data)
    {
        SM3Digest sm3 = new();
        sm3.BlockUpdate(data, 0, data.Length);
        byte[] md = new byte[sm3.GetDigestSize()];
        sm3.DoFinal(md, 0);
        return md;
    }

    public static byte[] SM3HashData(byte[] data, byte[] key)
    {
        SM3Digest sm3 = new();
        HMac mac = new(sm3);
        KeyParameter keyParameter = new(key);
        mac.Init(keyParameter);
        mac.BlockUpdate(data, 0, data.Length);
        byte[] result = new byte[mac.GetMacSize()];
        mac.DoFinal(result, 0);
        return result;
    }      
}

三、SM4

 public class MySM4  
{
    public static (byte[] Key, byte[] IV) SM4KeyGen(string password, int keySizeBit = 128, byte[]? salts = null)
    {
        byte[] salt = salts ?? [0x31, 0x33, 0x35, 0x37, 0x32, 0x34, 0x36, 0x38];
        if (salt.Length % 8 == 0 && salt.Length >= 8)
        { }
        else
        { throw new ArgumentException("加盐数组的长度必须为8的倍数", nameof(salts)); }

        int myIterations = 1000;
        Rfc2898DeriveBytes keyBytesGen = new(password, salt, myIterations, HashAlgorithmName.SHA256);

        int keyLen = keySizeBit / 8;     
        byte[] key = new byte[keyLen];
        byte[] iv = new byte[16];
        key = keyBytesGen.GetBytes(key.Length);
        iv = keyBytesGen.GetBytes(iv.Length);
        return (Key: key, IV: iv);
    }

    public static byte[] SM4CBCEncrypt(byte[] data, byte[] key, byte[] iv)
    {
        var engine = new SM4Engine();
        var blockCipher = new CbcBlockCipher(engine);
        var cipher = new PaddedBufferedBlockCipher(blockCipher);
        cipher.Init(true, new ParametersWithIV(new KeyParameter(key), iv));
        return cipher.DoFinal(data);
    }

    public static byte[] SM4CBCDecrypt(byte[] dataEncrypted, byte[] key, byte[] iv)
    {
        var engine = new SM4Engine();
        var blockCipher = new CbcBlockCipher(engine);
        var cipher = new PaddedBufferedBlockCipher(blockCipher);
        cipher.Init(false, new ParametersWithIV(new KeyParameter(key), iv));
        return cipher.DoFinal(dataEncrypted);
    }
}

【补充说明】

SM2算法安全性相当于RSA-3072

SM3算法安全性相当于SHA-256

SM4算法的安全性相当于AES-128

在日常使用中,推荐使用 SM2 + AES-256 这样的组合形式代替 RSA-2048 + AES-256 的组合形式。

【参考文献】

  1. 《C# SM2算法 加密,解密,签名,验签》https://www.cnblogs.com/Cxiaoao/p/15170345.html
  2.  nuget包:EasilyNET.Security (3.24) NuGet Gallery | EasilyNET.Security 3.24.613.115
  3. 《C#.NET 国密SM3withSM2签名与验签 和JAVA互通》https://www.cnblogs.com/runliuv/p/15079404.html
  4. 《国密算法—SM2介绍及基于BC的实现》国密算法—SM2介绍及基于BC的实现_sm2不同版本的bc包1.57 和1.64-CSDN博客
  5. 《关于SM2算法 ASN.1编码 - 签名长度》关于SM2算法 ASN.1编码 - 签名长度_sm2签名长度-CSDN博客
  6. 《C#.NET 国密SM3withSM2签名与验签 和JAVA互通》https://www.cnblogs.com/runliuv/p/15079404.html
  7. 《关于SM2算法 ASN.1编码 - 签名长度》关于SM2算法 ASN.1编码 - 签名长度_sm2签名长度-CSDN博客
  8. 《C# sm3加密实现【转】Sm3Crypto ,动态库BouncyCastle.Crypto》C# sm3加密实现【转】Sm3Crypto ,动态库BouncyCastle.Crypto_c# sm3 签名-CSDN博客
  9. 《SM3加密算法详解(2021-12-8)》SM3加密算法详解(2021-12-8)-CSDN博客
  10. 《C# 国密加密》https://www.cnblogs.com/weidaorisun/p/16190122.html
  11. 《.NET C# 实现国密算法加解密》.NET C# 实现国密算法加解密-CSDN博客
  12. 《The GmSSL Project》国密SM4/SMS4分组密码
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java 中实现国密算法 SM2SM3SM4 可以使用 Bouncy Castle 密码库。Bouncy Castle 是一个流行的密码库,支持多种密码算法,包括国密算法。 以下是一个简单的示例,说明如何在 Java 中使用 Bouncy Castle 实现 SM2SM3SM4: 1. 添加 Bouncy Castle 依赖 在 Maven 项目中,可以在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15to18</artifactId> <version>1.68</version> </dependency> ``` 2. SM2 加密和解密示例 ```java import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.bouncycastle.crypto.util.PublicKeyFactory; import java.security.SecureRandom; public class SM2Example { public static void main(String[] args) throws Exception { // 生成密钥对 ECKeyPairGenerator ecKeyPairGenerator = new ECKeyPairGenerator(); SecureRandom secureRandom = new SecureRandom(); X9ECParameters ecParams = SECNamedCurves.getByName("sm2p256v1"); ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParams.getCurve(), ecParams.getG(), ecParams.getN(), ecParams.getH(), ecParams.getSeed()); ECKeyGenerationParameters keyParams = new ECKeyGenerationParameters(ecDomainParameters, secureRandom); ecKeyPairGenerator.init(keyParams); AsymmetricCipherKeyPair keyPair = ecKeyPairGenerator.generateKeyPair(); // 加密 SM2Engine sm2Engine = new SM2Engine(); CipherParameters publicKeyParameters = PublicKeyFactory.createKey(keyPair.getPublic().getEncoded()); CipherParameters privateKeyParameters = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded()); sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, secureRandom)); byte[] plainText = "hello world".getBytes(); byte[] cipherText = sm2Engine.processBlock(plainText, 0, plainText.length); // 解密 sm2Engine.init(false, privateKeyParameters); byte[] decryptedText = sm2Engine.processBlock(cipherText, 0, cipherText.length); System.out.println(new String(decryptedText)); } } ``` 3. SM3 摘要示例 ```java import org.bouncycastle.crypto.digests.SM3Digest; public class SM3Example { public static void main(String[] args) { // 计算摘要 byte[] input = "hello world".getBytes(); SM3Digest digest = new SM3Digest(); digest.update(input, 0, input.length); byte[] result = new byte[digest.getDigestSize()]; digest.doFinal(result, 0); // 输出摘要 System.out.println(bytesToHex(result)); } private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int i = 0; i < bytes.length; i++) { int v = bytes[i] & 0xFF; hexChars[i * 2] = HEX_ARRAY[v >>> 4]; hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F]; } return new String(hexChars); } } ``` 4. SM4 加密和解密示例 ```java import org.bouncycastle.crypto.engines.SM4Engine; import org.bouncycastle.crypto.params.KeyParameter; import java.security.SecureRandom; public class SM4Example { public static void main(String[] args) { // 生成密钥 SecureRandom secureRandom = new SecureRandom(); byte[] key = new byte[16]; secureRandom.nextBytes(key); KeyParameter keyParameter = new KeyParameter(key); // 加密 SM4Engine sm4Engine = new SM4Engine(); sm4Engine.init(true, keyParameter); byte[] plainText = "hello world".getBytes(); byte[] cipherText = new byte[sm4Engine.getOutputSize(plainText.length)]; int length = sm4Engine.processBytes(plainText, 0, plainText.length, cipherText, 0); sm4Engine.doFinal(cipherText, length); // 解密 sm4Engine.init(false, keyParameter); byte[] decryptedText = new byte[sm4Engine.getOutputSize(cipherText.length)]; length = sm4Engine.processBytes(cipherText, 0, cipherText.length, decryptedText, 0); sm4Engine.doFinal(decryptedText, length); System.out.println(new String(decryptedText)); } } ``` 以上是实现 SM2SM3SM4 的简单示例,具体实现可以根据具体需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值