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 } }