EcdsaKey
Contains ECDSA (ecdsa-sha2-nistp{256,384,521}) private and public key.
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;
using System;
using System.Formats.Asn1;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
namespace Renci.SshNet.Security
{
[NullableContext(1)]
[Nullable(0)]
public class EcdsaKey : Key, IDisposable
{
[Nullable(0)]
private sealed class BclImpl : Impl
{
private readonly HashAlgorithmName _hashAlgorithmName;
[Nullable(2)]
[field: Nullable(2)]
public override byte[] PrivateKey {
[NullableContext(2)]
get;
}
public override ECDsa Ecdsa { get; }
public override int KeyLength => Ecdsa.KeySize;
public BclImpl(string curve_oid, int cord_size, byte[] qx, byte[] qy, [Nullable(2)] byte[] privatekey)
{
ECCurve curve = ECCurve.CreateFromValue(curve_oid);
ECParameters eCParameters = new ECParameters {
Curve = curve
};
eCParameters.Q.X = qx;
eCParameters.Q.Y = qy;
if (privatekey != null) {
eCParameters.D = privatekey.TrimLeadingZeros().Pad(cord_size);
PrivateKey = eCParameters.D;
}
Ecdsa = ECDsa.Create(eCParameters);
int keyLength = KeyLength;
HashAlgorithmName hashAlgorithmName = _hashAlgorithmName = ((keyLength <= 256) ? HashAlgorithmName.SHA256 : ((keyLength > 384) ? HashAlgorithmName.SHA512 : HashAlgorithmName.SHA384));
}
public override byte[] Sign(byte[] input)
{
return Ecdsa.SignData(input, _hashAlgorithmName);
}
public override bool Verify(byte[] input, byte[] signature)
{
return Ecdsa.VerifyData(input, signature, _hashAlgorithmName);
}
public override void Export(out byte[] qx, out byte[] qy)
{
ECParameters eCParameters = Ecdsa.ExportParameters(false);
qx = eCParameters.Q.X;
qy = eCParameters.Q.Y;
}
protected override void Dispose(bool disposing)
{
if (disposing)
Ecdsa.Dispose();
}
}
[Nullable(0)]
internal abstract class Impl : IDisposable
{
public abstract int KeyLength { get; }
[Nullable(2)]
public abstract byte[] PrivateKey {
[NullableContext(2)]
get;
}
public abstract ECDsa Ecdsa { get; }
public abstract bool Verify(byte[] input, byte[] signature);
public abstract byte[] Sign(byte[] input);
public abstract void Export(out byte[] qx, out byte[] qy);
protected abstract void Dispose(bool disposing);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
private const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7";
private const string ECDSA_P384_OID_VALUE = "1.3.132.0.34";
private const string ECDSA_P521_OID_VALUE = "1.3.132.0.35";
private static readonly BigInteger Encoded256 = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.E4FB69AD864E06534C35D5133C4923F03E856CB1E35FDC1CE9AAD2B5B2FE2C3D, 8).ToBigInteger();
private static readonly BigInteger Encoded384 = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.5A07D3258D64CC99968850E8882D7274BF6A63FDD7E1F1F9825B7F264A829DBC, 8).ToBigInteger();
private static readonly BigInteger Encoded521 = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.587D06A68D49DC8EAE2FAACD5E744F37EF2ADC222B106B77BC7AB67B66F30409, 8).ToBigInteger();
[Nullable(2)]
private EcdsaDigitalSignature _digitalSignature;
internal readonly Impl _impl;
public HashAlgorithmName HashAlgorithm {
get {
switch (KeyLength) {
case 256:
return HashAlgorithmName.SHA256;
case 384:
return HashAlgorithmName.SHA384;
case 521:
return HashAlgorithmName.SHA512;
default:
return HashAlgorithmName.SHA256;
}
}
}
public override int KeyLength => _impl.KeyLength;
protected internal override DigitalSignature DigitalSignature {
get {
if (_digitalSignature == null)
_digitalSignature = new EcdsaDigitalSignature(this);
return _digitalSignature;
}
}
public override BigInteger[] Public {
get {
BigInteger bigInteger;
switch (KeyLength) {
case 256:
bigInteger = Encoded256;
break;
case 384:
bigInteger = Encoded384;
break;
default:
bigInteger = Encoded521;
break;
}
_impl.Export(out byte[] qx, out byte[] qy);
byte[] array = new byte[1 + qx.Length + qy.Length];
array[0] = 4;
Buffer.BlockCopy(qx, 0, array, 1, qx.Length);
Buffer.BlockCopy(qy, 0, array, qx.Length + 1, qy.Length);
return new BigInteger[2] {
bigInteger,
new BigInteger(array, false, true)
};
}
}
[Nullable(2)]
public byte[] PrivateKey {
[NullableContext(2)]
get {
return _impl.PrivateKey;
}
}
public ECDsa Ecdsa => _impl.Ecdsa;
public override string ToString()
{
return $"""{KeyLength}";
}
public EcdsaKey(SshKeyData publicKeyData)
{
ThrowHelper.ThrowIfNull(publicKeyData, "publicKeyData");
if (!publicKeyData.Name.StartsWith("ecdsa-sha2-", StringComparison.Ordinal) || publicKeyData.Keys.Length != 2) {
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(36, 2);
defaultInterpolatedStringHandler.AppendLiteral("Invalid ECDSA public key data. (");
defaultInterpolatedStringHandler.AppendFormatted(publicKeyData.Name);
defaultInterpolatedStringHandler.AppendLiteral(", ");
defaultInterpolatedStringHandler.AppendFormatted(publicKeyData.Keys.Length);
defaultInterpolatedStringHandler.AppendLiteral(").");
throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear(), "publicKeyData");
}
string curveOid = GetCurveOid(Encoding.ASCII.GetString(publicKeyData.Keys[0].ToByteArray(false, true)));
byte[] publickey = publicKeyData.Keys[1].ToByteArray(false, true);
_impl = Import(curveOid, publickey, null);
}
public EcdsaKey(string curve, byte[] publickey, byte[] privatekey)
{
_impl = Import(GetCurveOid(curve), publickey, privatekey);
}
public EcdsaKey(byte[] data)
{
AsnReader asnReader = new AsnReader(data, AsnEncodingRules.DER, default(AsnReaderOptions));
AsnReader asnReader2 = asnReader.ReadSequence(null);
asnReader.ThrowIfNotEmpty();
asnReader2.ReadInteger(null);
byte[] privatekey = asnReader2.ReadOctetString(null).TrimLeadingZeros();
string curve_oid = asnReader2.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0, true)).ReadObjectIdentifier(null);
int unusedBitCount = default(int);
byte[] publickey = asnReader2.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1, true)).ReadBitString(out unusedBitCount, null);
asnReader2.ThrowIfNotEmpty();
_impl = Import(curve_oid, publickey, privatekey);
}
private static Impl Import(string curve_oid, byte[] publickey, [Nullable(2)] byte[] privatekey)
{
int num = (publickey.Length - 1) / 2;
byte[] array = new byte[num];
Buffer.BlockCopy(publickey, 1, array, 0, array.Length);
byte[] array2 = new byte[num];
Buffer.BlockCopy(publickey, num + 1, array2, 0, array2.Length);
return new BclImpl(curve_oid, num, array, array2, privatekey);
}
private static string GetCurveOid(string curve)
{
if (string.Equals(curve, "nistp256", StringComparison.OrdinalIgnoreCase) || string.Equals(curve, "1.2.840.10045.3.1.7"))
return "1.2.840.10045.3.1.7";
if (string.Equals(curve, "nistp384", StringComparison.OrdinalIgnoreCase) || string.Equals(curve, "1.3.132.0.34"))
return "1.3.132.0.34";
if (string.Equals(curve, "nistp521", StringComparison.OrdinalIgnoreCase) || string.Equals(curve, "1.3.132.0.35"))
return "1.3.132.0.35";
throw new SshException("Unexpected Curve: " + curve);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing) {
_digitalSignature?.Dispose();
_impl.Dispose();
}
}
}
}