RsaPem.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. using Masuit.Tools.Systems;
  2. using System;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Numerics;
  6. using System.Security.Cryptography;
  7. using System.Text;
  8. using System.Text.RegularExpressions;
  9. namespace Masuit.Tools.Security
  10. {
  11. /// <summary>
  12. /// RSA PEM格式密钥对的解析和导出
  13. /// </summary>
  14. public class RsaPem
  15. {
  16. /// <summary>
  17. /// modulus 模数n,公钥、私钥都有
  18. /// </summary>
  19. public byte[] KeyModulus;
  20. /// <summary>
  21. /// publicExponent 公钥指数e,公钥、私钥都有
  22. /// </summary>
  23. public byte[] KeyExponent;
  24. /// <summary>
  25. /// privateExponent 私钥指数d,只有私钥的时候才有
  26. /// </summary>
  27. public byte[] KeyD;
  28. //以下参数只有私钥才有 https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rsaparameters?redirectedfrom=MSDN&view=netframework-4.8
  29. /// <summary>
  30. /// prime1
  31. /// </summary>
  32. public byte[] ValP;
  33. /// <summary>
  34. /// prime2
  35. /// </summary>
  36. public byte[] ValQ;
  37. /// <summary>
  38. /// exponent1
  39. /// </summary>
  40. public byte[] ValDp;
  41. /// <summary>
  42. /// exponent2
  43. /// </summary>
  44. public byte[] ValDq;
  45. /// <summary>
  46. /// coefficient
  47. /// </summary>
  48. public byte[] ValInverseQ;
  49. private RsaPem()
  50. {
  51. }
  52. /// <summary>
  53. /// 通过RSA中的公钥和私钥构造一个PEM,如果convertToPublic含私钥的RSA将只读取公钥,仅含公钥的RSA不受影响
  54. /// </summary>
  55. public RsaPem(RSACryptoServiceProvider rsa, bool convertToPublic = false)
  56. {
  57. var isPublic = convertToPublic || rsa.PublicOnly;
  58. var param = rsa.ExportParameters(!isPublic);
  59. KeyModulus = param.Modulus;
  60. KeyExponent = param.Exponent;
  61. if (!isPublic)
  62. {
  63. KeyD = param.D;
  64. ValP = param.P;
  65. ValQ = param.Q;
  66. ValDp = param.DP;
  67. ValDq = param.DQ;
  68. ValInverseQ = param.InverseQ;
  69. }
  70. }
  71. /// <summary>
  72. /// 通过全量的PEM字段数据构造一个PEM,除了模数modulus和公钥指数exponent必须提供外,其他私钥指数信息要么全部提供,要么全部不提供(导出的PEM就只包含公钥)
  73. /// 注意:所有参数首字节如果是0,必须先去掉
  74. /// </summary>
  75. public RsaPem(byte[] modulus, byte[] exponent, byte[] d, byte[] p, byte[] q, byte[] dp, byte[] dq, byte[] inverseQ)
  76. {
  77. KeyModulus = modulus;
  78. KeyExponent = exponent;
  79. KeyD = d;
  80. ValP = p;
  81. ValQ = q;
  82. ValDp = dp;
  83. ValDq = dq;
  84. ValInverseQ = inverseQ;
  85. }
  86. /// <summary>
  87. /// 通过公钥指数和私钥指数构造一个PEM,会反推计算出P、Q但和原始生成密钥的P、Q极小可能相同
  88. /// 注意:所有参数首字节如果是0,必须先去掉
  89. /// 出错将会抛出异常
  90. /// </summary>
  91. /// <param name="modulus">必须提供模数</param>
  92. /// <param name="exponent">必须提供公钥指数</param>
  93. /// <param name="dOrNull">私钥指数可以不提供,导出的PEM就只包含公钥</param>
  94. public RsaPem(byte[] modulus, byte[] exponent, byte[] dOrNull)
  95. {
  96. KeyModulus = modulus; //modulus
  97. KeyExponent = exponent; //publicExponent
  98. if (dOrNull != null)
  99. {
  100. KeyD = dOrNull; //privateExponent
  101. //反推P、Q
  102. BigInteger n = BigX(modulus);
  103. BigInteger e = BigX(exponent);
  104. BigInteger d = BigX(dOrNull);
  105. BigInteger p = FindFactor(e, d, n);
  106. BigInteger q = n / p;
  107. if (p.CompareTo(q) > 0)
  108. {
  109. (p, q) = (q, p);
  110. }
  111. BigInteger exp1 = d % (p - BigInteger.One);
  112. BigInteger exp2 = d % (q - BigInteger.One);
  113. BigInteger coeff = BigInteger.ModPow(q, p - 2, p);
  114. ValP = BigB(p); //prime1
  115. ValQ = BigB(q); //prime2
  116. ValDp = BigB(exp1); //exponent1
  117. ValDq = BigB(exp2); //exponent2
  118. ValInverseQ = BigB(coeff); //coefficient
  119. }
  120. }
  121. /// <summary>
  122. /// 密钥位数
  123. /// </summary>
  124. public int KeySize => KeyModulus.Length * 8;
  125. /// <summary>
  126. /// 是否包含私钥
  127. /// </summary>
  128. public bool HasPrivate => KeyD != null;
  129. /// <summary>
  130. /// 将PEM中的公钥私钥转成RSA对象,如果未提供私钥,RSA中就只包含公钥
  131. /// </summary>
  132. public RSACryptoServiceProvider GetRSA()
  133. {
  134. //var rsaParams = System.Security.Cryptography.RSA.Create();
  135. //rsaParams.Flags = CspProviderFlags.UseMachineKeyStore;
  136. var rsa = new RSACryptoServiceProvider();
  137. var param = new RSAParameters
  138. {
  139. Modulus = KeyModulus,
  140. Exponent = KeyExponent
  141. };
  142. if (KeyD != null)
  143. {
  144. param.D = KeyD;
  145. param.P = ValP;
  146. param.Q = ValQ;
  147. param.DP = ValDp;
  148. param.DQ = ValDq;
  149. param.InverseQ = ValInverseQ;
  150. }
  151. rsa.ImportParameters(param);
  152. return rsa;
  153. }
  154. /// <summary>
  155. /// 转成正整数,如果是负数,需要加前导0转成正整数
  156. /// </summary>
  157. public static BigInteger BigX(byte[] bigb)
  158. {
  159. if (bigb[0] > 127)
  160. {
  161. byte[] c = new byte[bigb.Length + 1];
  162. Array.Copy(bigb, 0, c, 1, bigb.Length);
  163. bigb = c;
  164. }
  165. return new BigInteger(bigb.Reverse().ToArray()); //C#的二进制是反的
  166. }
  167. /// <summary>
  168. /// BigInt导出byte整数首字节>0x7F的会加0前导,保证正整数,因此需要去掉0
  169. /// </summary>
  170. public static byte[] BigB(BigInteger bigx)
  171. {
  172. byte[] val = bigx.ToByteArray().Reverse().ToArray(); //C#的二进制是反的
  173. if (val[0] == 0)
  174. {
  175. byte[] c = new byte[val.Length - 1];
  176. Array.Copy(val, 1, c, 0, c.Length);
  177. val = c;
  178. }
  179. return val;
  180. }
  181. /// <summary>
  182. /// 由n e d 反推 P Q
  183. /// </summary>
  184. private static BigInteger FindFactor(BigInteger e, BigInteger d, BigInteger n)
  185. {
  186. BigInteger edMinus1 = e * d - BigInteger.One;
  187. int s = -1;
  188. if (edMinus1 != BigInteger.Zero)
  189. {
  190. s = (int)(BigInteger.Log(edMinus1 & -edMinus1) / BigInteger.Log(2));
  191. }
  192. BigInteger t = edMinus1 >> s;
  193. long now = DateTime.Now.Ticks;
  194. for (int aInt = 2; ; aInt++)
  195. {
  196. if (aInt % 10 == 0 && DateTime.Now.Ticks - now > 3000 * 10000)
  197. {
  198. throw new Exception("推算RSA.P超时"); //测试最多循环2次,1024位的速度很快 8ms
  199. }
  200. BigInteger aPow = BigInteger.ModPow(new BigInteger(aInt), t, n);
  201. for (int i = 1; i <= s; i++)
  202. {
  203. if (aPow == BigInteger.One)
  204. {
  205. break;
  206. }
  207. if (aPow == n - BigInteger.One)
  208. {
  209. break;
  210. }
  211. BigInteger aPowSquared = aPow * aPow % n;
  212. if (aPowSquared == BigInteger.One)
  213. {
  214. return BigInteger.GreatestCommonDivisor(aPow - BigInteger.One, n);
  215. }
  216. aPow = aPowSquared;
  217. }
  218. }
  219. }
  220. /// <summary>
  221. /// 用PEM格式密钥对创建RSA,支持PKCS#1、PKCS#8格式的PEM
  222. /// 出错将会抛出异常
  223. /// </summary>
  224. public static RsaPem FromPEM(string pem)
  225. {
  226. RsaPem param = new RsaPem();
  227. var base64 = PemCode.Replace(pem, "");
  228. byte[] data = null;
  229. try
  230. {
  231. data = Convert.FromBase64String(base64);
  232. }
  233. catch
  234. {
  235. }
  236. if (data == null)
  237. {
  238. throw new Exception("PEM内容无效");
  239. }
  240. var idx = 0;
  241. //读取长度
  242. Func<byte, int> readLen = (first) =>
  243. {
  244. if (data[idx] == first)
  245. {
  246. idx++;
  247. if (data[idx] == 0x81)
  248. {
  249. idx++;
  250. return data[idx++];
  251. }
  252. if (data[idx] == 0x82)
  253. {
  254. idx++;
  255. return ((data[idx++]) << 8) + data[idx++];
  256. }
  257. if (data[idx] < 0x80)
  258. {
  259. return data[idx++];
  260. }
  261. }
  262. throw new Exception("PEM未能提取到数据");
  263. };
  264. //读取块数据
  265. Func<byte[]> readBlock = () =>
  266. {
  267. var len = readLen(0x02);
  268. if (data[idx] == 0x00)
  269. {
  270. idx++;
  271. len--;
  272. }
  273. var val = new byte[len];
  274. for (var i = 0; i < len; i++)
  275. {
  276. val[i] = data[idx + i];
  277. }
  278. idx += len;
  279. return val;
  280. };
  281. //比较data从idx位置开始是否是byts内容
  282. Func<byte[], bool> eq = byts =>
  283. {
  284. for (var i = 0; i < byts.Length; i++, idx++)
  285. {
  286. if (idx >= data.Length)
  287. {
  288. return false;
  289. }
  290. if (byts[i] != data[idx])
  291. {
  292. return false;
  293. }
  294. }
  295. return true;
  296. };
  297. if (pem.Contains("PUBLIC KEY"))
  298. {
  299. //使用公钥
  300. //读取数据总长度
  301. readLen(0x30);
  302. //看看有没有oid
  303. var idx2 = idx;
  304. if (eq(SeqOid))
  305. {
  306. //读取1长度
  307. readLen(0x03);
  308. idx++; //跳过0x00
  309. //读取2长度
  310. readLen(0x30);
  311. }
  312. else
  313. {
  314. idx = idx2;
  315. }
  316. //Modulus
  317. param.KeyModulus = readBlock();
  318. //Exponent
  319. param.KeyExponent = readBlock();
  320. }
  321. else if (pem.Contains("PRIVATE KEY"))
  322. {
  323. //使用私钥
  324. //读取数据总长度
  325. readLen(0x30);
  326. //读取版本号
  327. if (!eq(Ver))
  328. {
  329. throw new Exception("PEM未知版本");
  330. }
  331. //检测PKCS8
  332. var idx2 = idx;
  333. if (eq(SeqOid))
  334. {
  335. //读取1长度
  336. readLen(0x04);
  337. //读取2长度
  338. readLen(0x30);
  339. //读取版本号
  340. if (!eq(Ver))
  341. {
  342. throw new Exception("PEM版本无效");
  343. }
  344. }
  345. else
  346. {
  347. idx = idx2;
  348. }
  349. //读取数据
  350. param.KeyModulus = readBlock();
  351. param.KeyExponent = readBlock();
  352. param.KeyD = readBlock();
  353. param.ValP = readBlock();
  354. param.ValQ = readBlock();
  355. param.ValDp = readBlock();
  356. param.ValDq = readBlock();
  357. param.ValInverseQ = readBlock();
  358. }
  359. else
  360. {
  361. throw new Exception("pem需要BEGIN END标头");
  362. }
  363. return param;
  364. }
  365. private static readonly Regex PemCode = new Regex(@"--+.+?--+|\s+");
  366. private static readonly byte[] SeqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
  367. private static readonly byte[] Ver = { 0x02, 0x01, 0x00 };
  368. /// <summary>
  369. /// 将RSA中的密钥对转换成PEM格式,usePKCS8=false时返回PKCS#1格式,否则返回PKCS#8格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
  370. /// </summary>
  371. public string ToPEM(bool convertToPublic, bool usePKCS8)
  372. {
  373. var ms = new PooledMemoryStream();
  374. //写入一个长度字节码
  375. Action<int> writeLenByte = len =>
  376. {
  377. if (len < 0x80)
  378. {
  379. ms.WriteByte((byte)len);
  380. }
  381. else if (len <= 0xff)
  382. {
  383. ms.WriteByte(0x81);
  384. ms.WriteByte((byte)len);
  385. }
  386. else
  387. {
  388. ms.WriteByte(0x82);
  389. ms.WriteByte((byte)(len >> 8 & 0xff));
  390. ms.WriteByte((byte)(len & 0xff));
  391. }
  392. };
  393. //写入一块数据
  394. Action<byte[]> writeBlock = byts =>
  395. {
  396. var addZero = (byts[0] >> 4) >= 0x8;
  397. ms.WriteByte(0x02);
  398. var len = byts.Length + (addZero ? 1 : 0);
  399. writeLenByte(len);
  400. if (addZero)
  401. {
  402. ms.WriteByte(0x00);
  403. }
  404. ms.Write(byts, 0, byts.Length);
  405. };
  406. //根据后续内容长度写入长度数据
  407. Func<int, byte[], byte[]> writeLen = (index, byts) =>
  408. {
  409. var len = byts.Length - index;
  410. ms.SetLength(0);
  411. ms.Write(byts, 0, index);
  412. writeLenByte(len);
  413. ms.Write(byts, index, len);
  414. return ms.ToArray();
  415. };
  416. Action<Stream, byte[]> writeAll = (stream, byts) =>
  417. {
  418. stream.Write(byts, 0, byts.Length);
  419. };
  420. Func<string, int, string> TextBreak = (text, line) =>
  421. {
  422. var idx = 0;
  423. var len = text.Length;
  424. var str = new StringBuilder();
  425. while (idx < len)
  426. {
  427. if (idx > 0)
  428. {
  429. str.Append('\n');
  430. }
  431. str.Append(idx + line >= len ? text.Substring(idx) : text.Substring(idx, line));
  432. idx += line;
  433. }
  434. return str.ToString();
  435. };
  436. if (KeyD == null || convertToPublic)
  437. {
  438. //生成公钥
  439. //写入总字节数,不含本段长度,额外需要24字节的头,后续计算好填入
  440. ms.WriteByte(0x30);
  441. var index1 = (int)ms.Length;
  442. //固定内容
  443. writeAll(ms, SeqOid);
  444. //从0x00开始的后续长度
  445. ms.WriteByte(0x03);
  446. var index2 = (int)ms.Length;
  447. ms.WriteByte(0x00);
  448. //后续内容长度
  449. ms.WriteByte(0x30);
  450. var index3 = (int)ms.Length;
  451. //写入Modulus
  452. writeBlock(KeyModulus);
  453. //写入Exponent
  454. writeBlock(KeyExponent);
  455. //计算空缺的长度
  456. var bytes = ms.ToArray();
  457. bytes = writeLen(index3, bytes);
  458. bytes = writeLen(index2, bytes);
  459. bytes = writeLen(index1, bytes);
  460. return "-----BEGIN PUBLIC KEY-----\n" + TextBreak(Convert.ToBase64String(bytes), 64) + "\n-----END PUBLIC KEY-----";
  461. }
  462. else
  463. {
  464. /****生成私钥****/
  465. //写入总字节数,后续写入
  466. ms.WriteByte(0x30);
  467. int index1 = (int)ms.Length;
  468. //写入版本号
  469. writeAll(ms, Ver);
  470. //PKCS8 多一段数据
  471. int index2 = -1, index3 = -1;
  472. if (usePKCS8)
  473. {
  474. //固定内容
  475. writeAll(ms, SeqOid);
  476. //后续内容长度
  477. ms.WriteByte(0x04);
  478. index2 = (int)ms.Length;
  479. //后续内容长度
  480. ms.WriteByte(0x30);
  481. index3 = (int)ms.Length;
  482. //写入版本号
  483. writeAll(ms, Ver);
  484. }
  485. //写入数据
  486. writeBlock(KeyModulus);
  487. writeBlock(KeyExponent);
  488. writeBlock(KeyD);
  489. writeBlock(ValP);
  490. writeBlock(ValQ);
  491. writeBlock(ValDp);
  492. writeBlock(ValDq);
  493. writeBlock(ValInverseQ);
  494. //计算空缺的长度
  495. var byts = ms.ToArray();
  496. if (index2 != -1)
  497. {
  498. byts = writeLen(index3, byts);
  499. byts = writeLen(index2, byts);
  500. }
  501. byts = writeLen(index1, byts);
  502. var flag = " PRIVATE KEY";
  503. if (!usePKCS8)
  504. {
  505. flag = " RSA" + flag;
  506. }
  507. return "-----BEGIN" + flag + "-----\n" + TextBreak(Convert.ToBase64String(byts), 64) + "\n-----END" + flag + "-----";
  508. }
  509. }
  510. /// <summary>
  511. /// 将XML格式密钥转成PEM,支持公钥xml、私钥xml
  512. /// 出错将会抛出异常
  513. /// </summary>
  514. public static RsaPem FromXML(string xml)
  515. {
  516. var rtv = new RsaPem();
  517. var xmlM = XmlExp.Match(xml);
  518. if (!xmlM.Success)
  519. {
  520. throw new Exception("XML内容不符合要求");
  521. }
  522. var tagM = XmlTagExp.Match(xmlM.Groups[1].Value);
  523. while (tagM.Success)
  524. {
  525. string tag = tagM.Groups[1].Value;
  526. string b64 = tagM.Groups[2].Value;
  527. byte[] val = Convert.FromBase64String(b64);
  528. switch (tag)
  529. {
  530. case "Modulus":
  531. rtv.KeyModulus = val;
  532. break;
  533. case "Exponent":
  534. rtv.KeyExponent = val;
  535. break;
  536. case "D":
  537. rtv.KeyD = val;
  538. break;
  539. case "P":
  540. rtv.ValP = val;
  541. break;
  542. case "Q":
  543. rtv.ValQ = val;
  544. break;
  545. case "DP":
  546. rtv.ValDp = val;
  547. break;
  548. case "DQ":
  549. rtv.ValDq = val;
  550. break;
  551. case "InverseQ":
  552. rtv.ValInverseQ = val;
  553. break;
  554. }
  555. tagM = tagM.NextMatch();
  556. }
  557. if (rtv.KeyModulus == null || rtv.KeyExponent == null)
  558. {
  559. throw new Exception("XML公钥丢失");
  560. }
  561. if (rtv.KeyD != null)
  562. {
  563. if (rtv.ValP == null || rtv.ValQ == null || rtv.ValDp == null || rtv.ValDq == null || rtv.ValInverseQ == null)
  564. {
  565. return new RsaPem(rtv.KeyModulus, rtv.KeyExponent, rtv.KeyD);
  566. }
  567. }
  568. return rtv;
  569. }
  570. private static readonly Regex XmlExp = new Regex("\\s*<RSAKeyValue>([<>\\/\\+=\\w\\s]+)</RSAKeyValue>\\s*");
  571. private static readonly Regex XmlTagExp = new Regex("<(.+?)>\\s*([^<]+?)\\s*</");
  572. /// <summary>
  573. /// 将RSA中的密钥对转换成XML格式
  574. /// ,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
  575. /// </summary>
  576. public string ToXML(bool convertToPublic)
  577. {
  578. StringBuilder str = new StringBuilder();
  579. str.Append("<RSAKeyValue>");
  580. str.Append("<Modulus>" + Convert.ToBase64String(KeyModulus) + "</Modulus>");
  581. str.Append("<Exponent>" + Convert.ToBase64String(KeyExponent) + "</Exponent>");
  582. if (KeyD != null && !convertToPublic)
  583. {
  584. /****生成私钥****/
  585. str.Append("<P>" + Convert.ToBase64String(ValP) + "</P>");
  586. str.Append("<Q>" + Convert.ToBase64String(ValQ) + "</Q>");
  587. str.Append("<DP>" + Convert.ToBase64String(ValDp) + "</DP>");
  588. str.Append("<DQ>" + Convert.ToBase64String(ValDq) + "</DQ>");
  589. str.Append("<InverseQ>" + Convert.ToBase64String(ValInverseQ) + "</InverseQ>");
  590. str.Append("<D>" + Convert.ToBase64String(KeyD) + "</D>");
  591. }
  592. str.Append("</RSAKeyValue>");
  593. return str.ToString();
  594. }
  595. }
  596. }