using System; using System.Text; using System.Security.Cryptography; using Newtonsoft.Json.Linq; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Security; using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Math; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Utilities.Encoders; namespace MySystem { public class SMUtil { private const string KEY_ALGORITHM = "AES"; public const string SM4_ECB_PKCS5PADDING = "SM4/ECB/PKCS5Padding"; public const string ALGORITHM_NAME = "SM4"; static SMUtil() { // 在 .NET 中不需要像 Java 那样显式注册提供者,BouncyCastle 通过 NuGet 引用即可使用 } // 生成一个对称秘钥(返回字符串,行为与 Java 版保持一致:base64 后取前 16 个字符) public static string GetSymmetrickey() { try { byte[] keyBytes = new byte[32]; // 256 bits using (var rng = new RNGCryptoServiceProvider()) { rng.GetBytes(keyBytes); } string key = Convert.ToBase64String(keyBytes); if (key.Length >= 16) key = key.Substring(0, 16); return key; } catch { return string.Empty; } } // 生成 SM2 公私钥对,返回 { publicKeyBase64, privateKeyBase64 } public static string[] GenerateSmKey() { try { X9ECParameters ecP = ECNamedCurveTable.GetByName("sm2p256v1"); ECDomainParameters domainParams = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H); var gen = new ECKeyPairGenerator(); var secureRandom = new SecureRandom(); var keyGenParam = new ECKeyGenerationParameters(domainParams, secureRandom); gen.Init(keyGenParam); AsymmetricCipherKeyPair pair = gen.GenerateKeyPair(); // 构造 X.509 SubjectPublicKeyInfo(使用公钥点),以及 PKCS#8 PrivateKeyInfo var pubParam = pair.Public as ECPublicKeyParameters; var privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private); // AlgorithmIdentifier: id-ecPublicKey + named curve OID var curveOid = ECNamedCurveTable.GetOid("sm2p256v1"); var algId = new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, curveOid); var spki = new SubjectPublicKeyInfo(algId, pubParam.Q.GetEncoded(false)); byte[] pubBytes = spki.ToAsn1Object().GetEncoded(); byte[] privBytes = privInfo.ToAsn1Object().GetEncoded(); return new string[] { Convert.ToBase64String(pubBytes), Convert.ToBase64String(privBytes) }; } catch (Exception) { return null; } } // 从 base64 字符串恢复公钥参数(AsymmetricKeyParameter) public static AsymmetricKeyParameter CreatePublicKey(string publicKeyBase64) { try { byte[] data = Convert.FromBase64String(publicKeyBase64); return PublicKeyFactory.CreateKey(data); } catch (Exception) { return null; } } // 从 base64 字符串恢复私钥参数 public static AsymmetricKeyParameter CreatePrivateKey(string privateKeyBase64) { try { byte[] data = Convert.FromBase64String(privateKeyBase64); return PrivateKeyFactory.CreateKey(data); } catch (Exception) { return null; } } // SM2 加密(对称秘钥字符串使用 SM2 公钥加密,返回 base64) public static string Sm2Encrypt(string symmetrickey, string pubKeyBase64) { try { var pubParam = CreatePublicKey(pubKeyBase64) as ECPublicKeyParameters; if (pubParam == null) return null; var engine = new SM2Engine(); engine.Init(true, new ParametersWithRandom(pubParam, new SecureRandom())); byte[] inBytes = Encoding.UTF8.GetBytes(symmetrickey); byte[] outBytes = engine.ProcessBlock(inBytes, 0, inBytes.Length); return Convert.ToBase64String(outBytes); } catch (Exception) { return null; } } // SM2 解密(传入 base64 密文和私钥 base64) public static string Sm2Decrypt(string cipherBase64, string priKeyBase64) { try { var priParam = CreatePrivateKey(priKeyBase64) as ECPrivateKeyParameters; if (priParam == null) return null; var engine = new SM2Engine(); engine.Init(false, priParam); byte[] inBytes = Convert.FromBase64String(cipherBase64); byte[] outBytes = engine.ProcessBlock(inBytes, 0, inBytes.Length); return Encoding.UTF8.GetString(outBytes); } catch (Exception) { return null; } } // SM3 摘要 public static byte[] Sm3(byte[] srcData) { var digest = new SM3Digest(); digest.BlockUpdate(srcData, 0, srcData.Length); byte[] result = new byte[digest.GetDigestSize()]; digest.DoFinal(result, 0); return result; } public static string Sm3Hex(string srcData) { byte[] hash = Sm3(Encoding.UTF8.GetBytes(srcData)); return Hex.ToHexString(hash); } // HMAC-SM3 public static byte[] HmacSm3(byte[] key, byte[] srcData) { var mac = new HMac(new SM3Digest()); mac.Init(new KeyParameter(key)); mac.BlockUpdate(srcData, 0, srcData.Length); byte[] result = new byte[mac.GetMacSize()]; mac.DoFinal(result, 0); return result; } public static string HmacSm3Hex(string key, string srcData) { byte[] hash = HmacSm3(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(srcData)); return Hex.ToHexString(hash); } // 另一种调用 MessageDigest 的 SM3 实现(使用 BouncyCastle 的 Digest 就足够了) public static byte[] Sm3Bc(byte[] srcData) { return Sm3(srcData); } public static string Sm3BcHex(string srcData) { return Sm3Hex(srcData); } // SM4 加解密(使用 BouncyCastle 的块加密器和填充) public static string Sm4Encrypt(string symmetrickey, string data) { string algorithmName = SM4_ECB_PKCS5PADDING; byte[] key = Encoding.UTF8.GetBytes(symmetrickey); byte[] iv = Encoding.UTF8.GetBytes(symmetrickey); byte[] cipher = Sm4Core(algorithmName, true, key, iv, Encoding.UTF8.GetBytes(data)); return Convert.ToBase64String(cipher); } public static string Sm4Decrypt(string symmetrickey, string data) { string algorithmName = SM4_ECB_PKCS5PADDING; byte[] key = Encoding.UTF8.GetBytes(symmetrickey); byte[] iv = Encoding.UTF8.GetBytes(symmetrickey); byte[] plain = Sm4Core(algorithmName, false, key, iv, Convert.FromBase64String(data)); return Encoding.UTF8.GetString(plain); } private static byte[] Sm4Core(string algorithmName, bool forEncrypt, byte[] key, byte[] iv, byte[] input) { try { // 将 Java 的 "SM4/ECB/PKCS5Padding" 换为 BouncyCastle 可识别的形式 string bcName = algorithmName.Replace("PKCS5Padding", "PKCS7Padding"); var cipher = CipherUtilities.GetCipher(bcName); ICipherParameters parameters = null; if (algorithmName.Contains("/ECB/")) { parameters = new KeyParameter(key); } else { parameters = new ParametersWithIV(new KeyParameter(key), iv); } cipher.Init(forEncrypt, parameters); byte[] outBytes = cipher.DoFinal(input); return outBytes; } catch (Exception ex) { throw new Exception("SM4 operation failed", ex); } } // 将 JObject 的 key 转为小写(null 或空值替换为 "") public static JObject DealJSONObj(JObject jsonObj) { var result = new JObject(); foreach (var prop in jsonObj.Properties()) { JToken val = prop.Value; if (!HasValue(val)) val = ""; result[prop.Name.ToLower()] = val; } return result; } public static JArray DealJSONArray(JArray jsonArray) { var arr = new JArray(); foreach (var item in jsonArray) { if (item is JObject jo) { arr.Add(DealJSONObj(jo)); } else { arr.Add(item); } } return arr; } private static bool HasValue(JToken token) { if (token == null) return false; if (token.Type == JTokenType.Null) return false; if (token.Type == JTokenType.String) { var s = token.ToString(); return !string.IsNullOrWhiteSpace(s); } return true; } } }