EcdsaKey
Contains ECDSA (ecdsa-sha2-nistp{256,384,521}) private and public key.
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
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
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public class EcdsaKey : Key, IDisposable
{
[System.Runtime.CompilerServices.Nullable(0)]
private sealed class BclImpl : Impl
{
private readonly HashAlgorithmName _hashAlgorithmName;
[System.Runtime.CompilerServices.Nullable(2)]
[field: System.Runtime.CompilerServices.Nullable(2)]
public override byte[] PrivateKey {
[System.Runtime.CompilerServices.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, [System.Runtime.CompilerServices.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();
}
}
[System.Runtime.CompilerServices.NullableContext(2)]
[System.Runtime.CompilerServices.Nullable(0)]
private sealed class BouncyCastleImpl : Impl
{
[System.Runtime.CompilerServices.Nullable(1)]
private readonly ECPublicKeyParameters _publicKeyParameters;
private readonly ECPrivateKeyParameters _privateKeyParameters;
[System.Runtime.CompilerServices.Nullable(1)]
private readonly DsaDigestSigner _signer;
public override byte[] PrivateKey { get; }
public override ECDsa Ecdsa { get; }
public override int KeyLength { get; }
[System.Runtime.CompilerServices.NullableContext(1)]
public BouncyCastleImpl(string curve_oid, byte[] qx, byte[] qy, [System.Runtime.CompilerServices.Nullable(2)] byte[] privatekey)
{
DerObjectIdentifier derObjectIdentifier;
IDigest digest;
switch (curve_oid) {
case "1.2.840.10045.3.1.7":
derObjectIdentifier = SecObjectIdentifiers.SecP256r1;
digest = new Sha256Digest();
KeyLength = 256;
break;
case "1.3.132.0.34":
derObjectIdentifier = SecObjectIdentifiers.SecP384r1;
digest = new Sha384Digest();
KeyLength = 384;
break;
case "1.3.132.0.35":
derObjectIdentifier = SecObjectIdentifiers.SecP521r1;
digest = new Sha512Digest();
KeyLength = 521;
break;
default:
throw new SshException("Unexpected OID: " + curve_oid);
}
_signer = new DsaDigestSigner(new ECDsaSigner(), digest, PlainDsaEncoding.Instance);
X9ECParameters byOid = SecNamedCurves.GetByOid(derObjectIdentifier);
ECNamedDomainParameters eCNamedDomainParameters = new ECNamedDomainParameters(derObjectIdentifier, byOid);
if (privatekey != null) {
_privateKeyParameters = new ECPrivateKeyParameters(new Org.BouncyCastle.Math.BigInteger(1, privatekey), eCNamedDomainParameters);
_publicKeyParameters = new ECPublicKeyParameters(eCNamedDomainParameters.G.Multiply(_privateKeyParameters.D).Normalize(), eCNamedDomainParameters);
} else
_publicKeyParameters = new ECPublicKeyParameters(byOid.Curve.CreatePoint(new Org.BouncyCastle.Math.BigInteger(1, qx), new Org.BouncyCastle.Math.BigInteger(1, qy)), eCNamedDomainParameters);
}
[System.Runtime.CompilerServices.NullableContext(1)]
public override byte[] Sign(byte[] input)
{
_signer.Init(true, _privateKeyParameters);
_signer.BlockUpdate(input, 0, input.Length);
return _signer.GenerateSignature();
}
[System.Runtime.CompilerServices.NullableContext(1)]
public override bool Verify(byte[] input, byte[] signature)
{
_signer.Init(false, _publicKeyParameters);
_signer.BlockUpdate(input, 0, input.Length);
return _signer.VerifySignature(signature);
}
[System.Runtime.CompilerServices.NullableContext(1)]
public override void Export(out byte[] qx, out byte[] qy)
{
qx = _publicKeyParameters.Q.XCoord.GetEncoded();
qy = _publicKeyParameters.Q.YCoord.GetEncoded();
}
protected override void Dispose(bool disposing)
{
}
}
[System.Runtime.CompilerServices.NullableContext(2)]
[System.Runtime.CompilerServices.Nullable(0)]
internal abstract class Impl : IDisposable
{
public abstract int KeyLength { get; }
public abstract byte[] PrivateKey { get; }
public abstract ECDsa Ecdsa { get; }
[System.Runtime.CompilerServices.NullableContext(1)]
public abstract bool Verify(byte[] input, byte[] signature);
[System.Runtime.CompilerServices.NullableContext(1)]
public abstract byte[] Sign(byte[] input);
[System.Runtime.CompilerServices.NullableContext(1)]
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 System.Numerics.BigInteger Encoded256 = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.E4FB69AD864E06534C35D5133C4923F03E856CB1E35FDC1CE9AAD2B5B2FE2C3D, 8).ToBigInteger();
private static readonly System.Numerics.BigInteger Encoded384 = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.5A07D3258D64CC99968850E8882D7274BF6A63FDD7E1F1F9825B7F264A829DBC, 8).ToBigInteger();
private static readonly System.Numerics.BigInteger Encoded521 = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.587D06A68D49DC8EAE2FAACD5E744F37EF2ADC222B106B77BC7AB67B66F30409, 8).ToBigInteger();
[System.Runtime.CompilerServices.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 System.Numerics.BigInteger[] Public {
get {
System.Numerics.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 System.Numerics.BigInteger[2] {
bigInteger,
new System.Numerics.BigInteger(array, false, true)
};
}
}
[System.Runtime.CompilerServices.Nullable(2)]
public byte[] PrivateKey {
[System.Runtime.CompilerServices.NullableContext(2)]
get {
return _impl.PrivateKey;
}
}
[System.Runtime.CompilerServices.Nullable(2)]
public ECDsa Ecdsa {
[System.Runtime.CompilerServices.NullableContext(2)]
get {
return _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)
throw new ArgumentException($"""{publicKeyData.Name}""{publicKeyData.Keys.Length}""", "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, [System.Runtime.CompilerServices.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);
try {
return new BclImpl(curve_oid, num, array, array2, privatekey);
} catch (NotImplementedException) {
return new BouncyCastleImpl(curve_oid, 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();
}
}
}
}