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