C#中封装SM2工具类
前言
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。
一、SM2是什么?
SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
旧标准的加密排序C1C2C3 新标准 C1C3C2,C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
二、C#具体代码
1.引入库
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;
2.具体工具
代码如下(示例):
/// <summary>
/// SM2工具类
/// </summary>
public class SM2Util
{
/// <summary>
/// 加密
/// </summary>
/// <param name="publicKey_string"></param>
/// <param name="data_string"></param>
/// <returns></returns>
public static string Encrypt(string publicKey_string, string data_string)
{
var publicKey = Hex.Decode(publicKey_string);
var data = Encoding.UTF8.GetBytes(data_string);
return Encrypt(publicKey, data);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="privateKey_string"></param>
/// <param name="encryptedData_string"></param>
/// <returns></returns>
public static string Decrypt(string privateKey_string, string encryptedData_string)
{
var privateKey = Hex.Decode(privateKey_string);
var encryptedData = Hex.Decode(encryptedData_string);
var de_str = SM2Util.Decrypt(privateKey, encryptedData);
string plainText = Encoding.UTF8.GetString(de_str);
return plainText;
}
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;
Console.Out.WriteLine("公钥: " + Encoding.ASCII.GetString(Hex.Encode(publicKey.GetEncoded())).ToUpper());
Console.Out.WriteLine("私钥: " + Encoding.ASCII.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);
var 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.ASCII.GetString(Hex.Encode(c1.GetEncoded()));
string sc2 = Encoding.ASCII.GetString(Hex.Encode(source));
string sc3 = Encoding.ASCII.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.ASCII.GetString(Hex.Encode(encryptedData));
byte[] c1Bytes = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(0, 130)));
int c2Len = encryptedData.Length - 97;
byte[] c2 = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(130, 2 * c2Len)));
byte[] c3 = Hex.Decode(Encoding.ASCII.GetBytes(data.Substring(130 + 2 * c2Len, 64)));
SM2 sm2 = SM2.Instance;
var userD = new BigInteger(1, privateKey);
ECPoint c1 = sm2.ecc_curve.DecodePoint(c1Bytes);
var 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 = "";
// // 国密规范正式公钥
// String pubk = "";
// System.Console.Out.WriteLine("加密: ");
// String cipherText = SM2Utils.Encrypt(Hex.Decode(pubk), sourceData);
// System.Console.Out.WriteLine(cipherText);
// System.Console.Out.WriteLine("解密: ");
// plainText = Encoding.Default.GetString(SM2Utils.Decrypt(Hex.Decode(prik), Hex.Decode(cipherText)));
// System.Console.Out.WriteLine(plainText);
// Console.ReadLine();
//}
/// <summary>
/// SM2加密
/// </summary>
/// <param name="plainText">明文</param>
/// <returns>密文</returns>
public static String 加密(String plainText)
{
// 国密规范正式公钥
String pubk = "";
byte[] sourceData = Encoding.Default.GetBytes(plainText);
String cipherText = SM2Util.Encrypt(Hex.Decode(pubk), sourceData);
return cipherText;
}
/// <summary>
/// SM2解密
/// </summary>
/// <param name="cipherText">密文</param>
/// <returns>明文</returns>
public static string 解密(String cipherText)
{
// 国密规范正式私钥
String prik = "";
String plainText = Encoding.Default.GetString(SM2Util.Decrypt(Hex.Decode(prik), Hex.Decode(cipherText)));
return plainText;
}
}
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;
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_curve = new FpCurve(ecc_p, ecc_a, ecc_b, null, null);
ecc_point_g = ecc_curve.CreatePoint(ecc_gx, ecc_gy);
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)
{
var 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.AffineXCoord.ToBigInteger().ToByteArray();
sm3.BlockUpdate(p, 0, p.Length);
p = userKey.AffineYCoord.ToBigInteger().ToByteArray();
sm3.BlockUpdate(p, 0, p.Length);
// Z
byte[] md = new byte[sm3.GetDigestSize()];
sm3.DoFinal(md, 0);
return md;
}
}
总结
SM2 加解密联调时踩坑
1、密文数据,有些加密硬件出来密文结构为 C1|C2|C3 ,有些为 C1|C3|C2 , 需要对应密文结构做解密操作
2、有些加密硬件,公钥前加04 ,私钥前加00,密文前加04 ,在处理时候,可以根据长度处理,尤其 04 的处理。