C#实现SM2国密加密

 

本文主要讲解“国密加密算法”SM系列的C#实现方法,不涉及具体的算法剖析,在网络上找到的java实现方法比较少,切在跨语言加密解密上会存在一些问题,所以整理此文志之。JAVA实现参考http://blog.csdn.net/ererfei/article/details/50998162

1.SM2 & SM3
由于SM2算法中需要使用SM3摘要算法,所以把他们放在一起

项目目录结构如下:

首先要下载一个dll包——BouncyCastle.Crypto.dll,并将此dll引用到项目中。实现代码如下(每个工具类都有Main可以运行测试):

a.      SM2主类

【SM2.cs】

using System;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Text;
 
namespace Com.Mlq.SM
{
    
    public class SM2
    {
        public static SM2 Instance
        {
            get
            {
                return new SM2();
            }
            
        }
        public static SM2 InstanceTest
        {
            get
            {
                return new SM2();
            }
            
        }
 
        public static readonly string[] sm2_param = {
            "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",// p,0
            "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",// a,1
            "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",// b,2
            "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",// n,3
            "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",// gx,4
            "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" // gy,5
        };
 
        public string[] ecc_param = sm2_param;
        
        public readonly BigInteger ecc_p;
        public readonly BigInteger ecc_a;
        public readonly BigInteger ecc_b;
        public readonly BigInteger ecc_n;
        public readonly BigInteger ecc_gx;
        public readonly BigInteger ecc_gy;
        
        public readonly ECCurve ecc_curve;
        public readonly ECPoint ecc_point_g;
        
        public readonly ECDomainParameters ecc_bc_spec;
        
        public readonly ECKeyPairGenerator ecc_key_pair_generator;
 
        private SM2()
        {
            ecc_param = sm2_param;
 
            ECFieldElement ecc_gx_fieldelement;
            ECFieldElement ecc_gy_fieldelement;
            
            ecc_p = new BigInteger(ecc_param[0], 16);
            ecc_a = new BigInteger(ecc_param[1], 16);
            ecc_b = new BigInteger(ecc_param[2], 16);
            ecc_n = new BigInteger(ecc_param[3], 16);
            ecc_gx = new BigInteger(ecc_param[4], 16);
            ecc_gy = new BigInteger(ecc_param[5], 16);
 
 
            ecc_gx_fieldelement = new FpFieldElement(ecc_p, ecc_gx);
            ecc_gy_fieldelement = new FpFieldElement(ecc_p, ecc_gy);
 
            ecc_curve = new FpCurve(ecc_p, ecc_a, ecc_b);
            ecc_point_g = new FpPoint(ecc_curve, ecc_gx_fieldelement, ecc_gy_fieldelement);
            
            ecc_bc_spec = new ECDomainParameters(ecc_curve, ecc_point_g, ecc_n);
            
            ECKeyGenerationParameters ecc_ecgenparam;
            ecc_ecgenparam = new ECKeyGenerationParameters(ecc_bc_spec, new SecureRandom());
            
            ecc_key_pair_generator = new ECKeyPairGenerator();
            ecc_key_pair_generator.Init(ecc_ecgenparam);
        }
 
        public virtual byte[] Sm2GetZ(byte[] userId, ECPoint userKey)
        {
            SM3Digest sm3 = new SM3Digest();
            byte[] p;
            // userId length
            int len = userId.Length * 8;
            sm3.Update((byte) (len >> 8 & 0x00ff));
            sm3.Update((byte) (len & 0x00ff));
            
            // userId
            sm3.BlockUpdate(userId, 0, userId.Length);
            
            // a,b
            p = ecc_a.ToByteArray();
            sm3.BlockUpdate(p, 0, p.Length);
            p = ecc_b.ToByteArray();
            sm3.BlockUpdate(p, 0, p.Length);
            // gx,gy
            p = ecc_gx.ToByteArray();
            sm3.BlockUpdate(p, 0, p.Length);
            p = ecc_gy.ToByteArray();
            sm3.BlockUpdate(p, 0, p.Length);
            
            // x,y
            p = userKey.X.ToBigInteger().ToByteArray();
            sm3.BlockUpdate(p, 0, p.Length);
            p = userKey.Y.ToBigInteger().ToByteArray();
            sm3.BlockUpdate(p, 0, p.Length);
            
            // Z
            byte[] md = new byte[sm3.GetDigestSize()];
            sm3.DoFinal(md, 0);
            
            return md;
        }
        
    }
}

