RsaKey.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #nullable enable
  2. using System;
  3. using System.Security.Cryptography;
  4. using Renci.SshNet.Common;
  5. using Renci.SshNet.Security.Cryptography;
  6. namespace Renci.SshNet.Security
  7. {
  8. /// <summary>
  9. /// Contains the RSA private and public key.
  10. /// </summary>
  11. public class RsaKey : Key, IDisposable
  12. {
  13. private RsaDigitalSignature? _digitalSignature;
  14. /// <summary>
  15. /// Gets the name of the key.
  16. /// </summary>
  17. /// <returns>
  18. /// The name of the key.
  19. /// </returns>
  20. public override string ToString()
  21. {
  22. return "ssh-rsa";
  23. }
  24. internal RSA RSA { get; }
  25. /// <summary>
  26. /// Gets the modulus.
  27. /// </summary>
  28. /// <value>
  29. /// The modulus.
  30. /// </value>
  31. public BigInteger Modulus { get; }
  32. /// <summary>
  33. /// Gets the exponent.
  34. /// </summary>
  35. /// <value>
  36. /// The exponent.
  37. /// </value>
  38. public BigInteger Exponent { get; }
  39. /// <summary>
  40. /// Gets the D.
  41. /// </summary>
  42. /// <value>
  43. /// The D.
  44. /// </value>
  45. public BigInteger D { get; }
  46. /// <summary>
  47. /// Gets the P.
  48. /// </summary>
  49. /// <value>
  50. /// The P.
  51. /// </value>
  52. public BigInteger P { get; }
  53. /// <summary>
  54. /// Gets the Q.
  55. /// </summary>
  56. /// <value>
  57. /// The Q.
  58. /// </value>
  59. public BigInteger Q { get; }
  60. /// <summary>
  61. /// Gets the DP.
  62. /// </summary>
  63. /// <value>
  64. /// The DP.
  65. /// </value>
  66. public BigInteger DP { get; }
  67. /// <summary>
  68. /// Gets the DQ.
  69. /// </summary>
  70. /// <value>
  71. /// The DQ.
  72. /// </value>
  73. public BigInteger DQ { get; }
  74. /// <summary>
  75. /// Gets the inverse Q.
  76. /// </summary>
  77. /// <value>
  78. /// The inverse Q.
  79. /// </value>
  80. public BigInteger InverseQ { get; }
  81. /// <summary>
  82. /// Gets the length of the key in bits.
  83. /// </summary>
  84. /// <value>
  85. /// The bit-length of the key.
  86. /// </value>
  87. public override int KeyLength
  88. {
  89. get
  90. {
  91. return Modulus.BitLength;
  92. }
  93. }
  94. /// <summary>
  95. /// Gets the digital signature implementation for this key.
  96. /// </summary>
  97. /// <value>
  98. /// An implementation of an RSA digital signature using the SHA-1 hash algorithm.
  99. /// </value>
  100. protected internal override DigitalSignature DigitalSignature
  101. {
  102. get
  103. {
  104. _digitalSignature ??= new RsaDigitalSignature(this);
  105. return _digitalSignature;
  106. }
  107. }
  108. /// <summary>
  109. /// Gets the RSA public key.
  110. /// </summary>
  111. /// <value>
  112. /// An array with <see cref="Exponent"/> at index 0, and <see cref="Modulus"/>
  113. /// at index 1.
  114. /// </value>
  115. public override BigInteger[] Public
  116. {
  117. get
  118. {
  119. return new[] { Exponent, Modulus };
  120. }
  121. }
  122. /// <summary>
  123. /// Initializes a new instance of the <see cref="RsaKey"/> class.
  124. /// </summary>
  125. /// <param name="publicKeyData">The encoded public key data.</param>
  126. public RsaKey(SshKeyData publicKeyData)
  127. {
  128. if (publicKeyData is null)
  129. {
  130. throw new ArgumentNullException(nameof(publicKeyData));
  131. }
  132. if (publicKeyData.Name != "ssh-rsa" || publicKeyData.Keys.Length != 2)
  133. {
  134. throw new ArgumentException($"Invalid RSA public key data. ({publicKeyData.Name}, {publicKeyData.Keys.Length}).", nameof(publicKeyData));
  135. }
  136. Exponent = publicKeyData.Keys[0];
  137. Modulus = publicKeyData.Keys[1];
  138. RSA = RSA.Create();
  139. RSA.ImportParameters(GetRSAParameters());
  140. }
  141. /// <summary>
  142. /// Initializes a new instance of the <see cref="RsaKey"/> class.
  143. /// </summary>
  144. /// <param name="privateKeyData">DER encoded private key data.</param>
  145. public RsaKey(byte[] privateKeyData)
  146. {
  147. if (privateKeyData is null)
  148. {
  149. throw new ArgumentNullException(nameof(privateKeyData));
  150. }
  151. var der = new DerData(privateKeyData);
  152. _ = der.ReadBigInteger(); // skip version
  153. Modulus = der.ReadBigInteger();
  154. Exponent = der.ReadBigInteger();
  155. D = der.ReadBigInteger();
  156. P = der.ReadBigInteger();
  157. Q = der.ReadBigInteger();
  158. DP = der.ReadBigInteger();
  159. DQ = der.ReadBigInteger();
  160. InverseQ = der.ReadBigInteger();
  161. if (!der.IsEndOfData)
  162. {
  163. throw new InvalidOperationException("Invalid private key (expected EOF).");
  164. }
  165. RSA = RSA.Create();
  166. RSA.ImportParameters(GetRSAParameters());
  167. }
  168. /// <summary>
  169. /// Initializes a new instance of the <see cref="RsaKey"/> class.
  170. /// </summary>
  171. /// <param name="modulus">The modulus.</param>
  172. /// <param name="exponent">The exponent.</param>
  173. /// <param name="d">The d.</param>
  174. /// <param name="p">The p.</param>
  175. /// <param name="q">The q.</param>
  176. /// <param name="inverseQ">The inverse Q.</param>
  177. public RsaKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q, BigInteger inverseQ)
  178. {
  179. Modulus = modulus;
  180. Exponent = exponent;
  181. D = d;
  182. P = p;
  183. Q = q;
  184. DP = PrimeExponent(d, p);
  185. DQ = PrimeExponent(d, q);
  186. InverseQ = inverseQ;
  187. RSA = RSA.Create();
  188. RSA.ImportParameters(GetRSAParameters());
  189. }
  190. internal RSAParameters GetRSAParameters()
  191. {
  192. // Specification of the RSAParameters fields (taken from the CryptographicException
  193. // thrown when not done correctly):
  194. // Exponent and Modulus are required. If D is present, it must have the same length
  195. // as Modulus. If D is present, P, Q, DP, DQ, and InverseQ are required and must
  196. // have half the length of Modulus, rounded up, otherwise they must be omitted.
  197. // See also https://github.com/dotnet/runtime/blob/9b57a265c7efd3732b035bade005561a04767128/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs#L42
  198. if (D.IsZero)
  199. {
  200. // Public key
  201. return new RSAParameters()
  202. {
  203. Modulus = Modulus.ToByteArray(isUnsigned: true, isBigEndian: true),
  204. Exponent = Exponent.ToByteArray(isUnsigned: true, isBigEndian: true),
  205. };
  206. }
  207. var n = Modulus.ToByteArray(isUnsigned: true, isBigEndian: true);
  208. var halfModulusLength = (n.Length + 1) / 2;
  209. return new RSAParameters()
  210. {
  211. Modulus = n,
  212. Exponent = Exponent.ToByteArray(isUnsigned: true, isBigEndian: true),
  213. D = D.ExportKeyParameter(n.Length),
  214. P = P.ExportKeyParameter(halfModulusLength),
  215. Q = Q.ExportKeyParameter(halfModulusLength),
  216. DP = DP.ExportKeyParameter(halfModulusLength),
  217. DQ = DQ.ExportKeyParameter(halfModulusLength),
  218. InverseQ = InverseQ.ExportKeyParameter(halfModulusLength),
  219. };
  220. }
  221. private static BigInteger PrimeExponent(BigInteger privateExponent, BigInteger prime)
  222. {
  223. var pe = prime - BigInteger.One;
  224. return privateExponent % pe;
  225. }
  226. /// <summary>
  227. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  228. /// </summary>
  229. public void Dispose()
  230. {
  231. Dispose(disposing: true);
  232. GC.SuppressFinalize(this);
  233. }
  234. /// <summary>
  235. /// Releases unmanaged and - optionally - managed resources.
  236. /// </summary>
  237. /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.</param>
  238. protected virtual void Dispose(bool disposing)
  239. {
  240. if (disposing)
  241. {
  242. _digitalSignature?.Dispose();
  243. RSA.Dispose();
  244. }
  245. }
  246. }
  247. }