RSAUtils.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. using System;
  2. using System.IO;
  3. using System.Security.Cryptography;
  4. namespace HiTeachCE.Extension
  5. {
  6. public static class RSAUtils
  7. {
  8. private const string PrivateKeyHeader = "-----BEGIN RSA PRIVATE KEY-----";
  9. private const string PrivateKeyFooter = "-----END RSA PRIVATE KEY-----";
  10. public static RSA? FromPrivateKey(string key)
  11. {
  12. key = key.Trim();
  13. if (!key.StartsWith(PrivateKeyHeader) || !key.EndsWith(PrivateKeyFooter))
  14. throw new ArgumentException("Expect PKCS#1 PEM format key");
  15. key = key.Substring(PrivateKeyHeader.Length, key.Length - PrivateKeyHeader.Length - PrivateKeyFooter.Length);
  16. // Convert.FromBase64String ignores whitespace
  17. byte[] keyBytes = Convert.FromBase64String(key);
  18. return DecodeRSAPrivateKey(keyBytes);
  19. }
  20. //------- Parses binary ans.1 RSA private key; returns RSA ---
  21. static RSA? DecodeRSAPrivateKey(byte[] privateKey)
  22. {
  23. // --------- Set up stream to decode the asn.1 encoded RSA private key ------
  24. using var mem = new MemoryStream(privateKey);
  25. using var binr = new BinaryReader(mem);
  26. byte bt = 0;
  27. ushort twobytes = 0;
  28. twobytes = binr.ReadUInt16();
  29. if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
  30. binr.ReadByte(); //advance 1 byte
  31. else if (twobytes == 0x8230)
  32. binr.ReadInt16(); //advance 2 bytes
  33. else
  34. return null;
  35. twobytes = binr.ReadUInt16();
  36. if (twobytes != 0x0102) //version number
  37. return null;
  38. bt = binr.ReadByte();
  39. if (bt != 0x00)
  40. return null;
  41. // ------- create RSA from private key RSAParameters -----
  42. var rsaParameters = new RSAParameters
  43. {
  44. Modulus = binr.ReadBytes(GetIntegerSize(binr)),
  45. Exponent = binr.ReadBytes(GetIntegerSize(binr)),
  46. D = binr.ReadBytes(GetIntegerSize(binr)),
  47. P = binr.ReadBytes(GetIntegerSize(binr)),
  48. Q = binr.ReadBytes(GetIntegerSize(binr)),
  49. DP = binr.ReadBytes(GetIntegerSize(binr)),
  50. DQ = binr.ReadBytes(GetIntegerSize(binr)),
  51. InverseQ = binr.ReadBytes(GetIntegerSize(binr)),
  52. };
  53. return RSA.Create(rsaParameters);
  54. }
  55. private const string PublicKeyHeader = "-----BEGIN PUBLIC KEY-----";
  56. private const string PublicKeyFooter = "-----END PUBLIC KEY-----";
  57. public static RSA? FromPublicKey(string key)
  58. {
  59. key = key.Trim();
  60. if (!key.StartsWith(PublicKeyHeader) || !key.EndsWith(PublicKeyFooter))
  61. throw new ArgumentException("Expect PKCS#1 PEM format key");
  62. key = key.Substring(PublicKeyHeader.Length, key.Length - PublicKeyHeader.Length - PublicKeyFooter.Length);
  63. // Convert.FromBase64String ignores whitespace
  64. byte[] keyBytes = Convert.FromBase64String(key);
  65. return DecodeX509PublicKey(keyBytes);
  66. }
  67. static RSA? DecodeX509PublicKey(byte[] x509Key)
  68. {
  69. // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
  70. byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
  71. // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
  72. using var mem = new MemoryStream(x509Key);
  73. using var binr = new BinaryReader(mem);
  74. ushort twobytes = binr.ReadUInt16();
  75. switch (twobytes)
  76. {
  77. case 0x8130:
  78. binr.ReadByte(); //advance 1 byte
  79. break;
  80. case 0x8230:
  81. binr.ReadInt16(); //advance 2 bytes
  82. break;
  83. default:
  84. return null;
  85. }
  86. byte[] seq = binr.ReadBytes(15);
  87. if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct
  88. return null;
  89. twobytes = binr.ReadUInt16();
  90. if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
  91. binr.ReadByte(); //advance 1 byte
  92. else if (twobytes == 0x8203)
  93. binr.ReadInt16(); //advance 2 bytes
  94. else
  95. return null;
  96. byte bt = binr.ReadByte();
  97. if (bt != 0x00) //expect null byte next
  98. return null;
  99. twobytes = binr.ReadUInt16();
  100. if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
  101. binr.ReadByte(); //advance 1 byte
  102. else if (twobytes == 0x8230)
  103. binr.ReadInt16(); //advance 2 bytes
  104. else
  105. return null;
  106. twobytes = binr.ReadUInt16();
  107. byte lowbyte = 0x00;
  108. byte highbyte = 0x00;
  109. if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
  110. lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
  111. else if (twobytes == 0x8202)
  112. {
  113. highbyte = binr.ReadByte(); //advance 2 bytes
  114. lowbyte = binr.ReadByte();
  115. }
  116. else
  117. return null;
  118. byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
  119. int modsize = BitConverter.ToInt32(modint, 0);
  120. byte firstbyte = binr.ReadByte();
  121. binr.BaseStream.Seek(-1, SeekOrigin.Current);
  122. if (firstbyte == 0x00)
  123. { //if first byte (highest order) of modulus is zero, don't include it
  124. binr.ReadByte(); //skip this null byte
  125. modsize -= 1; //reduce modulus buffer size by 1
  126. }
  127. byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
  128. if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
  129. return null;
  130. int expbytes = binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
  131. byte[] exponent = binr.ReadBytes(expbytes);
  132. // ------- create RSA from public key RSAParameters -----
  133. var rsaParameters = new RSAParameters
  134. {
  135. Modulus = modulus,
  136. Exponent = exponent
  137. };
  138. return RSA.Create(rsaParameters);
  139. }
  140. private static int GetIntegerSize(BinaryReader binr)
  141. {
  142. byte bt = binr.ReadByte();
  143. if (bt != 0x02) //expect integer
  144. return 0;
  145. bt = binr.ReadByte();
  146. int count;
  147. if (bt == 0x81)
  148. count = binr.ReadByte(); // data size in next byte
  149. else
  150. if (bt == 0x82)
  151. {
  152. byte highbyte = binr.ReadByte();
  153. byte lowbyte = binr.ReadByte();
  154. byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
  155. count = BitConverter.ToInt32(modint, 0);
  156. }
  157. else
  158. {
  159. count = bt; // we already have the data size
  160. }
  161. while (binr.ReadByte() == 0x00)
  162. { //remove high order zeros in data
  163. count -= 1;
  164. }
  165. binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
  166. return count;
  167. }
  168. static bool CompareBytearrays(byte[] a, byte[] b)
  169. {
  170. if (a.Length != b.Length)
  171. return false;
  172. int i = 0;
  173. foreach (byte c in a)
  174. {
  175. if (c != b[i])
  176. return false;
  177. i++;
  178. }
  179. return true;
  180. }
  181. }
  182. }