SMUtil.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. using System;
  2. using System.Text;
  3. using System.Security.Cryptography;
  4. using Newtonsoft.Json.Linq;
  5. using Org.BouncyCastle.Crypto;
  6. using Org.BouncyCastle.Crypto.Digests;
  7. using Org.BouncyCastle.Crypto.Engines;
  8. using Org.BouncyCastle.Crypto.Macs;
  9. using Org.BouncyCastle.Crypto.Parameters;
  10. using Org.BouncyCastle.Crypto.Generators;
  11. using Org.BouncyCastle.Crypto.Prng;
  12. using Org.BouncyCastle.Security;
  13. using Org.BouncyCastle.Crypto.Paddings;
  14. using Org.BouncyCastle.Crypto.Modes;
  15. using Org.BouncyCastle.Math;
  16. using Org.BouncyCastle.Asn1.Sec;
  17. using Org.BouncyCastle.Asn1.X9;
  18. using Org.BouncyCastle.Asn1.X509;
  19. using Org.BouncyCastle.Asn1.Pkcs;
  20. using Org.BouncyCastle.Pkcs;
  21. using Org.BouncyCastle.Utilities.Encoders;
  22. namespace MySystem
  23. {
  24. public class SMUtil
  25. {
  26. private const string KEY_ALGORITHM = "AES";
  27. public const string SM4_ECB_PKCS5PADDING = "SM4/ECB/PKCS5Padding";
  28. public const string ALGORITHM_NAME = "SM4";
  29. static SMUtil()
  30. {
  31. // 在 .NET 中不需要像 Java 那样显式注册提供者,BouncyCastle 通过 NuGet 引用即可使用
  32. }
  33. // 生成一个对称秘钥(返回字符串,行为与 Java 版保持一致:base64 后取前 16 个字符)
  34. public static string GetSymmetrickey()
  35. {
  36. try
  37. {
  38. byte[] keyBytes = new byte[32]; // 256 bits
  39. using (var rng = new RNGCryptoServiceProvider())
  40. {
  41. rng.GetBytes(keyBytes);
  42. }
  43. string key = Convert.ToBase64String(keyBytes);
  44. if (key.Length >= 16)
  45. key = key.Substring(0, 16);
  46. return key;
  47. }
  48. catch
  49. {
  50. return string.Empty;
  51. }
  52. }
  53. // 生成 SM2 公私钥对,返回 { publicKeyBase64, privateKeyBase64 }
  54. public static string[] GenerateSmKey()
  55. {
  56. try
  57. {
  58. X9ECParameters ecP = ECNamedCurveTable.GetByName("sm2p256v1");
  59. ECDomainParameters domainParams = new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H);
  60. var gen = new ECKeyPairGenerator();
  61. var secureRandom = new SecureRandom();
  62. var keyGenParam = new ECKeyGenerationParameters(domainParams, secureRandom);
  63. gen.Init(keyGenParam);
  64. AsymmetricCipherKeyPair pair = gen.GenerateKeyPair();
  65. // 构造 X.509 SubjectPublicKeyInfo(使用公钥点),以及 PKCS#8 PrivateKeyInfo
  66. var pubParam = pair.Public as ECPublicKeyParameters;
  67. var privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
  68. // AlgorithmIdentifier: id-ecPublicKey + named curve OID
  69. var curveOid = ECNamedCurveTable.GetOid("sm2p256v1");
  70. var algId = new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, curveOid);
  71. var spki = new SubjectPublicKeyInfo(algId, pubParam.Q.GetEncoded(false));
  72. byte[] pubBytes = spki.ToAsn1Object().GetEncoded();
  73. byte[] privBytes = privInfo.ToAsn1Object().GetEncoded();
  74. return new string[] { Convert.ToBase64String(pubBytes), Convert.ToBase64String(privBytes) };
  75. }
  76. catch (Exception)
  77. {
  78. return null;
  79. }
  80. }
  81. // 从 base64 字符串恢复公钥参数(AsymmetricKeyParameter)
  82. public static AsymmetricKeyParameter CreatePublicKey(string publicKeyBase64)
  83. {
  84. try
  85. {
  86. byte[] data = Convert.FromBase64String(publicKeyBase64);
  87. return PublicKeyFactory.CreateKey(data);
  88. }
  89. catch (Exception)
  90. {
  91. return null;
  92. }
  93. }
  94. // 从 base64 字符串恢复私钥参数
  95. public static AsymmetricKeyParameter CreatePrivateKey(string privateKeyBase64)
  96. {
  97. try
  98. {
  99. byte[] data = Convert.FromBase64String(privateKeyBase64);
  100. return PrivateKeyFactory.CreateKey(data);
  101. }
  102. catch (Exception)
  103. {
  104. return null;
  105. }
  106. }
  107. // SM2 加密(对称秘钥字符串使用 SM2 公钥加密,返回 base64)
  108. public static string Sm2Encrypt(string symmetrickey, string pubKeyBase64)
  109. {
  110. try
  111. {
  112. var pubParam = CreatePublicKey(pubKeyBase64) as ECPublicKeyParameters;
  113. if (pubParam == null) return null;
  114. var engine = new SM2Engine();
  115. engine.Init(true, new ParametersWithRandom(pubParam, new SecureRandom()));
  116. byte[] inBytes = Encoding.UTF8.GetBytes(symmetrickey);
  117. byte[] outBytes = engine.ProcessBlock(inBytes, 0, inBytes.Length);
  118. return Convert.ToBase64String(outBytes);
  119. }
  120. catch (Exception)
  121. {
  122. return null;
  123. }
  124. }
  125. // SM2 解密(传入 base64 密文和私钥 base64)
  126. public static string Sm2Decrypt(string cipherBase64, string priKeyBase64)
  127. {
  128. try
  129. {
  130. var priParam = CreatePrivateKey(priKeyBase64) as ECPrivateKeyParameters;
  131. if (priParam == null) return null;
  132. var engine = new SM2Engine();
  133. engine.Init(false, priParam);
  134. byte[] inBytes = Convert.FromBase64String(cipherBase64);
  135. byte[] outBytes = engine.ProcessBlock(inBytes, 0, inBytes.Length);
  136. return Encoding.UTF8.GetString(outBytes);
  137. }
  138. catch (Exception)
  139. {
  140. return null;
  141. }
  142. }
  143. // SM3 摘要
  144. public static byte[] Sm3(byte[] srcData)
  145. {
  146. var digest = new SM3Digest();
  147. digest.BlockUpdate(srcData, 0, srcData.Length);
  148. byte[] result = new byte[digest.GetDigestSize()];
  149. digest.DoFinal(result, 0);
  150. return result;
  151. }
  152. public static string Sm3Hex(string srcData)
  153. {
  154. byte[] hash = Sm3(Encoding.UTF8.GetBytes(srcData));
  155. return Hex.ToHexString(hash);
  156. }
  157. // HMAC-SM3
  158. public static byte[] HmacSm3(byte[] key, byte[] srcData)
  159. {
  160. var mac = new HMac(new SM3Digest());
  161. mac.Init(new KeyParameter(key));
  162. mac.BlockUpdate(srcData, 0, srcData.Length);
  163. byte[] result = new byte[mac.GetMacSize()];
  164. mac.DoFinal(result, 0);
  165. return result;
  166. }
  167. public static string HmacSm3Hex(string key, string srcData)
  168. {
  169. byte[] hash = HmacSm3(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(srcData));
  170. return Hex.ToHexString(hash);
  171. }
  172. // 另一种调用 MessageDigest 的 SM3 实现(使用 BouncyCastle 的 Digest 就足够了)
  173. public static byte[] Sm3Bc(byte[] srcData)
  174. {
  175. return Sm3(srcData);
  176. }
  177. public static string Sm3BcHex(string srcData)
  178. {
  179. return Sm3Hex(srcData);
  180. }
  181. // SM4 加解密(使用 BouncyCastle 的块加密器和填充)
  182. public static string Sm4Encrypt(string symmetrickey, string data)
  183. {
  184. string algorithmName = SM4_ECB_PKCS5PADDING;
  185. byte[] key = Encoding.UTF8.GetBytes(symmetrickey);
  186. byte[] iv = Encoding.UTF8.GetBytes(symmetrickey);
  187. byte[] cipher = Sm4Core(algorithmName, true, key, iv, Encoding.UTF8.GetBytes(data));
  188. return Convert.ToBase64String(cipher);
  189. }
  190. public static string Sm4Decrypt(string symmetrickey, string data)
  191. {
  192. string algorithmName = SM4_ECB_PKCS5PADDING;
  193. byte[] key = Encoding.UTF8.GetBytes(symmetrickey);
  194. byte[] iv = Encoding.UTF8.GetBytes(symmetrickey);
  195. byte[] plain = Sm4Core(algorithmName, false, key, iv, Convert.FromBase64String(data));
  196. return Encoding.UTF8.GetString(plain);
  197. }
  198. private static byte[] Sm4Core(string algorithmName, bool forEncrypt, byte[] key, byte[] iv, byte[] input)
  199. {
  200. try
  201. {
  202. // 将 Java 的 "SM4/ECB/PKCS5Padding" 换为 BouncyCastle 可识别的形式
  203. string bcName = algorithmName.Replace("PKCS5Padding", "PKCS7Padding");
  204. var cipher = CipherUtilities.GetCipher(bcName);
  205. ICipherParameters parameters = null;
  206. if (algorithmName.Contains("/ECB/"))
  207. {
  208. parameters = new KeyParameter(key);
  209. }
  210. else
  211. {
  212. parameters = new ParametersWithIV(new KeyParameter(key), iv);
  213. }
  214. cipher.Init(forEncrypt, parameters);
  215. byte[] outBytes = cipher.DoFinal(input);
  216. return outBytes;
  217. }
  218. catch (Exception ex)
  219. {
  220. throw new Exception("SM4 operation failed", ex);
  221. }
  222. }
  223. // 将 JObject 的 key 转为小写(null 或空值替换为 "")
  224. public static JObject DealJSONObj(JObject jsonObj)
  225. {
  226. var result = new JObject();
  227. foreach (var prop in jsonObj.Properties())
  228. {
  229. JToken val = prop.Value;
  230. if (!HasValue(val))
  231. val = "";
  232. result[prop.Name.ToLower()] = val;
  233. }
  234. return result;
  235. }
  236. public static JArray DealJSONArray(JArray jsonArray)
  237. {
  238. var arr = new JArray();
  239. foreach (var item in jsonArray)
  240. {
  241. if (item is JObject jo)
  242. {
  243. arr.Add(DealJSONObj(jo));
  244. }
  245. else
  246. {
  247. arr.Add(item);
  248. }
  249. }
  250. return arr;
  251. }
  252. private static bool HasValue(JToken token)
  253. {
  254. if (token == null) return false;
  255. if (token.Type == JTokenType.Null) return false;
  256. if (token.Type == JTokenType.String)
  257. {
  258. var s = token.ToString();
  259. return !string.IsNullOrWhiteSpace(s);
  260. }
  261. return true;
  262. }
  263. }
  264. }