C# .NET Framework 实现SM2加签、验签
本文只实现SM2的加签以及验签。
首先需要安装包 Portable.BouncyCastle。然后创建类 EasyGmutil
namespace ConsoleApp1
{
public class EasyGmUtil
{
private static X9ECParameters x9ECParameters = GMNamedCurves.GetByName("sm2p256v1");
private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
/**
*
* @param msg
* @param userId
* @param privateKey
* @return r||s,直接拼接byte数组的rs
*/
public static byte[] signSm3WithSm2(byte[] msg, byte[] userId, byte[] privateKeyBytes)
{
ECPrivateKeyParameters privateKeyParameters = getPrivatekeyFromD(new BigInteger(1, privateKeyBytes));
return rsAsn1ToPlainByteArray(signSm3WithSm2Asn1Rs(msg, userId, privateKeyParameters));
}
/**
* @param msg
* @param userId
* @param privateKey
* @return rs in <b>asn1 format</b>
*/
public static byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, AsymmetricKeyParameter privateKey)
{
try
{
ISigner signer = SignerUtilities.InitSigner("SM3withSM2", true, privateKey, new SecureRandom());
signer.BlockUpdate(msg, 0, msg.Length);
byte[] sig = signer.GenerateSignature();
return sig;
}
catch (Exception e)
{
return null;
}
}
/**
*
* @param msg
* @param userId
* @param rs r||s,直接拼接byte数组的rs
* @param publicKey
* @return
*/
public static bool verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, byte[] publicKeyBytes)
{
if (rs == null || msg == null || userId == null) return false;
if (rs.Length != RS_LEN * 2) return false;
if (publicKeyBytes.Length != 64 && publicKeyBytes.Length != 65) throw new ArgumentException("err key length");
BigInteger x, y;
if (publicKeyBytes.Length > 64)
{
x = fromUnsignedByteArray(publicKeyBytes, 1, 32);
y = fromUnsignedByteArray(publicKeyBytes, 33, 32);
}
else
{
x = fromUnsignedByteArray(publicKeyBytes, 0, 32);
y = fromUnsignedByteArray(publicKeyBytes, 32, 32);
}
ECPublicKeyParameters publicKey = getPublickeyFromXY(x, y);
return verifySm3WithSm2Asn1Rs(msg, userId, rsPlainByteArrayToAsn1(rs), publicKey);
}
public static BigInteger fromUnsignedByteArray(byte[] var0, int var1, int var2)
{
byte[] var3 = var0;
if (var1 != 0 || var2 != var0.Length)
{
var3 = new byte[var2];
Array.Copy(var0, var1, var3, 0, var2);
}
return new BigInteger(1, var3);
}
/**
*
* @param msg
* @param userId
* @param rs in <b>asn1 format</b>
* @param publicKey
* @return
*/
public static bool verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] sign, AsymmetricKeyParameter publicKey)
{
try
{
ISigner signer = SignerUtilities.GetSigner("SM3withSM2");
signer.Init(false, publicKey);
signer.BlockUpdate(msg, 0, msg.Length);
return signer.VerifySignature(sign);
}
catch (Exception e)
{
return false;
}
}
public static ECPrivateKeyParameters getPrivatekeyFromD(BigInteger d)
{
return new ECPrivateKeyParameters(d, ecDomainParameters);
}
public static ECPublicKeyParameters getPublickeyFromXY(BigInteger x, BigInteger y)
{
return new ECPublicKeyParameters(x9ECParameters.Curve.CreatePoint(x, y), ecDomainParameters);
}
private const int RS_LEN = 32;
private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS)
{
// for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123,
// r and s are the result of mod n, so they should be less than n and have length<=32
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));
}
}
/**
* BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s
* @param rsDer rs in asn1 format
* @return sign result in plain byte array
*/
private static byte[] rsAsn1ToPlainByteArray(byte[] rsDer)
{
Asn1Sequence seq = Asn1Sequence.GetInstance(rsDer);
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;
}
/**
* BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式
* @param sign in plain byte array
* @return rs result in asn1 format
*/
private static byte[] rsPlainByteArrayToAsn1(byte[] sign)
{
if (sign.Length != RS_LEN * 2) throw new ArgumentException("err rs. ");
BigInteger r = new BigInteger(1, Arrays.CopyOfRange(sign, 0, RS_LEN));
BigInteger s = new BigInteger(1, Arrays.CopyOfRange(sign, RS_LEN, RS_LEN * 2));
Asn1EncodableVector v = new Asn1EncodableVector();
v.Add(new DerInteger(r));
v.Add(new DerInteger(s));
try
{
return new DerSequence(v).GetEncoded("DER");
}
catch (IOException e)
{
//log.Error("RsPlainByteArrayToAsn1 error: " + e.Message, e);
return null;
}
}
}
}
此处为签名及验签 Demo
string PrivateKey = "AJciGDkPWcwX"; // 私钥,用于加签,此处是乱写的
string PublicKey = "BFaPS+Hj3eXg4ENOXDjrpLhwNIY"; // 公钥,与私钥配对,此处是乱写的
string userId = "";
string str = "TestContent";
//对字符串签名
byte[] signByte = EasyGmUtil.signSm3WithSm2(Encoding.UTF8.GetBytes(str),
Encoding.UTF8.GetBytes(userId), Org.BouncyCastle.Utilities.Encoders.Base64.Decode(PrivateKey));
string signStr = Org.BouncyCastle.Utilities.Encoders.Base64.ToBase64String(signByte);
//验签
bool flag = EasyGmUtil.verifySm3WithSm2(Encoding.UTF8.GetBytes(str), Encoding.UTF8.GetBytes(userId),
Org.BouncyCastle.Utilities.Encoders.Base64.Decode(signStr),
Org.BouncyCastle.Utilities.Encoders.Base64.Decode(PublicKey));
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?