RsaKey.cs 8.5 KB

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