#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();
}
}
}
}