diff --git a/src/Renci.SshNet/Common/SshData.cs b/src/Renci.SshNet/Common/SshData.cs index 54b77c6d9..578edb8c5 100644 --- a/src/Renci.SshNet/Common/SshData.cs +++ b/src/Renci.SshNet/Common/SshData.cs @@ -231,7 +231,7 @@ protected ulong ReadUInt64() /// /// The that was read. /// - protected string ReadString(Encoding encoding) + protected string ReadString(Encoding encoding = null) { return _stream.ReadString(encoding); } diff --git a/src/Renci.SshNet/Common/SshDataStream.cs b/src/Renci.SshNet/Common/SshDataStream.cs index 9b64bed3a..9e2a28051 100644 --- a/src/Renci.SshNet/Common/SshDataStream.cs +++ b/src/Renci.SshNet/Common/SshDataStream.cs @@ -249,12 +249,14 @@ public ulong ReadUInt64() /// /// Reads the next data type from the SSH data stream. /// - /// The character encoding to use. + /// The character encoding to use. Defaults to . /// /// The read from the SSH data stream. /// - public string ReadString(Encoding encoding) + public string ReadString(Encoding encoding = null) { + encoding ??= Encoding.UTF8; + var length = ReadUInt32(); if (length > int.MaxValue) diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 4a24e78a9..7eccb8dff 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -396,16 +396,16 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy HostKeyAlgorithms = new Dictionary> { - { "ssh-ed25519", data => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(), data) }, - { "ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data) }, - { "ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data) }, - { "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) }, + { "ssh-ed25519", data => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(new SshKeyData(data))) }, + { "ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(new SshKeyData(data))) }, + { "ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(new SshKeyData(data))) }, + { "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(new SshKeyData(data))) }, #pragma warning disable SA1107 // Code should not contain multiple statements on one line - { "rsa-sha2-512", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-512", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); } }, - { "rsa-sha2-256", data => { var key = new RsaKey(); return new KeyHostAlgorithm("rsa-sha2-256", key, data, new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); } }, + { "rsa-sha2-512", data => { var key = new RsaKey(new SshKeyData(data)); return new KeyHostAlgorithm("rsa-sha2-512", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); } }, + { "rsa-sha2-256", data => { var key = new RsaKey(new SshKeyData(data)); return new KeyHostAlgorithm("rsa-sha2-256", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); } }, #pragma warning restore SA1107 // Code should not contain multiple statements on one line - { "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data) }, - { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) }, + { "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(new SshKeyData(data))) }, + { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(new SshKeyData(data))) }, }; CompressionAlgorithms = new Dictionary diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs index 1373630cd..a93ffb195 100644 --- a/src/Renci.SshNet/PrivateKeyFile.cs +++ b/src/Renci.SshNet/PrivateKeyFile.cs @@ -573,12 +573,14 @@ private static Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase) switch (keyType) { case "ssh-ed25519": - // public key - publicKey = privateKeyReader.ReadBignum2(); + // https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-11#section-3.2.3 - // private key + // ENC(A) + _ = privateKeyReader.ReadBignum2(); + + // k || ENC(A) unencryptedPrivateKey = privateKeyReader.ReadBignum2(); - parsedKey = new ED25519Key(publicKey.Reverse(), unencryptedPrivateKey); + parsedKey = new ED25519Key(unencryptedPrivateKey); break; case "ecdsa-sha2-nistp256": case "ecdsa-sha2-nistp384": diff --git a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs index 65c6fceec..b0f915cc0 100644 --- a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs @@ -1,4 +1,5 @@ using System; + using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; @@ -15,57 +16,27 @@ public class DsaKey : Key, IDisposable /// /// Gets the P. /// - public BigInteger P - { - get - { - return _privateKey[0]; - } - } + public BigInteger P { get; } /// /// Gets the Q. /// - public BigInteger Q - { - get - { - return _privateKey[1]; - } - } + public BigInteger Q { get; } /// /// Gets the G. /// - public BigInteger G - { - get - { - return _privateKey[2]; - } - } + public BigInteger G { get; } /// /// Gets public key Y. /// - public BigInteger Y - { - get - { - return _privateKey[3]; - } - } + public BigInteger Y { get; } /// /// Gets private key X. /// - public BigInteger X - { - get - { - return _privateKey[4]; - } - } + public BigInteger X { get; } /// /// Gets the length of the key. @@ -94,10 +65,16 @@ protected internal override DigitalSignature DigitalSignature } /// - /// Gets or sets the public. + /// Gets the DSA public key. /// /// - /// The public. + /// An array whose values are: + /// + /// 0 + /// 1 + /// 2 + /// 3 + /// /// public override BigInteger[] Public { @@ -105,35 +82,53 @@ public override BigInteger[] Public { return new[] { P, Q, G, Y }; } - set - { - if (value.Length != 4) - { - throw new InvalidOperationException("Invalid public key."); - } - - _privateKey = value; - } } /// /// Initializes a new instance of the class. /// - public DsaKey() + /// The encoded public key data. + public DsaKey(SshKeyData publicKeyData) { - _privateKey = new BigInteger[5]; + if (publicKeyData is null) + { + throw new ArgumentNullException(nameof(publicKeyData)); + } + + if (publicKeyData.Name != "ssh-dss" || publicKeyData.Keys.Length != 4) + { + throw new ArgumentException($"Invalid DSA public key data. ({publicKeyData.Name}, {publicKeyData.Keys.Length}).", nameof(publicKeyData)); + } + + P = publicKeyData.Keys[0]; + Q = publicKeyData.Keys[1]; + G = publicKeyData.Keys[2]; + Y = publicKeyData.Keys[3]; } /// /// Initializes a new instance of the class. /// - /// DER encoded private key data. - public DsaKey(byte[] data) - : base(data) + /// DER encoded private key data. + public DsaKey(byte[] privateKeyData) { - if (_privateKey.Length != 5) + if (privateKeyData is null) + { + throw new ArgumentNullException(nameof(privateKeyData)); + } + + var der = new DerData(privateKeyData); + _ = der.ReadBigInteger(); // skip version + + P = der.ReadBigInteger(); + Q = der.ReadBigInteger(); + G = der.ReadBigInteger(); + Y = der.ReadBigInteger(); + X = der.ReadBigInteger(); + + if (!der.IsEndOfData) { - throw new InvalidOperationException("Invalid private key."); + throw new InvalidOperationException("Invalid private key (expected EOF)."); } } @@ -147,7 +142,11 @@ public DsaKey(byte[] data) /// The x. public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger x) { - _privateKey = new BigInteger[5] { p, q, g, y, x }; + P = p; + Q = q; + G = g; + Y = y; + X = x; } /// diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs index bb0037945..5ee2332c0 100644 --- a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs +++ b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs @@ -11,13 +11,7 @@ namespace Renci.SshNet.Security /// public class ED25519Key : Key, IDisposable { -#pragma warning disable IDE1006 // Naming Styles -#pragma warning disable SX1309 // Field names should begin with underscore - private readonly byte[] privateKey = new byte[Ed25519.ExpandedPrivateKeySizeInBytes]; -#pragma warning restore SX1309 // Field names should begin with underscore -#pragma warning restore IDE1006 // Naming Styles private ED25519DigitalSignature _digitalSignature; - private byte[] _publicKey = new byte[Ed25519.PublicKeySizeInBytes]; private bool _isDisposed; /// @@ -32,20 +26,16 @@ public override string ToString() } /// - /// Gets or sets the public. + /// Gets the Ed25519 public key. /// /// - /// The public. + /// An array with encoded at index 0. /// public override BigInteger[] Public { get { - return new BigInteger[] { _publicKey.ToBigInteger2() }; - } - set - { - _publicKey = value[0].ToByteArray().Reverse().TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + return new BigInteger[] { PublicKey.ToBigInteger2() }; } } @@ -78,52 +68,46 @@ protected internal override DigitalSignature DigitalSignature /// /// Gets the PublicKey Bytes. /// - public byte[] PublicKey - { - get - { - return _publicKey; - } - } + public byte[] PublicKey { get; } /// /// Gets the PrivateKey Bytes. /// - public byte[] PrivateKey - { - get - { - return privateKey; - } - } + public byte[] PrivateKey { get; } /// /// Initializes a new instance of the class. /// - public ED25519Key() + /// The encoded public key data. + public ED25519Key(SshKeyData publicKeyData) { - } + if (publicKeyData is null) + { + throw new ArgumentNullException(nameof(publicKeyData)); + } - /// - /// Initializes a new instance of the class. - /// - /// pk data. - public ED25519Key(byte[] pk) - { - _publicKey = pk.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + if (publicKeyData.Name != "ssh-ed25519" || publicKeyData.Keys.Length != 1) + { + throw new ArgumentException($"Invalid Ed25519 public key data ({publicKeyData.Name}, {publicKeyData.Keys.Length}).", nameof(publicKeyData)); + } + + PublicKey = publicKeyData.Keys[0].ToByteArray().Reverse().TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + PrivateKey = new byte[Ed25519.ExpandedPrivateKeySizeInBytes]; } /// /// Initializes a new instance of the class. /// - /// pk data. - /// sk data. - public ED25519Key(byte[] pk, byte[] sk) + /// + /// The private key data k || ENC(A) as described in RFC 8032. + /// + public ED25519Key(byte[] privateKeyData) { - _publicKey = pk.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); var seed = new byte[Ed25519.PrivateKeySeedSizeInBytes]; - Buffer.BlockCopy(sk, 0, seed, 0, seed.Length); - Ed25519.KeyPairFromSeed(out _publicKey, out privateKey, seed); + Buffer.BlockCopy(privateKeyData, 0, seed, 0, seed.Length); + Ed25519.KeyPairFromSeed(out var publicKey, out var privateKey, seed); + PublicKey = publicKey; + PrivateKey = privateKey; } /// diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs index 455bb1363..7898a92c9 100644 --- a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs @@ -140,10 +140,11 @@ protected internal override DigitalSignature DigitalSignature } /// - /// Gets or sets the public. + /// Gets the ECDSA public key. /// /// - /// The public. + /// An array with the ASCII-encoded curve identifier (e.g. "nistp256") + /// at index 0, and the public curve point Q at index 1. /// public override BigInteger[] Public { @@ -213,14 +214,6 @@ public override BigInteger[] Public // returns Curve-Name and x/y as ECPoint return new[] { new BigInteger(curve.Reverse()), new BigInteger(q.Reverse()) }; } - set - { - var curve_s = Encoding.ASCII.GetString(value[0].ToByteArray().Reverse()); - var curve_oid = GetCurveOid(curve_s); - - var publickey = value[1].ToByteArray().Reverse(); - Import(curve_oid, publickey, privatekey: null); - } } /// @@ -236,8 +229,24 @@ public override BigInteger[] Public /// /// Initializes a new instance of the class. /// - public EcdsaKey() + /// The encoded public key data. + public EcdsaKey(SshKeyData publicKeyData) { + if (publicKeyData is null) + { + throw new ArgumentNullException(nameof(publicKeyData)); + } + + if (!publicKeyData.Name.StartsWith("ecdsa-sha2-", StringComparison.Ordinal) || publicKeyData.Keys.Length != 2) + { + throw new ArgumentException($"Invalid ECDSA public key data. ({publicKeyData.Name}, {publicKeyData.Keys.Length}).", nameof(publicKeyData)); + } + + var curve_s = Encoding.ASCII.GetString(publicKeyData.Keys[0].ToByteArray().Reverse()); + var curve_oid = GetCurveOid(curve_s); + + var publickey = publicKeyData.Keys[1].ToByteArray().Reverse(); + Import(curve_oid, publickey, privatekey: null); } /// diff --git a/src/Renci.SshNet/Security/Cryptography/Key.cs b/src/Renci.SshNet/Security/Cryptography/Key.cs index 81580a1ab..54ecac7e4 100644 --- a/src/Renci.SshNet/Security/Cryptography/Key.cs +++ b/src/Renci.SshNet/Security/Cryptography/Key.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -using Renci.SshNet.Common; +using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security @@ -11,25 +8,18 @@ namespace Renci.SshNet.Security /// public abstract class Key { - /// - /// Specifies array of big integers that represent private key. - /// -#pragma warning disable SA1401 // Fields should be private - protected BigInteger[] _privateKey; -#pragma warning restore SA1401 // Fields should be private - /// /// Gets the default digital signature implementation for this key. /// protected internal abstract DigitalSignature DigitalSignature { get; } /// - /// Gets or sets the public key. + /// Gets the public key. /// /// /// The public. /// - public abstract BigInteger[] Public { get; set; } + public abstract BigInteger[] Public { get; } /// /// Gets the length of the key. @@ -44,36 +34,6 @@ public abstract class Key /// public string Comment { get; set; } - /// - /// Initializes a new instance of the class. - /// - /// DER encoded private key data. - protected Key(byte[] data) - { - if (data is null) - { - throw new ArgumentNullException(nameof(data)); - } - - var der = new DerData(data); - _ = der.ReadBigInteger(); // skip version - - var keys = new List(); - while (!der.IsEndOfData) - { - keys.Add(der.ReadBigInteger()); - } - - _privateKey = keys.ToArray(); - } - - /// - /// Initializes a new instance of the class. - /// - protected Key() - { - } - /// /// Signs the specified data with the key. /// diff --git a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs index 19c8bedc7..f342db6c1 100644 --- a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs @@ -1,4 +1,5 @@ using System; + using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; @@ -29,13 +30,7 @@ public override string ToString() /// /// The modulus. /// - public BigInteger Modulus - { - get - { - return _privateKey[0]; - } - } + public BigInteger Modulus { get; } /// /// Gets the exponent. @@ -43,13 +38,7 @@ public BigInteger Modulus /// /// The exponent. /// - public BigInteger Exponent - { - get - { - return _privateKey[1]; - } - } + public BigInteger Exponent { get; } /// /// Gets the D. @@ -57,18 +46,7 @@ public BigInteger Exponent /// /// The D. /// - public BigInteger D - { - get - { - if (_privateKey.Length > 2) - { - return _privateKey[2]; - } - - return BigInteger.Zero; - } - } + public BigInteger D { get; } /// /// Gets the P. @@ -76,18 +54,7 @@ public BigInteger D /// /// The P. /// - public BigInteger P - { - get - { - if (_privateKey.Length > 3) - { - return _privateKey[3]; - } - - return BigInteger.Zero; - } - } + public BigInteger P { get; } /// /// Gets the Q. @@ -95,18 +62,7 @@ public BigInteger P /// /// The Q. /// - public BigInteger Q - { - get - { - if (_privateKey.Length > 4) - { - return _privateKey[4]; - } - - return BigInteger.Zero; - } - } + public BigInteger Q { get; } /// /// Gets the DP. @@ -114,18 +70,7 @@ public BigInteger Q /// /// The DP. /// - public BigInteger DP - { - get - { - if (_privateKey.Length > 5) - { - return _privateKey[5]; - } - - return BigInteger.Zero; - } - } + public BigInteger DP { get; } /// /// Gets the DQ. @@ -133,18 +78,7 @@ public BigInteger DP /// /// The DQ. /// - public BigInteger DQ - { - get - { - if (_privateKey.Length > 6) - { - return _privateKey[6]; - } - - return BigInteger.Zero; - } - } + public BigInteger DQ { get; } /// /// Gets the inverse Q. @@ -152,18 +86,7 @@ public BigInteger DQ /// /// The inverse Q. /// - public BigInteger InverseQ - { - get - { - if (_privateKey.Length > 7) - { - return _privateKey[7]; - } - - return BigInteger.Zero; - } - } + public BigInteger InverseQ { get; } /// /// Gets the length of the key. @@ -196,10 +119,11 @@ protected internal override DigitalSignature DigitalSignature } /// - /// Gets or sets the public. + /// Gets the RSA public key. /// /// - /// The public. + /// An array with at index 0, and + /// at index 1. /// public override BigInteger[] Public { @@ -207,34 +131,54 @@ public override BigInteger[] Public { return new[] { Exponent, Modulus }; } - set - { - if (value.Length != 2) - { - throw new InvalidOperationException("Invalid private key."); - } - - _privateKey = new[] { value[1], value[0] }; - } } /// /// Initializes a new instance of the class. /// - public RsaKey() + /// 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]; } /// /// Initializes a new instance of the class. /// - /// DER encoded private key data. - public RsaKey(byte[] data) - : base(data) + /// DER encoded private key data. + public RsaKey(byte[] privateKeyData) { - if (_privateKey.Length != 8) + if (privateKeyData is null) { - throw new InvalidOperationException("Invalid private key."); + 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)."); } } @@ -249,22 +193,19 @@ public RsaKey(byte[] data) /// The inverse Q. public RsaKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q, BigInteger inverseQ) { - _privateKey = new BigInteger[8] - { - modulus, - exponent, - d, - p, - q, - PrimeExponent(d, p), - PrimeExponent(d, q), - inverseQ - }; + Modulus = modulus; + Exponent = exponent; + D = d; + P = p; + Q = q; + DP = PrimeExponent(d, p); + DQ = PrimeExponent(d, q); + InverseQ = inverseQ; } private static BigInteger PrimeExponent(BigInteger privateExponent, BigInteger prime) { - var pe = prime - new BigInteger(1); + var pe = prime - BigInteger.One; return privateExponent % pe; } diff --git a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs index 0a0232a58..5611c9713 100644 --- a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs +++ b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs @@ -1,8 +1,6 @@ -using System.Collections.Generic; -using System.Text; +using System.Text; using Renci.SshNet.Common; -using Renci.SshNet.Security.Chaos.NaCl; using Renci.SshNet.Security.Cryptography; namespace Renci.SshNet.Security @@ -15,17 +13,11 @@ public class KeyHostAlgorithm : HostAlgorithm /// /// Gets the key used in this host key algorithm. /// - /// - /// The key used in this host key algorithm. - /// public Key Key { get; private set; } /// /// Gets the signature implementation used in this host key algorithm. /// - /// - /// The signature implementation used in this host key algorithm. - /// public DigitalSignature DigitalSignature { get; private set; } /// @@ -47,11 +39,7 @@ public override byte[] Data /// Initializes a new instance of the class. /// /// The signature format identifier. - /// - /// - /// This constructor is typically passed a private key in order to create an encoded signature for later - /// verification by the host. - /// + /// The key used in this host key algorithm. public KeyHostAlgorithm(string name, Key key) : base(name) { @@ -63,13 +51,9 @@ public KeyHostAlgorithm(string name, Key key) /// Initializes a new instance of the class. /// /// The signature format identifier. - /// - /// + /// The key used in this host key algorithm. + /// The signature implementation used in this host key algorithm. /// - /// - /// This constructor is typically passed a private key in order to create an encoded signature for later - /// verification by the host. - /// /// The key used by is intended to be equal to . /// This is not verified. /// @@ -80,59 +64,6 @@ public KeyHostAlgorithm(string name, Key key, DigitalSignature digitalSignature) DigitalSignature = digitalSignature; } - /// - /// Initializes a new instance of the class - /// with the given encoded public key data. The data will be decoded into . - /// - /// The signature format identifier. - /// - /// Host key encoded data. - /// - /// This constructor is typically passed a new or reusable instance in - /// order to verify an encoded signature sent by the host, created by the private counterpart - /// to the host's public key, which is encoded in . - /// - public KeyHostAlgorithm(string name, Key key, byte[] data) - : base(name) - { - Key = key; - - var sshKey = new SshKeyData(); - sshKey.Load(data); - Key.Public = sshKey.Keys; - - DigitalSignature = key.DigitalSignature; - } - - /// - /// Initializes a new instance of the class - /// with the given encoded public key data. The data will be decoded into . - /// - /// The signature format identifier. - /// - /// Host key encoded data. - /// - /// - /// - /// This constructor is typically passed a new or reusable instance in - /// order to verify an encoded signature sent by the host, created by the private counterpart - /// to the host's public key, which is encoded in . - /// - /// The key used by is intended to be equal to . - /// This is not verified. - /// - public KeyHostAlgorithm(string name, Key key, byte[] data, DigitalSignature digitalSignature) - : base(name) - { - Key = key; - - var sshKey = new SshKeyData(); - sshKey.Load(data); - Key.Public = sshKey.Keys; - - DigitalSignature = digitalSignature; - } - /// /// Signs and encodes the specified data. /// @@ -162,98 +93,6 @@ public override bool VerifySignature(byte[] data, byte[] signature) return DigitalSignature.Verify(data, signatureData.Signature); } - private sealed class SshKeyData : SshData - { - private byte[] _name; - private List _keys; - - public BigInteger[] Keys - { - get - { - var keys = new BigInteger[_keys.Count]; - - for (var i = 0; i < _keys.Count; i++) - { - var key = _keys[i]; - keys[i] = key.ToBigInteger2(); - } - - return keys; - } - private set - { - _keys = new List(value.Length); - - foreach (var key in value) - { - var keyData = key.ToByteArray().Reverse(); - if (Name == "ssh-ed25519") - { - keyData = keyData.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); - } - - _keys.Add(keyData); - } - } - } - - private string Name - { - get { return Utf8.GetString(_name, 0, _name.Length); } - set { _name = Utf8.GetBytes(value); } - } - - protected override int BufferCapacity - { - get - { - var capacity = base.BufferCapacity; - capacity += 4; // Name length - capacity += _name.Length; // Name - - foreach (var key in _keys) - { - capacity += 4; // Key length - capacity += key.Length; // Key - } - - return capacity; - } - } - - public SshKeyData() - { - } - - public SshKeyData(string name, params BigInteger[] keys) - { - Name = name; - Keys = keys; - } - - protected override void LoadData() - { - _name = ReadBinary(); - _keys = new List(); - - while (!IsEndOfData) - { - _keys.Add(ReadBinary()); - } - } - - protected override void SaveData() - { - WriteBinaryString(_name); - - foreach (var key in _keys) - { - WriteBinaryString(key); - } - } - } - internal sealed class SignatureKeyData : SshData { /// @@ -306,7 +145,7 @@ public SignatureKeyData(string name, byte[] signature) /// protected override void LoadData() { - AlgorithmName = Encoding.UTF8.GetString(ReadBinary()); + AlgorithmName = ReadString(); Signature = ReadBinary(); } @@ -315,7 +154,7 @@ protected override void LoadData() /// protected override void SaveData() { - WriteBinaryString(Encoding.UTF8.GetBytes(AlgorithmName)); + Write(AlgorithmName); WriteBinaryString(Signature); } } diff --git a/src/Renci.SshNet/Security/SshKeyData.cs b/src/Renci.SshNet/Security/SshKeyData.cs new file mode 100644 index 000000000..6a3af835a --- /dev/null +++ b/src/Renci.SshNet/Security/SshKeyData.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Text; + +using Renci.SshNet.Common; +using Renci.SshNet.Security.Chaos.NaCl; + +namespace Renci.SshNet.Security +{ + /// + /// Facilitates (de)serializing encoded public key data in the format + /// specified by RFC 4253 section 6.6. + /// + /// + /// See https://datatracker.ietf.org/doc/html/rfc4253#section-6.6. + /// + public sealed class SshKeyData : SshData + { + /// + /// Gets the public key format identifier. + /// + public string Name { get; private set; } + + /// + /// Gets the public key constituents. + /// + public BigInteger[] Keys { get; private set; } + + /// + protected override int BufferCapacity + { + get + { + var capacity = base.BufferCapacity; + capacity += 4; // Name length + capacity += Encoding.UTF8.GetByteCount(Name); // Name + + foreach (var key in Keys) + { + capacity += 4; // Key length + capacity += key.BitLength / 8; // Key + } + + return capacity; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The encoded public key data. + public SshKeyData(byte[] data) + { + Load(data); + } + + /// + /// Initializes a new instance of the class. + /// + /// The public key format identifer. + /// The public key constituents. + public SshKeyData(string name, BigInteger[] keys) + { + Name = name; + Keys = keys; + } + + /// + protected override void LoadData() + { + Name = ReadString(); + var keys = new List(); + + while (!IsEndOfData) + { + keys.Add(ReadBinary().ToBigInteger2()); + } + + Keys = keys.ToArray(); + } + + /// + protected override void SaveData() + { + Write(Name); + + foreach (var key in Keys) + { + var keyData = key.ToByteArray().Reverse(); + if (Name == "ssh-ed25519") + { + keyData = keyData.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + } + + WriteBinaryString(keyData); + } + } + } +} diff --git a/test/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs b/test/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs index 54900d046..c3b070884 100644 --- a/test/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs +++ b/test/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs @@ -1,6 +1,5 @@ using BenchmarkDotNet.Attributes; -using Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers; using Renci.SshNet.Common; using Renci.SshNet.Security; @@ -18,7 +17,7 @@ public HostKeyEventArgsBenchmarks() } private static KeyHostAlgorithm GetKeyHostAlgorithm() { - using (var s = typeof(RsaCipherBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.RSA.txt")) + using (var s = typeof(HostKeyEventArgsBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.RSA.txt")) { var privateKey = new PrivateKeyFile(s); return (KeyHostAlgorithm) privateKey.HostKeyAlgorithms.First(); diff --git a/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs b/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs deleted file mode 100644 index 13c08edb1..000000000 --- a/test/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs +++ /dev/null @@ -1,49 +0,0 @@ -using BenchmarkDotNet.Attributes; - -using Renci.SshNet.Security; -using Renci.SshNet.Security.Cryptography.Ciphers; - -namespace Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers -{ - [MemoryDiagnoser] - public class RsaCipherBenchmarks - { - private readonly RsaKey _privateKey; - private readonly RsaKey _publicKey; - private readonly byte[] _data; - - public RsaCipherBenchmarks() - { - _data = new byte[128]; - - Random random = new(Seed: 12345); - random.NextBytes(_data); - - using (var s = typeof(RsaCipherBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.RSA.txt")) - { - - _privateKey = (RsaKey)new PrivateKeyFile(s).Key; - - // The implementations of RsaCipher.Encrypt/Decrypt differ based on whether the supplied RsaKey has private key information - // or only public. So we extract out the public key information to a separate variable. - _publicKey = new RsaKey() - { - Public = _privateKey.Public - }; - } - } - - [Benchmark] - public byte[] Encrypt() - { - return new RsaCipher(_publicKey).Encrypt(_data); - } - - // RSA Decrypt does not work - // [Benchmark] - // public byte[] Decrypt() - // { - // return new RsaCipher(_privateKey).Decrypt(_data); - // } - } -} diff --git a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs index d7558c2c4..acfc16382 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs @@ -57,10 +57,7 @@ public void Sha1_SignAndVerify() //Assert.IsTrue(digitalSignature.Verify(data, signedBytes)); // 'Workaround': use a key with no private key information - var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey() - { - Public = rsaKey.Public - }); + var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey(rsaKey.Modulus, rsaKey.Exponent, default, default, default, default)); Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes)); } @@ -101,10 +98,9 @@ public void Sha256_SignAndVerify() //Assert.IsTrue(digitalSignature.Verify(data, signedBytes)); // 'Workaround': use a key with no private key information - var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey() - { - Public = rsaKey.Public - }, HashAlgorithmName.SHA256); + var digitalSignaturePublic = new RsaDigitalSignature( + new RsaKey(rsaKey.Modulus, rsaKey.Exponent, default, default, default, default), + HashAlgorithmName.SHA256); Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes)); } @@ -144,10 +140,9 @@ public void Sha512_SignAndVerify() //Assert.IsTrue(digitalSignature.Verify(data, signedBytes)); // 'Workaround': use a key with no private key information - var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey() - { - Public = rsaKey.Public - }, HashAlgorithmName.SHA512); + var digitalSignaturePublic = new RsaDigitalSignature( + new RsaKey(rsaKey.Modulus, rsaKey.Exponent, default, default, default, default), + HashAlgorithmName.SHA512); Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes)); } @@ -155,7 +150,7 @@ public void Sha512_SignAndVerify() public void Constructor_InvalidHashAlgorithm_ThrowsArgumentException() { ArgumentException exception = Assert.ThrowsException( - () => new RsaDigitalSignature(new RsaKey(), new HashAlgorithmName("invalid"))); + () => new RsaDigitalSignature(GetRsaKey(), new HashAlgorithmName("invalid"))); Assert.AreEqual("hashAlgorithmName", exception.ParamName); } diff --git a/test/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs b/test/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs index d024c05a6..d41199817 100644 --- a/test/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs @@ -86,7 +86,7 @@ public void SshRsa_SignAndVerify() CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data)); - keyAlgorithm = new KeyHostAlgorithm("ssh-rsa", new RsaKey(), GetRsaPublicKeyBytes()); + keyAlgorithm = new KeyHostAlgorithm("ssh-rsa", new RsaKey(new SshKeyData(GetRsaPublicKeyBytes()))); Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes)); } @@ -126,8 +126,8 @@ public void RsaSha256_SignAndVerify() CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data)); - key = new RsaKey(); - keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", key, GetRsaPublicKeyBytes(), new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); + key = new RsaKey(new SshKeyData(GetRsaPublicKeyBytes())); + keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA256)); Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes)); } @@ -167,8 +167,8 @@ public void RsaSha512_SignAndVerify() CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data)); - key = new RsaKey(); - keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-512", key, GetRsaPublicKeyBytes(), new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); + key = new RsaKey(new SshKeyData(GetRsaPublicKeyBytes())); + keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-512", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA512)); Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes)); }