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