当前位置:   article > 正文

C# SM2加解密 ——国密SM2算法

c# sm2

 SM2 是国家密码管理局组织制定并提出的椭圆曲线密码算法标准。

本文使用第三方密码库 BouncyCastle 实现 SM2 加解密,使用 NuGet 安装即可,包名:Portable.BouncyCastle,目前最新版本为:1.9.0

  1. using Org.BouncyCastle.Asn1.GM;
  2. using Org.BouncyCastle.Crypto;
  3. using Org.BouncyCastle.Crypto.Digests;
  4. using Org.BouncyCastle.Crypto.Engines;
  5. using Org.BouncyCastle.Crypto.Generators;
  6. using Org.BouncyCastle.Crypto.Parameters;
  7. using Org.BouncyCastle.Math;
  8. using Org.BouncyCastle.Math.EC;
  9. using Org.BouncyCastle.Security;
  10. using Org.BouncyCastle.Utilities.Encoders;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Text;
  14. namespace AI_SXPA.Utility
  15. {
  16. /// <summary>
  17. /// 可用
  18. /// </summary>
  19. public class Sm2Util
  20. {
  21. /// <summary>
  22. /// 加密模式
  23. /// </summary>
  24. public enum Mode
  25. {
  26. C1C2C3,
  27. C1C3C2
  28. }
  29. private readonly Mode _mode;
  30. private readonly string _privkey;
  31. private ICipherParameters _privateKeyParameters;
  32. private string _pubkey;
  33. private ICipherParameters _publicKeyParameters;
  34. public Sm2Util(string pubkey, string privkey, Mode mode = Mode.C1C3C2, bool isPkcs8 = false)
  35. {
  36. if (pubkey != null)
  37. _pubkey = pubkey;
  38. if (privkey != null)
  39. _privkey = privkey;
  40. _mode = mode;
  41. }
  42. public Sm2Util(string pubkey, Mode mode = Mode.C1C3C2, bool isPkcs8 = false)
  43. {
  44. if (pubkey != null)
  45. _pubkey = pubkey;
  46. _mode = mode;
  47. }
  48. private ICipherParameters PrivateKeyParameters
  49. {
  50. get
  51. {
  52. try
  53. {
  54. var r = _privateKeyParameters;
  55. if (r == null)
  56. r = _privateKeyParameters =
  57. new ECPrivateKeyParameters(new BigInteger(_privkey, 16),
  58. new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));
  59. return r;
  60. }
  61. catch (Exception ex)
  62. {
  63. return null;
  64. }
  65. }
  66. }
  67. private ICipherParameters PublicKeyParameters
  68. {
  69. get
  70. {
  71. try
  72. {
  73. var r = _publicKeyParameters;
  74. if (r == null)
  75. {
  76. //截取64字节有效的SM2公钥(如果公钥首个字节为0x04)
  77. if (_pubkey.Length > 128) _pubkey = _pubkey.Substring(_pubkey.Length - 128);
  78. //将公钥拆分为x,y分量(各32字节)
  79. var stringX = _pubkey.Substring(0, 64);
  80. var stringY = _pubkey.Substring(stringX.Length);
  81. //将公钥x、y分量转换为BigInteger类型
  82. var x = new BigInteger(stringX, 16);
  83. var y = new BigInteger(stringY, 16);
  84. //通过公钥x、y分量创建椭圆曲线公钥规范
  85. var x9Ec = GMNamedCurves.GetByName("SM2P256V1");
  86. r = _publicKeyParameters = new ECPublicKeyParameters(x9Ec.Curve.CreatePoint(x, y),
  87. new ECDomainParameters(x9Ec));
  88. }
  89. return r;
  90. }
  91. catch (Exception ex)
  92. {
  93. return null;
  94. }
  95. }
  96. }
  97. /// <summary>
  98. /// 生成秘钥对
  99. /// </summary>
  100. /// <returns></returns>
  101. public static Dictionary<string, string> GenerateKeyPair()
  102. {
  103. string[] param =
  104. {
  105. "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", // p,0
  106. "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", // a,1
  107. "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", // b,2
  108. "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", // n,3
  109. "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", // gx,4
  110. "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" // gy,5
  111. };
  112. var eccParam = param;
  113. var eccP = new BigInteger(eccParam[0], 16);
  114. var eccA = new BigInteger(eccParam[1], 16);
  115. var eccB = new BigInteger(eccParam[2], 16);
  116. var eccN = new BigInteger(eccParam[3], 16);
  117. var eccGx = new BigInteger(eccParam[4], 16);
  118. var eccGy = new BigInteger(eccParam[5], 16);
  119. ECFieldElement element = new FpFieldElement(eccP, eccGx);
  120. ECFieldElement ecFieldElement = new FpFieldElement(eccP, eccGy);
  121. ECCurve eccCurve = new FpCurve(eccP, eccA, eccB);
  122. ECPoint eccPointG = new FpPoint(eccCurve, element, ecFieldElement);
  123. var bcSpec = new ECDomainParameters(eccCurve, eccPointG, eccN);
  124. var ecgenparam = new ECKeyGenerationParameters(bcSpec, new SecureRandom());
  125. var generator = new ECKeyPairGenerator();
  126. generator.Init(ecgenparam);
  127. var key = generator.GenerateKeyPair();
  128. var ecpriv = (ECPrivateKeyParameters)key.Private;
  129. var ecpub = (ECPublicKeyParameters)key.Public;
  130. var privateKey = ecpriv.D;
  131. var publicKey = ecpub.Q;
  132. var dic = new Dictionary<string, string>();
  133. dic.Add("pubkey", Encoding.Default.GetString(Hex.Encode(publicKey.GetEncoded())));
  134. dic.Add("prikey", Encoding.Default.GetString(Hex.Encode(privateKey.ToByteArray())));
  135. //dic.Add("pubkey", Encoding.Default.GetString(Hex.Encode(publicKey.GetEncoded())).ToUpper());
  136. //dic.Add("prikey", Encoding.Default.GetString(Hex.Encode(privateKey.ToByteArray())).ToUpper());
  137. return dic;
  138. }
  139. /// <summary>
  140. /// 解密
  141. /// </summary>
  142. /// <param name="data"></param>
  143. /// <returns></returns>
  144. public byte[] Decrypt(byte[] data)
  145. {
  146. try
  147. {
  148. if (_mode == Mode.C1C3C2)
  149. data = C132ToC123(data);
  150. var sm2 = new SM2Engine(new SM3Digest());
  151. sm2.Init(false, PrivateKeyParameters);
  152. return sm2.ProcessBlock(data, 0, data.Length);
  153. }
  154. catch (Exception ex)
  155. {
  156. return null;
  157. }
  158. }
  159. /// <summary>
  160. /// 加密
  161. /// </summary>
  162. /// <param name="data"></param>
  163. /// <returns></returns>
  164. public byte[] Encrypt(byte[] data)
  165. {
  166. try
  167. {
  168. var sm2 = new SM2Engine(new SM3Digest());
  169. sm2.Init(true, new ParametersWithRandom(PublicKeyParameters));
  170. data = sm2.ProcessBlock(data, 0, data.Length);
  171. if (_mode == Mode.C1C3C2)
  172. data = C123ToC132(data);
  173. return data;
  174. }
  175. catch (Exception ex)
  176. {
  177. return null;
  178. }
  179. }
  180. private static byte[] C123ToC132(byte[] c1c2c3)
  181. {
  182. var gn = GMNamedCurves.GetByName("SM2P256V1");
  183. var c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
  184. var c3Len = 32;
  185. var result = new byte[c1c2c3.Length];
  186. Array.Copy(c1c2c3, 0, result, 0, c1Len); //c1
  187. Array.Copy(c1c2c3, c1c2c3.Length - c3Len, result, c1Len, c3Len); //c3
  188. Array.Copy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.Length - c1Len - c3Len); //c2
  189. return result;
  190. }
  191. private static byte[] C132ToC123(byte[] c1c3c2)
  192. {
  193. var gn = GMNamedCurves.GetByName("SM2P256V1");
  194. var c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
  195. var c3Len = 32;
  196. var result = new byte[c1c3c2.Length];
  197. Array.Copy(c1c3c2, 0, result, 0, c1Len); //c1: 0->65
  198. Array.Copy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.Length - c1Len - c3Len); //c2
  199. Array.Copy(c1c3c2, c1Len, result, c1c3c2.Length - c3Len, c3Len); //c3
  200. return result;
  201. }
  202. /// <summary>
  203. /// 字节数组转16进制原码字符串
  204. /// </summary>
  205. /// <param name="bytes"></param>
  206. /// <returns></returns>
  207. public static string BytesToHexStr(byte[] bytes)
  208. {
  209. var str = "";
  210. if (bytes != null)
  211. for (var i = 0; i < bytes.Length; i++)
  212. str += bytes[i].ToString("X2");
  213. return str;
  214. }
  215. /// <summary>
  216. /// 16进制原码字符串转字节数组
  217. /// </summary>
  218. /// <param name="hexStr">"AABBCC"或"AA BB CC"格式的字符串</param>
  219. /// <returns></returns>
  220. public static byte[] HexStrToBytes(string hexStr)
  221. {
  222. hexStr = hexStr.Replace(" ", "");
  223. if (hexStr.Length % 2 != 0) throw new ArgumentException("参数长度不正确,必须是偶数位。");
  224. var bytes = new byte[hexStr.Length / 2];
  225. for (var i = 0; i < bytes.Length; i++)
  226. {
  227. var b = Convert.ToByte(hexStr.Substring(i * 2, 2), 16);
  228. bytes[i] = b;
  229. }
  230. return bytes;
  231. }
  232. }
  233. }

SM2 加解密联调时踩坑
1、密文数据,有些加密硬件出来密文结构为 C1|C2|C3 ,有些为 C1|C3|C2 , 需要对应密文结构做解密操作
2、有些加密硬件,公钥前加04 ,私钥前加00,密文前加04 ,在处理时候,可以根据长度处理,尤其 04 的处理。

在线验证:在线SM2加密解密,生成公钥/私钥对 (config.net.cn)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/652435
推荐阅读
相关标签
  

闽ICP备14008679号