#nullable enable using System; using System.Numerics; using System.Security.Cryptography; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security { /// /// Contains the RSA private and public key. /// public class RsaKey : Key, IDisposable { private RsaDigitalSignature? _digitalSignature; /// /// Gets the name of the key. /// /// /// The name of the key. /// public override string ToString() { return "ssh-rsa"; } internal RSA RSA { get; } /// /// Gets the modulus. /// /// /// The modulus. /// public BigInteger Modulus { get; } /// /// Gets the exponent. /// /// /// The exponent. /// public BigInteger Exponent { get; } /// /// Gets the D. /// /// /// The D. /// public BigInteger D { get; } /// /// Gets the P. /// /// /// The P. /// public BigInteger P { get; } /// /// Gets the Q. /// /// /// The Q. /// public BigInteger Q { get; } /// /// Gets the DP. /// /// /// The DP. /// public BigInteger DP { get; } /// /// Gets the DQ. /// /// /// The DQ. /// public BigInteger DQ { get; } /// /// Gets the inverse Q. /// /// /// The inverse Q. /// public BigInteger InverseQ { get; } /// public override int KeyLength { get { return (int)Modulus.GetBitLength(); } } /// /// Gets the digital signature implementation for this key. /// /// /// An implementation of an RSA digital signature using the SHA-1 hash algorithm. /// protected internal override DigitalSignature DigitalSignature { get { _digitalSignature ??= new RsaDigitalSignature(this); return _digitalSignature; } } /// /// Gets the RSA public key. /// /// /// An array with at index 0, and /// at index 1. /// public override BigInteger[] Public { get { return new[] { Exponent, Modulus }; } } /// /// Initializes a new instance of the class. /// /// The encoded public key data. public RsaKey(SshKeyData publicKeyData) { if (publicKeyData is null) { throw new ArgumentNullException(nameof(publicKeyData)); } if (publicKeyData.Name != "ssh-rsa" || publicKeyData.Keys.Length != 2) { throw new ArgumentException($"Invalid RSA public key data. ({publicKeyData.Name}, {publicKeyData.Keys.Length}).", nameof(publicKeyData)); } Exponent = publicKeyData.Keys[0]; Modulus = publicKeyData.Keys[1]; RSA = RSA.Create(); RSA.ImportParameters(GetRSAParameters()); } /// /// Initializes a new instance of the class. /// /// DER encoded private key data. public RsaKey(byte[] privateKeyData) { if (privateKeyData is null) { throw new ArgumentNullException(nameof(privateKeyData)); } var der = new DerData(privateKeyData); _ = der.ReadBigInteger(); // skip version Modulus = der.ReadBigInteger(); Exponent = der.ReadBigInteger(); D = der.ReadBigInteger(); P = der.ReadBigInteger(); Q = der.ReadBigInteger(); DP = der.ReadBigInteger(); DQ = der.ReadBigInteger(); InverseQ = der.ReadBigInteger(); if (!der.IsEndOfData) { throw new InvalidOperationException("Invalid private key (expected EOF)."); } RSA = RSA.Create(); RSA.ImportParameters(GetRSAParameters()); } /// /// Initializes a new instance of the class. /// /// The modulus. /// The exponent. /// The d. /// The p. /// The q. /// The inverse Q. public RsaKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q, BigInteger inverseQ) { Modulus = modulus; Exponent = exponent; D = d; P = p; Q = q; DP = PrimeExponent(d, p); DQ = PrimeExponent(d, q); InverseQ = inverseQ; RSA = RSA.Create(); RSA.ImportParameters(GetRSAParameters()); } internal RSAParameters GetRSAParameters() { // Specification of the RSAParameters fields (taken from the CryptographicException // thrown when not done correctly): // Exponent and Modulus are required. If D is present, it must have the same length // as Modulus. If D is present, P, Q, DP, DQ, and InverseQ are required and must // have half the length of Modulus, rounded up, otherwise they must be omitted. // See also https://github.com/dotnet/runtime/blob/9b57a265c7efd3732b035bade005561a04767128/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs#L42 if (D.IsZero) { // Public key return new RSAParameters() { Modulus = Modulus.ToByteArray(isUnsigned: true, isBigEndian: true), Exponent = Exponent.ToByteArray(isUnsigned: true, isBigEndian: true), }; } var n = Modulus.ToByteArray(isUnsigned: true, isBigEndian: true); var halfModulusLength = (n.Length + 1) / 2; return new RSAParameters() { Modulus = n, Exponent = Exponent.ToByteArray(isUnsigned: true, isBigEndian: true), D = D.ExportKeyParameter(n.Length), P = P.ExportKeyParameter(halfModulusLength), Q = Q.ExportKeyParameter(halfModulusLength), DP = DP.ExportKeyParameter(halfModulusLength), DQ = DQ.ExportKeyParameter(halfModulusLength), InverseQ = InverseQ.ExportKeyParameter(halfModulusLength), }; } private static BigInteger PrimeExponent(BigInteger privateExponent, BigInteger prime) { var pe = prime - BigInteger.One; return privateExponent % pe; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources. /// /// to release both managed and unmanaged resources; to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing) { _digitalSignature?.Dispose(); RSA.Dispose(); } } } }