b.      SM2工具类

【SM2Utils.cs】

using Com.Mlq.SM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace Com.Mlq.SM
{
    class SM2Utils
    {
        public static void GenerateKeyPair()
        {
            SM2 sm2 = SM2.Instance;
            AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.GenerateKeyPair();  
            ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.Private;  
            ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.Public;  
            BigInteger privateKey = ecpriv.D;  
            ECPoint publicKey = ecpub.Q;
 
            System.Console.Out.WriteLine("公钥: " + Encoding.Default.GetString(Hex.Encode(publicKey.GetEncoded())).ToUpper());
            System.Console.Out.WriteLine("私钥: " + Encoding.Default.GetString(Hex.Encode(privateKey.ToByteArray())).ToUpper());  
        }
 
        public static String Encrypt(byte[] publicKey,byte[] data)
        {
            if (null == publicKey || publicKey.Length == 0)
            {
                return null;
            }
            if (data == null || data.Length == 0)
            {
                return null;
            }
 
            byte[] source = new byte[data.Length];
            Array.Copy(data, 0, source, 0, data.Length);
 
            Cipher cipher = new Cipher();
            SM2 sm2 = SM2.Instance;
 
            ECPoint userKey = sm2.ecc_curve.DecodePoint(publicKey);
 
            ECPoint c1 = cipher.Init_enc(sm2, userKey);
            cipher.Encrypt(source);
 
            byte[] c3 = new byte[32];
            cipher.Dofinal(c3);
 
            String sc1 = Encoding.Default.GetString(Hex.Encode(c1.GetEncoded()));
            String sc2 = Encoding.Default.GetString(Hex.Encode(source));
            String sc3 = Encoding.Default.GetString(Hex.Encode(c3));
 
            return (sc1 + sc2 + sc3).ToUpper();
        }
 
        public static byte[] Decrypt(byte[] privateKey, byte[] encryptedData)
        {
            if (null == privateKey || privateKey.Length == 0)
            {
                return null;
            }
            if (encryptedData == null || encryptedData.Length == 0)
            {
                return null;
            }
 
            String data = Encoding.Default.GetString(Hex.Encode(encryptedData));
 
            byte[] c1Bytes = Hex.Decode(Encoding.Default.GetBytes(data.Substring(0 , 130)));
            int c2Len = encryptedData.Length - 97;
            byte[] c2 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130 , 2 * c2Len)));
            byte[] c3 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130 + 2 * c2Len , 64)));
 
            SM2 sm2 = SM2.Instance;
            BigInteger userD = new BigInteger(1, privateKey);
 
            ECPoint c1 = sm2.ecc_curve.DecodePoint(c1Bytes);
            Cipher cipher = new Cipher();
            cipher.Init_dec(userD, c1);
            cipher.Decrypt(c2);
            cipher.Dofinal(c3);
 
            return c2;
        }
 
        //[STAThread]
        //public static void Main()
        //{
        //    GenerateKeyPair();
 
        //    String plainText = "ererfeiisgod";
        //    byte[] sourceData = Encoding.Default.GetBytes(plainText);
 
        //    //下面的秘钥可以使用generateKeyPair()生成的秘钥内容  
        //    // 国密规范正式私钥  
        //    String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
        //    // 国密规范正式公钥  
        //    String pubk
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值