diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs
index af8e1ca5c..b92f6ded1 100644
--- a/src/Renci.SshNet/ConnectionInfo.cs
+++ b/src/Renci.SshNet/ConnectionInfo.cs
@@ -389,6 +389,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
#endif
{"ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data)},
{"ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data)},
+ {"rsa-sha2-256", data => new KeyHostAlgorithm("rsa-sha2-256", new RsaWithSha256SignatureKey(), data)},
//{"x509v3-sign-rsa", () => { ... },
//{"x509v3-sign-dss", () => { ... },
//{"spki-sign-rsa", () => { ... },
diff --git a/src/Renci.SshNet/IPrivateKeySource.cs b/src/Renci.SshNet/IPrivateKeySource.cs
index fc3405462..8e75251b2 100644
--- a/src/Renci.SshNet/IPrivateKeySource.cs
+++ b/src/Renci.SshNet/IPrivateKeySource.cs
@@ -10,6 +10,6 @@ public interface IPrivateKeySource
///
/// Gets the host key.
///
- HostAlgorithm HostKey { get; }
+ HostAlgorithm[] HostKeys { get; }
}
}
\ No newline at end of file
diff --git a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
index 43d80b506..58ceaffdd 100644
--- a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
+++ b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
@@ -4,6 +4,7 @@
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages;
using Renci.SshNet.Common;
+using Renci.SshNet.Security;
using System.Threading;
namespace Renci.SshNet
@@ -60,24 +61,38 @@ public override AuthenticationResult Authenticate(Session session)
session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK");
+ HostAlgorithm[] hostKeys = new HostAlgorithm[] { };
+
+ foreach (var keyFile in KeyFiles)
+ {
+ var idx = hostKeys.Length;
+
+ Array.Resize(ref hostKeys, idx + keyFile.HostKeys.Length);
+
+ for(int i = 0; i < keyFile.HostKeys.Length; i++)
+ {
+ hostKeys[idx++] = keyFile.HostKeys[i];
+ }
+ }
+
try
{
- foreach (var keyFile in KeyFiles)
+ foreach (var hostKey in hostKeys)
{
_authenticationCompleted.Reset();
_isSignatureRequired = false;
var message = new RequestMessagePublicKey(ServiceName.Connection,
Username,
- keyFile.HostKey.Name,
- keyFile.HostKey.Data);
+ hostKey.Name,
+ hostKey.Data);
- if (KeyFiles.Count < 2)
+ if (hostKeys.Length < 2)
{
// If only one key file provided then send signature for very first request
var signatureData = new SignatureData(message, session.SessionId).GetBytes();
- message.Signature = keyFile.HostKey.Sign(signatureData);
+ message.Signature = hostKey.Sign(signatureData);
}
// Send public key authentication request
@@ -91,12 +106,12 @@ public override AuthenticationResult Authenticate(Session session)
var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection,
Username,
- keyFile.HostKey.Name,
- keyFile.HostKey.Data);
+ hostKey.Name,
+ hostKey.Data);
var signatureData = new SignatureData(message, session.SessionId).GetBytes();
- signatureMessage.Signature = keyFile.HostKey.Sign(signatureData);
+ signatureMessage.Signature = hostKey.Sign(signatureData);
// Send public key authentication request with signature
session.SendMessage(signatureMessage);
diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs
index f29b3e958..f7e9c5a00 100644
--- a/src/Renci.SshNet/PrivateKeyFile.cs
+++ b/src/Renci.SshNet/PrivateKeyFile.cs
@@ -77,7 +77,7 @@ public class PrivateKeyFile : IPrivateKeySource, IDisposable
///
/// Gets the host key.
///
- public HostAlgorithm HostKey { get; private set; }
+ public HostAlgorithm[] HostKeys { get; private set; }
///
/// Initializes a new instance of the class.
@@ -85,7 +85,7 @@ public class PrivateKeyFile : IPrivateKeySource, IDisposable
/// The key.
public PrivateKeyFile(Key key)
{
- HostKey = new KeyHostAlgorithm(key.ToString(), key);
+ HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm(key.ToString(), key) };
}
///
@@ -214,22 +214,24 @@ private void Open(Stream privateKey, string passPhrase)
switch (keyName)
{
case "RSA":
- _key = new RsaKey(decryptedData);
- HostKey = new KeyHostAlgorithm("ssh-rsa", _key);
+ HostKeys = new KeyHostAlgorithm[] {
+ new KeyHostAlgorithm("rsa-sha2-256", new RsaWithSha256SignatureKey(decryptedData)),
+ new KeyHostAlgorithm("ssh-rsa", new RsaKey(decryptedData)),
+ };
break;
case "DSA":
_key = new DsaKey(decryptedData);
- HostKey = new KeyHostAlgorithm("ssh-dss", _key);
+ HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm("ssh-dss", _key) };
break;
#if FEATURE_ECDSA
case "EC":
_key = new EcdsaKey(decryptedData);
- HostKey = new KeyHostAlgorithm(_key.ToString(), _key);
+ HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm(_key.ToString(), _key) };
break;
#endif
case "OPENSSH":
_key = ParseOpenSshV1Key(decryptedData, passPhrase);
- HostKey = new KeyHostAlgorithm(_key.ToString(), _key);
+ HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm(_key.ToString(), _key) };
break;
case "SSH2 ENCRYPTED":
var reader = new SshDataReader(decryptedData);
@@ -281,7 +283,7 @@ private void Open(Stream privateKey, string passPhrase)
var q = reader.ReadBigIntWithBits();//p
var p = reader.ReadBigIntWithBits();//q
_key = new RsaKey(modulus, exponent, d, p, q, inverseQ);
- HostKey = new KeyHostAlgorithm("ssh-rsa", _key);
+ HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm("ssh-rsa", _key) };
}
else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}")
{
@@ -296,7 +298,7 @@ private void Open(Stream privateKey, string passPhrase)
var y = reader.ReadBigIntWithBits();
var x = reader.ReadBigIntWithBits();
_key = new DsaKey(p, q, g, y, x);
- HostKey = new KeyHostAlgorithm("ssh-dss", _key);
+ HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm("ssh-dss", _key) };
}
else
{
diff --git a/src/Renci.SshNet/Security/Cryptography/RsaSha256DigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/RsaSha256DigitalSignature.cs
new file mode 100644
index 000000000..2da484ae1
--- /dev/null
+++ b/src/Renci.SshNet/Security/Cryptography/RsaSha256DigitalSignature.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Security.Cryptography;
+using Renci.SshNet.Abstractions;
+using Renci.SshNet.Common;
+using Renci.SshNet.Security.Cryptography.Ciphers;
+
+namespace Renci.SshNet.Security.Cryptography
+{
+ ///
+ /// Implements RSA digital signature algorithm.
+ ///
+ public class RsaSha256DigitalSignature : CipherDigitalSignature, IDisposable
+ {
+ private HashAlgorithm _hash;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The RSA key.
+ public RsaSha256DigitalSignature(RsaWithSha256SignatureKey rsaKey)
+ : base(new ObjectIdentifier(2, 16, 840, 1, 101, 3, 4, 2, 1), new RsaCipher(rsaKey))
+ {
+ _hash = SHA256.Create();
+ }
+
+ ///
+ /// Hashes the specified input.
+ ///
+ /// The input.
+ ///
+ /// Hashed data.
+ ///
+ protected override byte[] Hash(byte[] input)
+ {
+ return _hash.ComputeHash(input);
+ }
+
+ #region IDisposable Members
+
+ private bool _isDisposed;
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Releases unmanaged and - optionally - managed resources
+ ///
+ /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ return;
+
+ if (disposing)
+ {
+ var hash = _hash;
+ if (hash != null)
+ {
+ hash.Dispose();
+ _hash = null;
+ }
+
+ _isDisposed = true;
+ }
+ }
+
+ ///
+ /// Releases unmanaged resources and performs other cleanup operations before the
+ /// is reclaimed by garbage collection.
+ ///
+ ~RsaSha256DigitalSignature()
+ {
+ Dispose(false);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Renci.SshNet/Security/Cryptography/RsaWithSha256SignatureKey.cs b/src/Renci.SshNet/Security/Cryptography/RsaWithSha256SignatureKey.cs
new file mode 100644
index 000000000..2fa6ee366
--- /dev/null
+++ b/src/Renci.SshNet/Security/Cryptography/RsaWithSha256SignatureKey.cs
@@ -0,0 +1,69 @@
+using System;
+using Renci.SshNet.Common;
+using Renci.SshNet.Security.Cryptography;
+
+namespace Renci.SshNet.Security
+{
+ ///
+ /// Contains RSA private and public key
+ ///
+ public class RsaWithSha256SignatureKey : RsaKey
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RsaWithSha256SignatureKey()
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// DER encoded private key data.
+ public RsaWithSha256SignatureKey(byte[] data)
+ : base(data)
+ {
+ if (_privateKey.Length != 8)
+ throw new InvalidOperationException("Invalid private key.");
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The modulus.
+ /// The exponent.
+ /// The d.
+ /// The p.
+ /// The q.
+ /// The inverse Q.
+ public RsaWithSha256SignatureKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q,
+ BigInteger inverseQ) : base(modulus, exponent, d, p, q, inverseQ)
+ {
+ }
+
+ private RsaSha256DigitalSignature _digitalSignature;
+
+ ///
+ /// Gets the digital signature.
+ ///
+ protected override DigitalSignature DigitalSignature
+ {
+ get
+ {
+ if (_digitalSignature == null)
+ {
+ _digitalSignature = new RsaSha256DigitalSignature(this);
+ }
+
+ return _digitalSignature;
+ }
+ }
+
+ ///
+ /// Gets the Key String.
+ ///
+ public override string ToString()
+ {
+ return "rsa-sha2-256";
+ }
+ }
+}