|
|
|
@ -0,0 +1,266 @@
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将C1C2C3格式转换为C1C3C2格式
|
|
|
|
|
/// </summary>
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将C1C3C2格式转换为C1C2C3格式
|
|
|
|
|
/// </summary>
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|