using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using System;
using System.Text;
namespace Infrastructure.Helpers
{
public class SM2CryptoHelper
{
private static readonly ECDomainParameters DomainParams;
// 国标要求的默认用户ID
private static readonly byte[] DefaultUserId = Encoding.UTF8.GetBytes("1234567812345678");
static SM2CryptoHelper()
{
var curve = GMNamedCurves.GetByName("sm2p256v1");
DomainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
}
#region 生成密钥对
// 生成SM2密钥对
public static (byte[] privateKey, byte[] publicKey) GenerateKeyPair()
{
var gen = new ECKeyPairGenerator();
gen.Init(new ECKeyGenerationParameters(DomainParams, new SecureRandom()));
var keyPair = gen.GenerateKeyPair();
var privKey = (ECPrivateKeyParameters)keyPair.Private;
var pubKey = (ECPublicKeyParameters)keyPair.Public;
return (
AdjustTo32Bytes(privKey.D.ToByteArrayUnsigned()),
pubKey.Q.GetEncoded(false) // 未压缩格式公钥(65字节)
);
}
#endregion
#region 签名 验签
// SM2签名 (R|S裸编码,64字节)
public static byte[] Sign(byte[] privateKey, byte[] data)
{
if (privateKey == null || privateKey.Length != 32)
throw new ArgumentException("Private key must be 32 bytes");
var d = new BigInteger(1, privateKey);
var privateKeyParam = new ECPrivateKeyParameters(d, DomainParams);
// 使用国标要求的用户ID
var signer = new SM2Signer(new SM3Digest());
signer.Init(true, new ParametersWithID(
new ParametersWithRandom(privateKeyParam, new SecureRandom()),
DefaultUserId));
signer.BlockUpdate(data, 0, data.Length);
byte[] derSignature = signer.GenerateSignature();
// 解析DER格式为R|S裸编码
//return DerToRawSignature(derSignature);
// 使用更可靠的 DER 转裸签名方法
return ConvertDerToRawSignature(derSignature, 32);
}
// SM2验签
public static bool Verify(byte[] publicKey, byte[] data, byte[] signature)
{
if (signature.Length != 64)
throw new ArgumentException("Signature must be 64 bytes raw format (R|S)");
if (publicKey == null || (publicKey.Length != 65 && publicKey[0] == 0x04))
throw new ArgumentException("Public key must be in uncompressed format (65 bytes)");
try
{
// 将裸签名转换为DER格式
//byte[] derSignature = RawToDerSignature(signature);
byte[] derSignature = ConvertRawToDerSignature(signature, 32);
ECPoint q = DomainParams.Curve.DecodePoint(publicKey);
var pubKey = new ECPublicKeyParameters(q, DomainParams);
var signer = new SM2Signer(new SM3Digest());
signer.Init(false, new ParametersWithID(pubKey, DefaultUserId));
signer.BlockUpdate(data, 0, data.Length);
return signer.VerifySignature(derSignature);
}
catch
{
return false;
}
}
#endregion
#region 加密解密
// SM2加密 (C1C3C2模式)
public static byte[] Encrypt(byte[] publicKey, byte[] plaintext)
{
if (publicKey == null || publicKey.Length != 65)
throw new ArgumentException("Public key must be 65 bytes uncompressed format");
ECPoint q = DomainParams.Curve.DecodePoint(publicKey);
var pubKey = new ECPublicKeyParameters(q, DomainParams);
var cipher = new SM2Engine(new SM3Digest());
cipher.Init(true, new ParametersWithRandom(pubKey, new SecureRandom()));
byte[] encrypted = cipher.ProcessBlock(plaintext, 0, plaintext.Length);
// 转换为C1C3C2模式
return ConvertToC1C3C2(encrypted);
//return cipher.ProcessBlock(plaintext, 0, plaintext.Length);
}
///
/// 将C1C2C3格式转换为C1C3C2格式
///
private static byte[] ConvertToC1C3C2(byte[] c1c2c3)
{
// 密文结构: C1(65字节) + C2(明文长度) + C3(32字节)
int c1Length = 65; // C1部分长度
int c3Length = 32; // C3部分长度
int c2Length = c1c2c3.Length - c1Length - c3Length; // C2部分长度
if (c2Length < 0)
throw new ArgumentException("Invalid ciphertext length");
// 创建新数组: C1(65) + C3(32) + C2(变长)
byte[] c1c3c2 = new byte[c1c2c3.Length];
// 复制C1部分 (0-65字节)
Buffer.BlockCopy(c1c2c3, 0, c1c3c2, 0, c1Length);
// 复制C3部分 (最后32字节)
Buffer.BlockCopy(c1c2c3, c1Length + c2Length, c1c3c2, c1Length, c3Length);
// 复制C2部分 (65到65+c2Length)
Buffer.BlockCopy(c1c2c3, c1Length, c1c3c2, c1Length + c3Length, c2Length);
return c1c3c2;
}
// SM2解密 (C1C3C2模式)
public static byte[] Decrypt(byte[] privateKey, byte[] ciphertext)
{
if (privateKey == null || privateKey.Length != 32)
throw new ArgumentException("Private key must be 32 bytes");
// 先将密文转换为C1C2C3模式(BouncyCastle默认格式)
byte[] c1c2c3 = ConvertToC1C2C3(ciphertext);
var d = new BigInteger(1, privateKey);
var privKey = new ECPrivateKeyParameters(d, DomainParams);
// 明确指定C1C3C2模式
var cipher = new SM2Engine(new SM3Digest());
cipher.Init(false, privKey);
try
{
return cipher.ProcessBlock(c1c2c3, 0, c1c2c3.Length);
//return cipher.ProcessBlock(ciphertext, 0, ciphertext.Length);
}
catch (Exception ex)
{
throw new CryptoException("Decryption failed", ex);
}
}
///
/// 将C1C3C2格式转换为C1C2C3格式
///
private static byte[] ConvertToC1C2C3(byte[] c1c3c2)
{
// 密文结构: C1(65字节) + C3(32字节) + C2(明文长度)
int c1Length = 65; // C1部分长度
int c3Length = 32; // C3部分长度
int c2Length = c1c3c2.Length - c1Length - c3Length; // C2部分长度
if (c2Length < 0)
throw new ArgumentException("Invalid ciphertext length");
// 创建新数组: C1(65) + C2(变长) + C3(32)
byte[] c1c2c3 = new byte[c1c3c2.Length];
// 复制C1部分 (0-65字节)
Buffer.BlockCopy(c1c3c2, 0, c1c2c3, 0, c1Length);
// 复制C2部分 (65+32到结束)
Buffer.BlockCopy(c1c3c2, c1Length + c3Length, c1c2c3, c1Length, c2Length);
// 复制C3部分 (65到65+32)
Buffer.BlockCopy(c1c3c2, c1Length, c1c2c3, c1Length + c2Length, c3Length);
return c1c2c3;
}
#endregion
#region 内部工具方法
private static byte[] AdjustTo32Bytes(byte[] input)
{
if (input.Length == 32) return input;
if (input.Length > 32)
throw new ArgumentException("Input too long for 32-byte adjustment");
byte[] output = new byte[32];
Buffer.BlockCopy(input, 0, output, 32 - input.Length, input.Length);
return output;
}
// === 新增方法:可靠的 DER 转裸签名 ===
private static byte[] ConvertDerToRawSignature(byte[] derSignature, int partLength)
{
Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(derSignature);
byte[] rBytes = PadLeadingZeros(((DerInteger)seq[0]).Value.ToByteArrayUnsigned(), partLength);
byte[] sBytes = PadLeadingZeros(((DerInteger)seq[1]).Value.ToByteArrayUnsigned(), partLength);
byte[] rawSig = new byte[partLength * 2];
Buffer.BlockCopy(rBytes, 0, rawSig, 0, partLength);
Buffer.BlockCopy(sBytes, 0, rawSig, partLength, partLength);
return rawSig;
}
// === 新增方法:安全的填充处理 ===
private static byte[] PadLeadingZeros(byte[] input, int requiredLength)
{
if (input.Length == requiredLength)
return input;
if (input.Length > requiredLength)
throw new InvalidOperationException("Input longer than required length");
byte[] padded = new byte[requiredLength];
int offset = requiredLength - input.Length;
Buffer.BlockCopy(input, 0, padded, offset, input.Length);
return padded;
}
// === 新增方法:可靠的裸签名转 DER ===
private static byte[] ConvertRawToDerSignature(byte[] rawSignature, int partLength)
{
if (rawSignature.Length != partLength * 2)
throw new ArgumentException("Invalid raw signature length");
byte[] rBytes = new byte[partLength];
byte[] sBytes = new byte[partLength];
Buffer.BlockCopy(rawSignature, 0, rBytes, 0, partLength);
Buffer.BlockCopy(rawSignature, partLength, sBytes, 0, partLength);
// 移除前导零(匹配 Java 行为)
BigInteger r = new BigInteger(1, rBytes);
BigInteger s = new BigInteger(1, sBytes);
Asn1EncodableVector v = new Asn1EncodableVector();
v.Add(new DerInteger(r));
v.Add(new DerInteger(s));
return new DerSequence(v).GetEncoded();
}
#endregion
}
}