<PackageReference Include="SSH.NET" Version="2024.2.0" />

EcdsaKey

public class EcdsaKey : Key, IDisposable
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.IO; 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.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 val; IDigest val2; switch (curve_oid) { case "1.2.840.10045.3.1.7": val = SecObjectIdentifiers.SecP256r1; val2 = new Sha256Digest(); KeyLength = 256; break; case "1.3.132.0.34": val = SecObjectIdentifiers.SecP384r1; val2 = new Sha384Digest(); KeyLength = 384; break; case "1.3.132.0.35": val = SecObjectIdentifiers.SecP521r1; val2 = new Sha512Digest(); KeyLength = 521; break; default: throw new SshException("Unexpected OID: " + curve_oid); } _signer = new DsaDigestSigner(new ECDsaSigner(), val2, PlainDsaEncoding.Instance); X9ECParameters val3 = SecNamedCurves.GetByOid(val); ECNamedDomainParameters val4 = new ECNamedDomainParameters(val, val3); if (privatekey != null) { _privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, privatekey), val4); _publicKeyParameters = new ECPublicKeyParameters(val4.get_G().Multiply(_privateKeyParameters.get_D()).Normalize(), val4); } else _publicKeyParameters = new ECPublicKeyParameters(val3.get_Curve().CreatePoint(new BigInteger(1, qx), new BigInteger(1, qy)), val4); } [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.get_Q().get_XCoord().GetEncoded(); qy = _publicKeyParameters.get_Q().get_YCoord().GetEncoded(); } protected override void Dispose(bool disposing) { } } [System.Runtime.CompilerServices.Nullable(0)] private sealed class CngImpl : Impl { [System.Runtime.CompilerServices.NullableContext(0)] private enum KeyBlobMagicNumber { BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 827540293, BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 844317509, BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 861094725, BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 877871941, BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 894649157, BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 911426373 } private readonly CngKey _key; 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 CngImpl(string curve_oid, int cord_size, byte[] qx, byte[] qy, [System.Runtime.CompilerServices.Nullable(2)] byte[] privatekey) { KeyBlobMagicNumber value; switch (curve_oid) { case "1.2.840.10045.3.1.7": value = ((privatekey != null) ? KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P256_MAGIC : KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC); break; case "1.3.132.0.34": value = ((privatekey != null) ? KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P384_MAGIC : KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC); break; case "1.3.132.0.35": value = ((privatekey != null) ? KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P521_MAGIC : KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC); break; default: throw new SshException("Unknown: " + curve_oid); } if (privatekey != null) { privatekey = privatekey.Pad(cord_size); PrivateKey = privatekey; } int num = 8 + qx.Length + qy.Length; if (privatekey != null) num += privatekey.Length; byte[] array = new byte[num]; using (BinaryWriter binaryWriter = new BinaryWriter(new MemoryStream(array))) { binaryWriter.Write((int)value); binaryWriter.Write(cord_size); binaryWriter.Write(qx); binaryWriter.Write(qy); if (privatekey != null) binaryWriter.Write(privatekey); } _key = CngKey.Import(array, (privatekey == null) ? CngKeyBlobFormat.EccPublicBlob : CngKeyBlobFormat.EccPrivateBlob); Ecdsa = new ECDsaCng(_key); 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) { using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(_key.Export(CngKeyBlobFormat.EccPublicBlob)))) { binaryReader.ReadInt32(); int count = binaryReader.ReadInt32(); qx = binaryReader.ReadBytes(count); qy = binaryReader.ReadBytes(count); } } protected override void Dispose(bool disposing) { if (disposing) { Ecdsa.Dispose(); _key.Dispose(); } } } [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 BigInteger Encoded256; private static readonly BigInteger Encoded384; private static readonly BigInteger Encoded521; [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 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.Reverse()) }; } } [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(Extensions.ToByteArray(publicKeyData.Keys[0], false, true))); byte[] publickey = Extensions.ToByteArray(publicKeyData.Keys[1], 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)).ReadSequence(null); asnReader.ReadInteger(null); byte[] privatekey = asnReader.ReadOctetString(null).TrimLeadingZeros(); string curve_oid = asnReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0, true)).ReadObjectIdentifier(null); int unusedBitCount = default(int); byte[] publickey = asnReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1, true)).ReadBitString(out unusedBitCount, null); asnReader.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 CngImpl(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(); } } unsafe static EcdsaKey() { ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.E4FB69AD864E06534C35D5133C4923F03E856CB1E35FDC1CE9AAD2B5B2FE2C3D, 8); Encoded256 = new BigInteger(readOnlySpan.ToArray().Reverse()); readOnlySpan = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.5A07D3258D64CC99968850E8882D7274BF6A63FDD7E1F1F9825B7F264A829DBC, 8); Encoded384 = new BigInteger(readOnlySpan.ToArray().Reverse()); readOnlySpan = new ReadOnlySpan<byte>(&global::<PrivateImplementationDetails>.587D06A68D49DC8EAE2FAACD5E744F37EF2ADC222B106B77BC7AB67B66F30409, 8); Encoded521 = new BigInteger(readOnlySpan.ToArray().Reverse()); } } }