本文主要讲解“国密加密算法”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