Browse Source

Add support for RSA SHA-2 public key algorithms (#1177)

* Abstract out the hash algorithm from RsaDigitalSignature

* Add integration tests

* Add DigitalSignature property to KeyHostAlgorithm

* Add IHostAlgorithmsProvider interface

* Verify the host signature

* Fix HostKeyEventArgsTest after merge

* Remove PubkeyAcceptedAlgorithms ssh-rsa

* Add test coverage for RSA keys in PrivateKeyFile

* Obsolete IPrivateKeySource

---------

Co-authored-by: Wojciech Nagórski <wojtpl2@gmail.com>
Rob Hague 2 years ago
parent
commit
8732d3d7ef
34 changed files with 767 additions and 210 deletions
  1. 0 2
      src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs
  2. 0 1
      src/Renci.SshNet.IntegrationTests/Dockerfile
  3. 25 47
      src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs
  4. 43 22
      src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs
  5. 1 0
      src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
  6. 2 1
      src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys
  7. 5 2
      src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
  8. 1 1
      src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs
  9. 26 10
      src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
  10. 154 10
      src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs
  11. 215 0
      src/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs
  12. 2 2
      src/Renci.SshNet.Tests/Common/TestBase.cs
  13. 16 0
      src/Renci.SshNet/Common/ObjectIdentifier.cs
  14. 4 0
      src/Renci.SshNet/ConnectionInfo.cs
  15. 21 0
      src/Renci.SshNet/IHostAlgorithmsProvider.cs
  16. 14 3
      src/Renci.SshNet/IPrivateKeySource.cs
  17. 2 2
      src/Renci.SshNet/NetConfClient.cs
  18. 15 12
      src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
  19. 9 9
      src/Renci.SshNet/PrivateKeyConnectionInfo.cs
  20. 59 7
      src/Renci.SshNet/PrivateKeyFile.cs
  21. 2 2
      src/Renci.SshNet/ScpClient.cs
  22. 3 4
      src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs
  23. 1 1
      src/Renci.SshNet/Security/Cryptography/DsaKey.cs
  24. 1 1
      src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
  25. 1 1
      src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
  26. 2 2
      src/Renci.SshNet/Security/Cryptography/Key.cs
  27. 13 4
      src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
  28. 6 2
      src/Renci.SshNet/Security/Cryptography/RsaKey.cs
  29. 22 0
      src/Renci.SshNet/Security/KeyExchange.cs
  30. 1 15
      src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
  31. 2 18
      src/Renci.SshNet/Security/KeyExchangeEC.cs
  32. 95 25
      src/Renci.SshNet/Security/KeyHostAlgorithm.cs
  33. 2 2
      src/Renci.SshNet/SftpClient.cs
  34. 2 2
      src/Renci.SshNet/SshClient.cs

+ 0 - 2
src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs

@@ -20,9 +20,7 @@ namespace Renci.SshNet.IntegrationTests.Common
                             .ClearCiphers()
                             .ClearCiphers()
                             .ClearKeyExchangeAlgorithms()
                             .ClearKeyExchangeAlgorithms()
                             .ClearHostKeyAlgorithms()
                             .ClearHostKeyAlgorithms()
-                            .AddHostKeyAlgorithm(HostKeyAlgorithm.SshRsa)
                             .ClearPublicKeyAcceptedAlgorithms()
                             .ClearPublicKeyAcceptedAlgorithms()
-                            .AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.SshRsa)
                             .WithUsePAM(true)
                             .WithUsePAM(true)
                             .Update()
                             .Update()
                             .Restart();
                             .Restart();

+ 0 - 1
src/Renci.SshNet.IntegrationTests/Dockerfile

@@ -14,7 +14,6 @@ RUN apk update && apk upgrade --no-cache && \
     chmod 400 /etc/ssh/ssh*key && \
     chmod 400 /etc/ssh/ssh*key && \
     sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \
     sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \
     sed -i 's/#LogLevel\s*INFO/LogLevel DEBUG3/' /etc/ssh/sshd_config && \
     sed -i 's/#LogLevel\s*INFO/LogLevel DEBUG3/' /etc/ssh/sshd_config && \
-    echo 'PubkeyAcceptedAlgorithms ssh-rsa' >> /etc/ssh/sshd_config && \
     chmod 646 /etc/ssh/sshd_config && \
     chmod 646 /etc/ssh/sshd_config && \
     # install and configure sudo
     # install and configure sudo
     apk add --no-cache sudo && \
     apk add --no-cache sudo && \

+ 25 - 47
src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs

@@ -22,63 +22,46 @@ namespace Renci.SshNet.IntegrationTests
         {
         {
             _remoteSshdConfig?.Reset();
             _remoteSshdConfig?.Reset();
         }
         }
-        
+
         [TestMethod]
         [TestMethod]
         [Ignore] // No longer supported in recent versions of OpenSSH
         [Ignore] // No longer supported in recent versions of OpenSSH
+        // TODO: We should be able to enable some legacy settings to make it work
+        // https://www.openssh.com/legacy.html e.g. PubkeyAcceptedKeyTypes / HostbasedAcceptedKeyTypes ?
         public void SshDsa()
         public void SshDsa()
         {
         {
-            _remoteSshdConfig.ClearHostKeyAlgorithms()
-                             .AddHostKeyAlgorithm(HostKeyAlgorithm.SshDsa)
-                             .ClearHostKeyFiles()
-                             .AddHostKeyFile(HostKeyFile.Dsa.FilePath)
-                             .Update()
-                             .Restart();
-
-            HostKeyEventArgs hostKeyEventsArgs = null;
-
-            using (var client = new SshClient(_connectionInfoFactory.Create()))
-            {
-                client.HostKeyReceived += (sender, e) => hostKeyEventsArgs = e;
-                client.Connect();
-                client.Disconnect();
-            }
-
-            Assert.IsNotNull(hostKeyEventsArgs);
-            Assert.AreEqual(HostKeyFile.Dsa.KeyName, hostKeyEventsArgs.HostKeyName);
-            Assert.AreEqual(1024, hostKeyEventsArgs.KeyLength);
-            Assert.IsTrue(hostKeyEventsArgs.FingerPrint.SequenceEqual(HostKeyFile.Dsa.FingerPrint));
+            DoTest(HostKeyAlgorithm.SshDsa, HostKeyFile.Dsa, 1024);
         }
         }
 
 
         [TestMethod]
         [TestMethod]
         public void SshRsa()
         public void SshRsa()
         {
         {
-            _remoteSshdConfig.ClearHostKeyAlgorithms()
-                             .AddHostKeyAlgorithm(HostKeyAlgorithm.SshRsa)
-                             .Update()
-                             .Restart();
-
-            HostKeyEventArgs hostKeyEventsArgs = null;
+            DoTest(HostKeyAlgorithm.SshRsa, HostKeyFile.Rsa, 3072);
+        }
 
 
-            using (var client = new SshClient(_connectionInfoFactory.Create()))
-            {
-                client.HostKeyReceived += (sender, e) => hostKeyEventsArgs = e;
-                client.Connect();
-                client.Disconnect();
-            }
+        [TestMethod]
+        public void SshRsaSha256()
+        {
+            DoTest(HostKeyAlgorithm.RsaSha2256, HostKeyFile.Rsa, 3072);
+        }
 
 
-            Assert.IsNotNull(hostKeyEventsArgs);
-            Assert.AreEqual(HostKeyFile.Rsa.KeyName, hostKeyEventsArgs.HostKeyName);
-            Assert.AreEqual(3072, hostKeyEventsArgs.KeyLength);
-            Assert.IsTrue(hostKeyEventsArgs.FingerPrint.SequenceEqual(HostKeyFile.Rsa.FingerPrint));
+        [TestMethod]
+        public void SshRsaSha512()
+        {
+            DoTest(HostKeyAlgorithm.RsaSha2512, HostKeyFile.Rsa, 3072);
         }
         }
 
 
         [TestMethod]
         [TestMethod]
         public void SshEd25519()
         public void SshEd25519()
+        {
+            DoTest(HostKeyAlgorithm.SshEd25519, HostKeyFile.Ed25519, 256);
+        }
+
+        private void DoTest(HostKeyAlgorithm hostKeyAlgorithm, HostKeyFile hostKeyFile, int keyLength)
         {
         {
             _remoteSshdConfig.ClearHostKeyAlgorithms()
             _remoteSshdConfig.ClearHostKeyAlgorithms()
-                             .AddHostKeyAlgorithm(HostKeyAlgorithm.SshEd25519)
+                             .AddHostKeyAlgorithm(hostKeyAlgorithm)
                              .ClearHostKeyFiles()
                              .ClearHostKeyFiles()
-                             .AddHostKeyFile(HostKeyFile.Ed25519.FilePath)
+                             .AddHostKeyFile(hostKeyFile.FilePath)
                              .Update()
                              .Update()
                              .Restart();
                              .Restart();
 
 
@@ -92,14 +75,9 @@ namespace Renci.SshNet.IntegrationTests
             }
             }
 
 
             Assert.IsNotNull(hostKeyEventsArgs);
             Assert.IsNotNull(hostKeyEventsArgs);
-            Assert.AreEqual(HostKeyFile.Ed25519.KeyName, hostKeyEventsArgs.HostKeyName);
-            Assert.AreEqual(256, hostKeyEventsArgs.KeyLength);
-            Assert.IsTrue(hostKeyEventsArgs.FingerPrint.SequenceEqual(HostKeyFile.Ed25519.FingerPrint));
-        }
-
-        private void Client_HostKeyReceived(object sender, HostKeyEventArgs e)
-        {
-            throw new NotImplementedException();
+            Assert.AreEqual(hostKeyAlgorithm.Name, hostKeyEventsArgs.HostKeyName);
+            Assert.AreEqual(keyLength, hostKeyEventsArgs.KeyLength);
+            CollectionAssert.AreEqual(hostKeyFile.FingerPrint, hostKeyEventsArgs.FingerPrint);
         }
         }
     }
     }
 }
 }

+ 43 - 22
src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs

@@ -5,7 +5,7 @@ namespace Renci.SshNet.IntegrationTests
 {
 {
     [TestClass]
     [TestClass]
     public class PrivateKeyAuthenticationTests : TestBase
     public class PrivateKeyAuthenticationTests : TestBase
-        {
+    {
         private IConnectionInfoFactory _connectionInfoFactory;
         private IConnectionInfoFactory _connectionInfoFactory;
         private RemoteSshdConfig _remoteSshdConfig;
         private RemoteSshdConfig _remoteSshdConfig;
 
 
@@ -23,43 +23,64 @@ namespace Renci.SshNet.IntegrationTests
         }
         }
 
 
         [TestMethod]
         [TestMethod]
-        public void Ecdsa256()
+        [Ignore] // No longer supported in recent versions of OpenSSH
+        // TODO: We should be able to enable some legacy settings to make it work
+        // https://www.openssh.com/legacy.html e.g. PubkeyAcceptedKeyTypes / HostbasedAcceptedKeyTypes ?
+        public void SshDsa()
         {
         {
-            _remoteSshdConfig.AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.EcdsaSha2Nistp256)
-                             .Update()
-                             .Restart();
+            DoTest(PublicKeyAlgorithm.SshDss, "id_dsa");
+        }
 
 
-            var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod("key_ecdsa_256_openssh"));
+        [TestMethod]
+        public void SshRsa()
+        {
+            DoTest(PublicKeyAlgorithm.SshRsa, "id_rsa");
+        }
 
 
-            using (var client = new SshClient(connectionInfo))
-            {
-                client.Connect();
-            }
+        [TestMethod]
+        public void SshRsaSha256()
+        {
+            DoTest(PublicKeyAlgorithm.RsaSha2256, "id_rsa");
         }
         }
 
 
         [TestMethod]
         [TestMethod]
-        public void Ecdsa384()
+        public void SshRsaSha512()
         {
         {
-            _remoteSshdConfig.AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.EcdsaSha2Nistp384)
-                             .Update()
-                             .Restart();
+            DoTest(PublicKeyAlgorithm.RsaSha2512, "id_rsa");
+        }
 
 
-            var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod("key_ecdsa_384_openssh"));
+        [TestMethod]
+        public void Ecdsa256()
+        {
+            DoTest(PublicKeyAlgorithm.EcdsaSha2Nistp256, "key_ecdsa_256_openssh");
+        }
 
 
-            using (var client = new SshClient(connectionInfo))
-            {
-                client.Connect();
-            }
+        [TestMethod]
+        public void Ecdsa384()
+        {
+            DoTest(PublicKeyAlgorithm.EcdsaSha2Nistp384, "key_ecdsa_384_openssh");
         }
         }
 
 
         [TestMethod]
         [TestMethod]
-        public void EcdsaA521()
+        public void Ecdsa521()
+        {
+            DoTest(PublicKeyAlgorithm.EcdsaSha2Nistp521, "key_ecdsa_521_openssh");
+        }
+
+        [TestMethod]
+        public void Ed25519()
+        {
+            DoTest(PublicKeyAlgorithm.SshEd25519, "key_ed25519_openssh");
+        }
+
+        private void DoTest(PublicKeyAlgorithm publicKeyAlgorithm, string keyResource)
         {
         {
-            _remoteSshdConfig.AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.EcdsaSha2Nistp521)
+            _remoteSshdConfig.ClearPublicKeyAcceptedAlgorithms()
+                             .AddPublicKeyAcceptedAlgorithms(publicKeyAlgorithm)
                              .Update()
                              .Update()
                              .Restart();
                              .Restart();
 
 
-            var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod("key_ecdsa_521_openssh"));
+            var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod(keyResource));
 
 
             using (var client = new SshClient(connectionInfo))
             using (var client = new SshClient(connectionInfo))
             {
             {

+ 1 - 0
src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj

@@ -60,6 +60,7 @@
     <EmbeddedResource Include="resources\client\key_ecdsa_256_openssh" />
     <EmbeddedResource Include="resources\client\key_ecdsa_256_openssh" />
     <EmbeddedResource Include="resources\client\key_ecdsa_384_openssh" />
     <EmbeddedResource Include="resources\client\key_ecdsa_384_openssh" />
     <EmbeddedResource Include="resources\client\key_ecdsa_521_openssh" />
     <EmbeddedResource Include="resources\client\key_ecdsa_521_openssh" />
+    <EmbeddedResource Include="resources\client\key_ed25519_openssh" />
     <EmbeddedResource Include="resources\issue #70.png" />
     <EmbeddedResource Include="resources\issue #70.png" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 2 - 1
src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys

@@ -1,4 +1,5 @@
 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6VopnwPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5mO2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LEfXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQtL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAj
 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6VopnwPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5mO2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LEfXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQtL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAj
 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw=
 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw=
 ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ==
 ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ==
-ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ==
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ==
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAkNGPVOTuzuKTgGfHcve2MRj57yXhmZgkUyi9RpmJrl

+ 5 - 2
src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs

@@ -211,8 +211,11 @@ namespace Renci.SshNet.TestTools.OpenSSH
                 writer.WriteLine("MACs " + string.Join(",", MessageAuthenticationCodeAlgorithms.Select(c => c.Name).ToArray()));
                 writer.WriteLine("MACs " + string.Join(",", MessageAuthenticationCodeAlgorithms.Select(c => c.Name).ToArray()));
             }
             }
 
 
-            writer.WriteLine("PubkeyAcceptedAlgorithms " + string.Join(",", PublicKeyAcceptedAlgorithms.Select(c => c.Name).ToArray()));
-
+            if (PublicKeyAcceptedAlgorithms.Count > 0)
+            {
+                writer.WriteLine("PubkeyAcceptedAlgorithms " + string.Join(",", PublicKeyAcceptedAlgorithms.Select(c => c.Name).ToArray()));
+            }
+            
             foreach (var match in Matches)
             foreach (var match in Matches)
             {
             {
                 _matchFormatter.Format(match, writer);
                 _matchFormatter.Format(match, writer);

+ 1 - 1
src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs

@@ -42,7 +42,7 @@ namespace Renci.SshNet.Tests.Classes.Common
                 0xa0, 0x23, 0xaf, 0xff, 0x9c, 0x0f, 0x8c, 0x83, 0x7c, 0xf8, 0xe1, 0x8e, 0x32, 0x8e, 0x61, 0xfc,
                 0xa0, 0x23, 0xaf, 0xff, 0x9c, 0x0f, 0x8c, 0x83, 0x7c, 0xf8, 0xe1, 0x8e, 0x32, 0x8e, 0x61, 0xfc,
                 0x5b, 0xbd, 0xd4, 0x46, 0xe1
                 0x5b, 0xbd, 0xd4, 0x46, 0xe1
             }.SequenceEqual(target.HostKey));
             }.SequenceEqual(target.HostKey));
-            Assert.AreEqual("ssh-rsa", target.HostKeyName);
+            Assert.AreEqual("rsa-sha2-512", target.HostKeyName);
             Assert.AreEqual(2048, target.KeyLength);
             Assert.AreEqual(2048, target.KeyLength);
         }
         }
 
 

+ 26 - 10
src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs

@@ -1,8 +1,11 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Renci.SshNet.Common;
 using Renci.SshNet.Common;
+using Renci.SshNet.Security;
 using Renci.SshNet.Tests.Common;
 using Renci.SshNet.Tests.Common;
 using System;
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.IO;
+using System.Linq;
 
 
 namespace Renci.SshNet.Tests.Classes
 namespace Renci.SshNet.Tests.Classes
 {
 {
@@ -144,7 +147,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.RSA.txt"))
             using (var stream = GetData("Key.RSA.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream);
+                TestRsaKeyFile(new PrivateKeyFile(stream));
             }
             }
         }
         }
 
 
@@ -166,7 +169,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.SSH2.RSA.txt"))
             using (var stream = GetData("Key.SSH2.RSA.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream);
+                TestRsaKeyFile(new PrivateKeyFile(stream));
             }
             }
         }
         }
 
 
@@ -188,7 +191,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
             using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream, "12345");
+                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
             }
             }
         }
         }
 
 
@@ -262,7 +265,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.RSA.Encrypted.Des.CBC.12345.txt"))
             using (var stream = GetData("Key.RSA.Encrypted.Des.CBC.12345.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream, "12345");
+                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
             }
             }
         }
         }
 
 
@@ -284,7 +287,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
             using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream, "12345");
+                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
             }
             }
         }
         }
 
 
@@ -295,7 +298,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.RSA.Encrypted.Aes.192.CBC.12345.txt"))
             using (var stream = GetData("Key.RSA.Encrypted.Aes.192.CBC.12345.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream, "12345");
+                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
             }
             }
         }
         }
 
 
@@ -306,7 +309,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.RSA.Encrypted.Aes.256.CBC.12345.txt"))
             using (var stream = GetData("Key.RSA.Encrypted.Aes.256.CBC.12345.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream, "12345");
+                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
             }
             }
         }
         }
 
 
@@ -317,7 +320,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt"))
             using (var stream = GetData("Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream, "1234567890");
+                TestRsaKeyFile(new PrivateKeyFile(stream, "1234567890"));
             }
             }
         }
         }
 
 
@@ -576,7 +579,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.OPENSSH.RSA.txt"))
             using (var stream = GetData("Key.OPENSSH.RSA.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream);
+                TestRsaKeyFile(new PrivateKeyFile(stream));
             }
             }
         }
         }
 
 
@@ -587,7 +590,7 @@ namespace Renci.SshNet.Tests.Classes
         {
         {
             using (var stream = GetData("Key.OPENSSH.RSA.Encrypted.txt"))
             using (var stream = GetData("Key.OPENSSH.RSA.Encrypted.txt"))
             {
             {
-                _ = new PrivateKeyFile(stream, "12345");
+                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
             }
             }
         }
         }
 
 
@@ -678,5 +681,18 @@ namespace Renci.SshNet.Tests.Classes
             File.Delete(tempFile);
             File.Delete(tempFile);
             return tempFile;
             return tempFile;
         }
         }
+
+        private static void TestRsaKeyFile(PrivateKeyFile rsaPrivateKeyFile)
+        {
+            Assert.AreEqual(3, rsaPrivateKeyFile.HostAlgorithms.Count);
+
+            List<KeyHostAlgorithm> algorithms = rsaPrivateKeyFile.HostAlgorithms.Cast<KeyHostAlgorithm>().ToList();
+
+            Assert.AreEqual("rsa-sha2-512", algorithms[0].Name);
+            Assert.AreEqual("rsa-sha2-256", algorithms[1].Name);
+            Assert.AreEqual("ssh-rsa", algorithms[2].Name);
+
+            Assert.AreSame(algorithms[0], rsaPrivateKeyFile.HostKey);
+        }
     }
     }
 }
 }

+ 154 - 10
src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs

@@ -1,4 +1,9 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
 using Renci.SshNet.Security;
 using Renci.SshNet.Security;
 using Renci.SshNet.Security.Cryptography;
 using Renci.SshNet.Security.Cryptography;
 using Renci.SshNet.Tests.Common;
 using Renci.SshNet.Tests.Common;
@@ -11,17 +16,156 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
     [TestClass]
     [TestClass]
     public class RsaDigitalSignatureTest : TestBase
     public class RsaDigitalSignatureTest : TestBase
     {
     {
+        [TestMethod]
+        public void Sha1_SignAndVerify()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("hello world");
+
+            RsaKey rsaKey = GetRsaKey();
+
+            var digitalSignature = new RsaDigitalSignature(rsaKey); // Verify SHA-1 is the default
+
+            byte[] signedBytes = digitalSignature.Sign(data);
+
+            byte[] expectedSignedBytes = new byte[]
+            {
+                // echo -n 'hello world' | openssl dgst -sha1 -sign Key.RSA.txt -out test.signed
+                0x41, 0x50, 0x12, 0x14, 0xd3, 0x7c, 0xe0, 0x40, 0x50, 0x65, 0xfb, 0x33, 0xd9, 0x17, 0x89, 0xbf,
+                0xb2, 0x4b, 0x85, 0x15, 0xbf, 0x9e, 0x57, 0x3b, 0x01, 0x15, 0x2b, 0x99, 0xfa, 0x62, 0x9b, 0x2a,
+                0x05, 0xa0, 0x73, 0xc7, 0xb7, 0x5b, 0xd9, 0x01, 0xaa, 0x56, 0x73, 0x95, 0x13, 0x41, 0x33, 0x0d,
+                0x7f, 0x83, 0x8a, 0x60, 0x4d, 0x19, 0xdc, 0x9b, 0xba, 0x8e, 0x61, 0xed, 0xd0, 0x8a, 0x3e, 0x38,
+                0x71, 0xee, 0x34, 0xc3, 0x55, 0x0f, 0x55, 0x65, 0x89, 0xbb, 0x3e, 0x41, 0xee, 0xdf, 0xf5, 0x2f,
+                0xab, 0x9e, 0x89, 0x37, 0x68, 0x1f, 0x9f, 0x38, 0x00, 0x81, 0x29, 0x93, 0xeb, 0x61, 0x37, 0xad,
+                0x8d, 0x35, 0xf1, 0x3d, 0x4b, 0x9b, 0x99, 0x74, 0x7b, 0xeb, 0xf4, 0xfb, 0x76, 0xb4, 0xb6, 0xb4,
+                0x09, 0x33, 0x5c, 0xfa, 0x6a, 0xad, 0x1e, 0xed, 0x1c, 0xe1, 0xb4, 0x4d, 0xf2, 0xa5, 0xc3, 0x64,
+                0x9a, 0x45, 0x81, 0xee, 0x1b, 0xa6, 0x1d, 0x01, 0x3c, 0x4d, 0xb5, 0x62, 0x9e, 0xff, 0x8e, 0xff,
+                0x6c, 0x18, 0xed, 0xe9, 0x8e, 0x03, 0x2c, 0xc5, 0x94, 0x81, 0xca, 0x8b, 0x18, 0x3f, 0x25, 0xcd,
+                0xe5, 0x42, 0x49, 0x43, 0x23, 0x1f, 0xdc, 0x3f, 0xa2, 0x43, 0xbc, 0xbd, 0x42, 0xf5, 0x60, 0xfb,
+                0x01, 0xd3, 0x67, 0x0d, 0x8d, 0x85, 0x7b, 0x51, 0x14, 0xec, 0x26, 0x53, 0x00, 0x61, 0x25, 0x16,
+                0x19, 0x10, 0x3c, 0x86, 0x16, 0x59, 0x84, 0x08, 0xd1, 0xf9, 0x1e, 0x05, 0x88, 0xbd, 0x4a, 0x01,
+                0x43, 0x4e, 0xec, 0x76, 0x0b, 0xd7, 0x2c, 0xe9, 0x98, 0xb1, 0x4c, 0x0a, 0x13, 0xc6, 0x95, 0xf9,
+                0x8f, 0x95, 0x5c, 0x98, 0x4c, 0x8f, 0x97, 0x4a, 0xad, 0x0d, 0xfe, 0x84, 0xf0, 0x56, 0xc3, 0x29,
+                0x73, 0x75, 0x55, 0x3c, 0xd9, 0x5e, 0x5b, 0x6f, 0xf9, 0x81, 0xbc, 0xbc, 0x50, 0x75, 0x7d, 0xa8
+            };
+
+            CollectionAssert.AreEqual(expectedSignedBytes, signedBytes);
+
+            // Also verify RsaKey uses SHA-1 by default
+            CollectionAssert.AreEqual(expectedSignedBytes, rsaKey.Sign(data));
+
+            // The following fails due to the _isPrivate decision in RsaCipher.Transform. Is that really correct?
+            //Assert.IsTrue(digitalSignature.Verify(data, signedBytes));
+
+            // 'Workaround': use a key with no private key information
+            var digitalSignaturePublic = new RsaDigitalSignature(new RsaKey()
+            {
+                Public = rsaKey.Public
+            });
+            Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes));
+        }
 
 
-        /// <summary>
-        ///A test for RsaDigitalSignature Constructor
-        ///</summary>
         [TestMethod]
         [TestMethod]
-        [Ignore] // placeholder for actual test
-        public void RsaDigitalSignatureConstructorTest()
+        public void Sha256_SignAndVerify()
         {
         {
-            RsaKey rsaKey = null; // TODO: Initialize to an appropriate value
-            RsaDigitalSignature target = new RsaDigitalSignature(rsaKey);
-            Assert.Inconclusive("TODO: Implement code to verify target");
+            byte[] data = Encoding.UTF8.GetBytes("hello world");
+
+            RsaKey rsaKey = GetRsaKey();
+
+            var digitalSignature = new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256);
+
+            byte[] signedBytes = digitalSignature.Sign(data);
+
+            CollectionAssert.AreEqual(new byte[]
+            {
+                // echo -n 'hello world' | openssl dgst -sha256 -sign Key.RSA.txt -out test.signed
+                0x2e, 0xef, 0x01, 0x49, 0x5c, 0x66, 0x37, 0x56, 0xc2, 0xfb, 0x7b, 0xfa, 0x80, 0x2f, 0xdb, 0xaa,
+                0x0d, 0x15, 0xd9, 0x8d, 0xa9, 0xad, 0x81, 0x4f, 0x09, 0x2e, 0x53, 0x9e, 0xce, 0x5d, 0x68, 0x07,
+                0xae, 0xb9, 0xc0, 0x45, 0xfa, 0x30, 0xd0, 0xf7, 0xd6, 0xa6, 0x8d, 0x19, 0x24, 0x3a, 0xea, 0x91,
+                0x3e, 0xa2, 0x4a, 0x42, 0x2e, 0x21, 0xf1, 0x48, 0x57, 0xca, 0x2b, 0x6c, 0x9f, 0x79, 0x54, 0x91,
+                0x3e, 0x3a, 0x4d, 0xd1, 0x70, 0x87, 0x3d, 0xbe, 0x22, 0x97, 0xc9, 0xb0, 0x02, 0xf0, 0xa2, 0xae,
+                0x7a, 0xbb, 0x8b, 0xaf, 0xc0, 0x3b, 0xab, 0x71, 0xe8, 0x29, 0x1c, 0x18, 0x88, 0xca, 0x74, 0x1b,
+                0x34, 0x4f, 0xd1, 0x83, 0x39, 0x6e, 0x8f, 0x69, 0x3d, 0x7e, 0xef, 0xef, 0x57, 0x7c, 0xff, 0x21,
+                0x9c, 0x10, 0x2b, 0xd1, 0x4f, 0x26, 0xbe, 0xaa, 0xd2, 0xd9, 0x03, 0x14, 0x75, 0x97, 0x11, 0xaf,
+                0xf0, 0x28, 0xf2, 0xd3, 0x07, 0x79, 0x5b, 0x27, 0xdc, 0x97, 0xd8, 0xce, 0x4e, 0x78, 0x89, 0x16,
+                0x91, 0x2a, 0xb2, 0x47, 0x53, 0x94, 0xe9, 0xa1, 0x15, 0x98, 0x29, 0x0c, 0xa1, 0xf5, 0xe2, 0x8e,
+                0x11, 0xdc, 0x0c, 0x1c, 0x10, 0xa4, 0xf2, 0x46, 0x5c, 0x78, 0x0c, 0xc1, 0x4a, 0x65, 0x21, 0x8a,
+                0x2e, 0x32, 0x6c, 0x72, 0x06, 0xf9, 0x7f, 0xa1, 0x6c, 0x2e, 0x13, 0x06, 0x41, 0xaa, 0x23, 0xdd,
+                0xc8, 0x1c, 0x61, 0xb6, 0x96, 0x87, 0xc4, 0x84, 0xc8, 0x61, 0xec, 0x4e, 0xdd, 0x49, 0x9e, 0x4f,
+                0x0d, 0x8c, 0xf1, 0x7f, 0xf2, 0x6c, 0x73, 0x5a, 0xa6, 0x3b, 0xbf, 0x4e, 0xba, 0x57, 0x6b, 0xb3,
+                0x1e, 0x6c, 0x57, 0x76, 0x87, 0x9f, 0xb4, 0x3b, 0xcb, 0xcd, 0xe5, 0x10, 0x7a, 0x4c, 0xeb, 0xc0,
+                0xc4, 0xc3, 0x75, 0x51, 0x5f, 0xb7, 0x7c, 0xbc, 0x55, 0x8d, 0x05, 0xc7, 0xed, 0xc7, 0x52, 0x4a
+            }, signedBytes);
+
+
+            // The following fails due to the _isPrivate decision in RsaCipher.Transform. Is that really correct?
+            //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);
+            Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes));
+        }
+
+        [TestMethod]
+        public void Sha512_SignAndVerify()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("hello world");
+
+            RsaKey rsaKey = GetRsaKey();
+
+            var digitalSignature = new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA512);
+
+            byte[] signedBytes = digitalSignature.Sign(data);
+
+            CollectionAssert.AreEqual(new byte[]
+            {
+                // echo -n 'hello world' | openssl dgst -sha512 -sign Key.RSA.txt -out test.signed
+                0x69, 0x70, 0xb5, 0x9f, 0x32, 0x86, 0x3b, 0xae, 0xc0, 0x79, 0x6e, 0xdb, 0x35, 0xd5, 0xa6, 0x22,
+                0xcd, 0x2b, 0x4b, 0xd2, 0x68, 0x1a, 0x65, 0x41, 0xa6, 0xd9, 0x20, 0x54, 0x31, 0x9a, 0xb1, 0x44,
+                0x6e, 0x8f, 0x56, 0x4b, 0xfc, 0x27, 0x7f, 0x3f, 0xe7, 0x47, 0xcb, 0x78, 0x03, 0x05, 0x79, 0x8a,
+                0x16, 0x7b, 0x12, 0x01, 0x3a, 0xa2, 0xd5, 0x0d, 0x2b, 0x16, 0x38, 0xef, 0x84, 0x6b, 0xd7, 0x19,
+                0xeb, 0xac, 0x54, 0x01, 0x9d, 0xa6, 0x80, 0x74, 0x43, 0xa8, 0x6e, 0x5e, 0x33, 0x05, 0x06, 0x1d,
+                0x6d, 0xfe, 0x32, 0x4f, 0xe3, 0xcb, 0x3e, 0x2d, 0x4e, 0xe1, 0x47, 0x03, 0x69, 0xb4, 0x59, 0x80,
+                0x59, 0x05, 0x15, 0xa0, 0x11, 0x34, 0x47, 0x58, 0xd7, 0x93, 0x2d, 0x40, 0xf2, 0x2c, 0x37, 0x48,
+                0x6b, 0x3c, 0xd3, 0x03, 0x09, 0x32, 0x74, 0xa0, 0x2d, 0x33, 0x11, 0x99, 0x10, 0xb4, 0x09, 0x31,
+                0xec, 0xa3, 0x2c, 0x63, 0xba, 0x50, 0xd1, 0x02, 0x45, 0xae, 0xb5, 0x75, 0x7e, 0xfa, 0xfc, 0x06,
+                0xb6, 0x6a, 0xb2, 0xa1, 0x73, 0x14, 0xa5, 0xaa, 0x17, 0x88, 0x03, 0x19, 0x14, 0x9b, 0xe1, 0x10,
+                0xf8, 0x2f, 0x73, 0x01, 0xc7, 0x8d, 0x37, 0xef, 0x98, 0x69, 0xc2, 0xe2, 0x7a, 0x11, 0xd5, 0xb8,
+                0xc9, 0x35, 0x45, 0xcb, 0x56, 0x4b, 0x92, 0x4a, 0xe0, 0x4c, 0xd6, 0x82, 0xae, 0xad, 0x5b, 0xe9,
+                0x40, 0x7e, 0x2a, 0x48, 0x7d, 0x57, 0xc5, 0xfd, 0xe9, 0x98, 0xe0, 0xbb, 0x09, 0xa1, 0xf5, 0x48,
+                0x45, 0xcb, 0xee, 0xb9, 0x99, 0x81, 0x44, 0x15, 0x2e, 0x50, 0x39, 0x64, 0x58, 0x4c, 0x34, 0x86,
+                0xf8, 0x81, 0x9e, 0x1d, 0xb6, 0x97, 0xe0, 0xce, 0x16, 0xca, 0x20, 0x46, 0xe9, 0x49, 0x8f, 0xe6,
+                0xa0, 0x23, 0x08, 0x80, 0xa6, 0x37, 0x70, 0x06, 0xcc, 0x8f, 0xf4, 0xa0, 0x74, 0x53, 0x26, 0x38
+            }, signedBytes);
+
+            // The following fails due to the _isPrivate decision in RsaCipher.Transform. Is that really correct?
+            //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);
+            Assert.IsTrue(digitalSignaturePublic.Verify(data, signedBytes));
+        }
+
+        [TestMethod]
+        public void Constructor_InvalidHashAlgorithm_ThrowsArgumentException()
+        {
+            ArgumentException exception = Assert.ThrowsException<ArgumentException>(
+                () => new RsaDigitalSignature(new RsaKey(), new HashAlgorithmName("invalid")));
+
+            Assert.AreEqual("hashAlgorithmName", exception.ParamName);
+        }
+
+        private static RsaKey GetRsaKey()
+        {
+            using (var stream = GetData("Key.RSA.txt"))
+            {
+                return (RsaKey) ((KeyHostAlgorithm) new PrivateKeyFile(stream).HostKey).Key;
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -37,4 +181,4 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
             Assert.Inconclusive("A method that does not return a value cannot be verified.");
             Assert.Inconclusive("A method that does not return a value cannot be verified.");
         }
         }
     }
     }
-}
+}

+ 215 - 0
src/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs

@@ -0,0 +1,215 @@
+using System.Security.Cryptography;
+using System.Text;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Renci.SshNet.Security;
+using Renci.SshNet.Security.Cryptography;
+using Renci.SshNet.Tests.Common;
+
+namespace Renci.SshNet.Tests.Classes.Security
+{
+    [TestClass]
+    public class KeyHostAlgorithmTest : TestBase
+    {
+        [TestMethod]
+        public void NoSuppliedDigitalSignature_PropertyIsKeyDigitalSignature()
+        {
+            RsaKey rsaKey = GetRsaKey();
+
+            KeyHostAlgorithm keyHostAlgorithm = new KeyHostAlgorithm("ssh-rsa", rsaKey);
+
+            Assert.AreEqual("ssh-rsa", keyHostAlgorithm.Name);
+            Assert.AreSame(rsaKey, keyHostAlgorithm.Key);
+            Assert.AreSame(rsaKey.DigitalSignature, keyHostAlgorithm.DigitalSignature);
+        }
+
+        [TestMethod]
+        public void SuppliedDigitalSignature_PropertyIsSuppliedDigitalSignature()
+        {
+            RsaKey rsaKey = GetRsaKey();
+            RsaDigitalSignature rsaDigitalSignature = new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256);
+            KeyHostAlgorithm keyHostAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", rsaKey, rsaDigitalSignature);
+
+            Assert.AreEqual("rsa-sha2-256", keyHostAlgorithm.Name);
+            Assert.AreSame(rsaKey, keyHostAlgorithm.Key);
+            Assert.AreSame(rsaDigitalSignature, keyHostAlgorithm.DigitalSignature);
+        }
+
+        [TestMethod]
+        public void RsaPublicKeyDataDoesNotDependOnSignatureAlgorithm()
+        {
+            TestRsaPublicKeyData("ssh-rsa", HashAlgorithmName.SHA1);
+            TestRsaPublicKeyData("rsa-sha2-256", HashAlgorithmName.SHA256);
+        }
+
+        private void TestRsaPublicKeyData(string signatureIdentifier, HashAlgorithmName hashAlgorithmName)
+        {
+            RsaKey key = GetRsaKey();
+            KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm(signatureIdentifier, key, new RsaDigitalSignature(key, hashAlgorithmName));
+
+            CollectionAssert.AreEqual(GetRsaPublicKeyBytes(), keyAlgorithm.Data);
+        }
+
+        [TestMethod]
+        public void SshRsa_SignAndVerify()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("hello world");
+
+            RsaKey key = GetRsaKey();
+            KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm("ssh-rsa", key);
+
+            byte[] expectedEncodedSignatureBytes = new byte[]
+            {
+                0, 0, 0, 7, // byte count of "ssh-rsa"
+                (byte)'s', (byte)'s', (byte)'h', (byte)'-', (byte)'r', (byte)'s', (byte)'a', // ssh-rsa
+                0, 0, 1, 0, // byte count of signature (=256)
+                
+                // echo -n 'hello world' | openssl dgst -sha1 -sign Key.RSA.txt -out test.signed
+                0x41, 0x50, 0x12, 0x14, 0xd3, 0x7c, 0xe0, 0x40, 0x50, 0x65, 0xfb, 0x33, 0xd9, 0x17, 0x89, 0xbf,
+                0xb2, 0x4b, 0x85, 0x15, 0xbf, 0x9e, 0x57, 0x3b, 0x01, 0x15, 0x2b, 0x99, 0xfa, 0x62, 0x9b, 0x2a,
+                0x05, 0xa0, 0x73, 0xc7, 0xb7, 0x5b, 0xd9, 0x01, 0xaa, 0x56, 0x73, 0x95, 0x13, 0x41, 0x33, 0x0d,
+                0x7f, 0x83, 0x8a, 0x60, 0x4d, 0x19, 0xdc, 0x9b, 0xba, 0x8e, 0x61, 0xed, 0xd0, 0x8a, 0x3e, 0x38,
+                0x71, 0xee, 0x34, 0xc3, 0x55, 0x0f, 0x55, 0x65, 0x89, 0xbb, 0x3e, 0x41, 0xee, 0xdf, 0xf5, 0x2f,
+                0xab, 0x9e, 0x89, 0x37, 0x68, 0x1f, 0x9f, 0x38, 0x00, 0x81, 0x29, 0x93, 0xeb, 0x61, 0x37, 0xad,
+                0x8d, 0x35, 0xf1, 0x3d, 0x4b, 0x9b, 0x99, 0x74, 0x7b, 0xeb, 0xf4, 0xfb, 0x76, 0xb4, 0xb6, 0xb4,
+                0x09, 0x33, 0x5c, 0xfa, 0x6a, 0xad, 0x1e, 0xed, 0x1c, 0xe1, 0xb4, 0x4d, 0xf2, 0xa5, 0xc3, 0x64,
+                0x9a, 0x45, 0x81, 0xee, 0x1b, 0xa6, 0x1d, 0x01, 0x3c, 0x4d, 0xb5, 0x62, 0x9e, 0xff, 0x8e, 0xff,
+                0x6c, 0x18, 0xed, 0xe9, 0x8e, 0x03, 0x2c, 0xc5, 0x94, 0x81, 0xca, 0x8b, 0x18, 0x3f, 0x25, 0xcd,
+                0xe5, 0x42, 0x49, 0x43, 0x23, 0x1f, 0xdc, 0x3f, 0xa2, 0x43, 0xbc, 0xbd, 0x42, 0xf5, 0x60, 0xfb,
+                0x01, 0xd3, 0x67, 0x0d, 0x8d, 0x85, 0x7b, 0x51, 0x14, 0xec, 0x26, 0x53, 0x00, 0x61, 0x25, 0x16,
+                0x19, 0x10, 0x3c, 0x86, 0x16, 0x59, 0x84, 0x08, 0xd1, 0xf9, 0x1e, 0x05, 0x88, 0xbd, 0x4a, 0x01,
+                0x43, 0x4e, 0xec, 0x76, 0x0b, 0xd7, 0x2c, 0xe9, 0x98, 0xb1, 0x4c, 0x0a, 0x13, 0xc6, 0x95, 0xf9,
+                0x8f, 0x95, 0x5c, 0x98, 0x4c, 0x8f, 0x97, 0x4a, 0xad, 0x0d, 0xfe, 0x84, 0xf0, 0x56, 0xc3, 0x29,
+                0x73, 0x75, 0x55, 0x3c, 0xd9, 0x5e, 0x5b, 0x6f, 0xf9, 0x81, 0xbc, 0xbc, 0x50, 0x75, 0x7d, 0xa8
+            };
+
+            CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data));
+
+            keyAlgorithm = new KeyHostAlgorithm("ssh-rsa", new RsaKey(), GetRsaPublicKeyBytes());
+            Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes));
+        }
+
+        [TestMethod]
+        public void RsaSha256_SignAndVerify()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("hello world");
+
+            RsaKey key = GetRsaKey();
+            KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA256));
+
+            byte[] expectedEncodedSignatureBytes = new byte[]
+            {
+                0, 0, 0, 12, // byte count of "rsa-sha2-256"
+                (byte)'r', (byte)'s', (byte)'a', (byte)'-', (byte)'s', (byte)'h', (byte)'a', (byte)'2',
+                (byte)'-', (byte)'2', (byte)'5', (byte)'6',
+                0, 0, 1, 0, // byte count of signature (=256)
+                
+                // echo -n 'hello world' | openssl dgst -sha256 -sign Key.RSA.txt -out test.signed
+                0x2e, 0xef, 0x01, 0x49, 0x5c, 0x66, 0x37, 0x56, 0xc2, 0xfb, 0x7b, 0xfa, 0x80, 0x2f, 0xdb, 0xaa,
+                0x0d, 0x15, 0xd9, 0x8d, 0xa9, 0xad, 0x81, 0x4f, 0x09, 0x2e, 0x53, 0x9e, 0xce, 0x5d, 0x68, 0x07,
+                0xae, 0xb9, 0xc0, 0x45, 0xfa, 0x30, 0xd0, 0xf7, 0xd6, 0xa6, 0x8d, 0x19, 0x24, 0x3a, 0xea, 0x91,
+                0x3e, 0xa2, 0x4a, 0x42, 0x2e, 0x21, 0xf1, 0x48, 0x57, 0xca, 0x2b, 0x6c, 0x9f, 0x79, 0x54, 0x91,
+                0x3e, 0x3a, 0x4d, 0xd1, 0x70, 0x87, 0x3d, 0xbe, 0x22, 0x97, 0xc9, 0xb0, 0x02, 0xf0, 0xa2, 0xae,
+                0x7a, 0xbb, 0x8b, 0xaf, 0xc0, 0x3b, 0xab, 0x71, 0xe8, 0x29, 0x1c, 0x18, 0x88, 0xca, 0x74, 0x1b,
+                0x34, 0x4f, 0xd1, 0x83, 0x39, 0x6e, 0x8f, 0x69, 0x3d, 0x7e, 0xef, 0xef, 0x57, 0x7c, 0xff, 0x21,
+                0x9c, 0x10, 0x2b, 0xd1, 0x4f, 0x26, 0xbe, 0xaa, 0xd2, 0xd9, 0x03, 0x14, 0x75, 0x97, 0x11, 0xaf,
+                0xf0, 0x28, 0xf2, 0xd3, 0x07, 0x79, 0x5b, 0x27, 0xdc, 0x97, 0xd8, 0xce, 0x4e, 0x78, 0x89, 0x16,
+                0x91, 0x2a, 0xb2, 0x47, 0x53, 0x94, 0xe9, 0xa1, 0x15, 0x98, 0x29, 0x0c, 0xa1, 0xf5, 0xe2, 0x8e,
+                0x11, 0xdc, 0x0c, 0x1c, 0x10, 0xa4, 0xf2, 0x46, 0x5c, 0x78, 0x0c, 0xc1, 0x4a, 0x65, 0x21, 0x8a,
+                0x2e, 0x32, 0x6c, 0x72, 0x06, 0xf9, 0x7f, 0xa1, 0x6c, 0x2e, 0x13, 0x06, 0x41, 0xaa, 0x23, 0xdd,
+                0xc8, 0x1c, 0x61, 0xb6, 0x96, 0x87, 0xc4, 0x84, 0xc8, 0x61, 0xec, 0x4e, 0xdd, 0x49, 0x9e, 0x4f,
+                0x0d, 0x8c, 0xf1, 0x7f, 0xf2, 0x6c, 0x73, 0x5a, 0xa6, 0x3b, 0xbf, 0x4e, 0xba, 0x57, 0x6b, 0xb3,
+                0x1e, 0x6c, 0x57, 0x76, 0x87, 0x9f, 0xb4, 0x3b, 0xcb, 0xcd, 0xe5, 0x10, 0x7a, 0x4c, 0xeb, 0xc0,
+                0xc4, 0xc3, 0x75, 0x51, 0x5f, 0xb7, 0x7c, 0xbc, 0x55, 0x8d, 0x05, 0xc7, 0xed, 0xc7, 0x52, 0x4a
+            };
+
+            CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data));
+
+            key = new RsaKey();
+            keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-256", key, GetRsaPublicKeyBytes(), new RsaDigitalSignature(key, HashAlgorithmName.SHA256));
+            Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes));
+        }
+
+        [TestMethod]
+        public void RsaSha512_SignAndVerify()
+        {
+            byte[] data = Encoding.UTF8.GetBytes("hello world");
+
+            RsaKey key = GetRsaKey();
+            KeyHostAlgorithm keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-512", key, new RsaDigitalSignature(key, HashAlgorithmName.SHA512));
+
+            byte[] expectedEncodedSignatureBytes = new byte[]
+            {
+                0, 0, 0, 12, // byte count of "rsa-sha2-512"
+                (byte)'r', (byte)'s', (byte)'a', (byte)'-', (byte)'s', (byte)'h', (byte)'a', (byte)'2',
+                (byte)'-', (byte)'5', (byte)'1', (byte)'2',
+                0, 0, 1, 0, // byte count of signature (=256)
+                
+                // echo -n 'hello world' | openssl dgst -sha512 -sign Key.RSA.txt -out test.signed
+                0x69, 0x70, 0xb5, 0x9f, 0x32, 0x86, 0x3b, 0xae, 0xc0, 0x79, 0x6e, 0xdb, 0x35, 0xd5, 0xa6, 0x22,
+                0xcd, 0x2b, 0x4b, 0xd2, 0x68, 0x1a, 0x65, 0x41, 0xa6, 0xd9, 0x20, 0x54, 0x31, 0x9a, 0xb1, 0x44,
+                0x6e, 0x8f, 0x56, 0x4b, 0xfc, 0x27, 0x7f, 0x3f, 0xe7, 0x47, 0xcb, 0x78, 0x03, 0x05, 0x79, 0x8a,
+                0x16, 0x7b, 0x12, 0x01, 0x3a, 0xa2, 0xd5, 0x0d, 0x2b, 0x16, 0x38, 0xef, 0x84, 0x6b, 0xd7, 0x19,
+                0xeb, 0xac, 0x54, 0x01, 0x9d, 0xa6, 0x80, 0x74, 0x43, 0xa8, 0x6e, 0x5e, 0x33, 0x05, 0x06, 0x1d,
+                0x6d, 0xfe, 0x32, 0x4f, 0xe3, 0xcb, 0x3e, 0x2d, 0x4e, 0xe1, 0x47, 0x03, 0x69, 0xb4, 0x59, 0x80,
+                0x59, 0x05, 0x15, 0xa0, 0x11, 0x34, 0x47, 0x58, 0xd7, 0x93, 0x2d, 0x40, 0xf2, 0x2c, 0x37, 0x48,
+                0x6b, 0x3c, 0xd3, 0x03, 0x09, 0x32, 0x74, 0xa0, 0x2d, 0x33, 0x11, 0x99, 0x10, 0xb4, 0x09, 0x31,
+                0xec, 0xa3, 0x2c, 0x63, 0xba, 0x50, 0xd1, 0x02, 0x45, 0xae, 0xb5, 0x75, 0x7e, 0xfa, 0xfc, 0x06,
+                0xb6, 0x6a, 0xb2, 0xa1, 0x73, 0x14, 0xa5, 0xaa, 0x17, 0x88, 0x03, 0x19, 0x14, 0x9b, 0xe1, 0x10,
+                0xf8, 0x2f, 0x73, 0x01, 0xc7, 0x8d, 0x37, 0xef, 0x98, 0x69, 0xc2, 0xe2, 0x7a, 0x11, 0xd5, 0xb8,
+                0xc9, 0x35, 0x45, 0xcb, 0x56, 0x4b, 0x92, 0x4a, 0xe0, 0x4c, 0xd6, 0x82, 0xae, 0xad, 0x5b, 0xe9,
+                0x40, 0x7e, 0x2a, 0x48, 0x7d, 0x57, 0xc5, 0xfd, 0xe9, 0x98, 0xe0, 0xbb, 0x09, 0xa1, 0xf5, 0x48,
+                0x45, 0xcb, 0xee, 0xb9, 0x99, 0x81, 0x44, 0x15, 0x2e, 0x50, 0x39, 0x64, 0x58, 0x4c, 0x34, 0x86,
+                0xf8, 0x81, 0x9e, 0x1d, 0xb6, 0x97, 0xe0, 0xce, 0x16, 0xca, 0x20, 0x46, 0xe9, 0x49, 0x8f, 0xe6,
+                0xa0, 0x23, 0x08, 0x80, 0xa6, 0x37, 0x70, 0x06, 0xcc, 0x8f, 0xf4, 0xa0, 0x74, 0x53, 0x26, 0x38
+            };
+
+            CollectionAssert.AreEqual(expectedEncodedSignatureBytes, keyAlgorithm.Sign(data));
+
+            key = new RsaKey();
+            keyAlgorithm = new KeyHostAlgorithm("rsa-sha2-512", key, GetRsaPublicKeyBytes(), new RsaDigitalSignature(key, HashAlgorithmName.SHA512));
+            Assert.IsTrue(keyAlgorithm.VerifySignature(data, expectedEncodedSignatureBytes));
+        }
+
+        private static RsaKey GetRsaKey()
+        {
+            using (var stream = GetData("Key.RSA.txt"))
+            {
+                return (RsaKey) ((KeyHostAlgorithm) new PrivateKeyFile(stream).HostKey).Key;
+            }
+        }
+
+        private static byte[] GetRsaPublicKeyBytes()
+        {
+            return new byte[]
+            {
+                0, 0, 0, 7, // byte count of "ssh-rsa"
+                (byte)'s', (byte)'s', (byte)'h', (byte)'-', (byte)'r', (byte)'s', (byte)'a', // ssh-rsa
+                0, 0, 0, 1, // byte count of exponent
+                35, // exponent
+                0, 0, 1, 1, // byte count of modulus (=257)
+
+                // openssl rsa -in Key.RSA.txt -text
+                0x00, 0xb9, 0x3b, 0x57, 0x9f, 0xe0, 0x5a, 0xb5, 0x7d, 0x68, 0x26, 0xeb, 0xe1, 0xa9, 0xf2,
+                0x59, 0xc3, 0x98, 0xdc, 0xfe, 0x97, 0x08, 0xc4, 0x95, 0x0f, 0x9a, 0xea, 0x05, 0x08, 0x7d,
+                0xfe, 0x6d, 0x77, 0xca, 0x04, 0x9f, 0xfd, 0xe2, 0x2c, 0x4d, 0x11, 0x3c, 0xd9, 0x05, 0xab,
+                0x32, 0xbd, 0x3f, 0xe8, 0xcd, 0xba, 0x00, 0x6c, 0x21, 0xb7, 0xa9, 0xc2, 0x4e, 0x63, 0x17,
+                0xf6, 0x04, 0x47, 0x93, 0x00, 0x85, 0xde, 0xd6, 0x32, 0xc0, 0xa1, 0x37, 0x75, 0x18, 0xa0,
+                0xb0, 0x32, 0xf6, 0x4e, 0xca, 0x39, 0xec, 0x3c, 0xdf, 0x79, 0xfe, 0x50, 0xa1, 0xc1, 0xf7,
+                0x67, 0x05, 0xb3, 0x33, 0xa5, 0x96, 0x13, 0x19, 0xfa, 0x14, 0xca, 0x55, 0xe6, 0x7b, 0xf9,
+                0xb3, 0x8e, 0x32, 0xee, 0xfc, 0x9d, 0x2a, 0x5e, 0x04, 0x79, 0x97, 0x29, 0x3d, 0x1c, 0x54,
+                0xfe, 0xc7, 0x96, 0x04, 0xb5, 0x19, 0x7c, 0x55, 0x21, 0xe2, 0x0e, 0x42, 0xca, 0x4d, 0x9d,
+                0xfb, 0x77, 0x08, 0x6c, 0xaa, 0x07, 0x2c, 0xf8, 0xf9, 0x1f, 0xbd, 0x83, 0x14, 0x2b, 0xe0,
+                0xbc, 0x7a, 0xf9, 0xdf, 0x13, 0x4b, 0x60, 0x5a, 0x02, 0x99, 0x93, 0x41, 0x1a, 0xb6, 0x5f,
+                0x3b, 0x9c, 0xb5, 0xb2, 0x55, 0x70, 0x78, 0x2f, 0x38, 0x52, 0x0e, 0xd1, 0x8a, 0x2c, 0x23,
+                0xc0, 0x3a, 0x0a, 0xd7, 0xed, 0xf6, 0x1f, 0xa6, 0x50, 0xf0, 0x27, 0x65, 0x8a, 0xd4, 0xde,
+                0xa7, 0x1b, 0x41, 0x67, 0xc5, 0x6d, 0x47, 0x84, 0x37, 0x92, 0x2b, 0xb7, 0xb6, 0x4d, 0xb0,
+                0x1a, 0xda, 0xf6, 0x50, 0x82, 0xf1, 0x57, 0x31, 0x69, 0xce, 0xe0, 0xef, 0xcd, 0x64, 0xaa,
+                0x78, 0x08, 0xea, 0x4e, 0x45, 0xec, 0xa5, 0x89, 0x68, 0x5d, 0xb4, 0xa0, 0x23, 0xaf, 0xff,
+                0x9c, 0x0f, 0x8c, 0x83, 0x7c, 0xf8, 0xe1, 0x8e, 0x32, 0x8e, 0x61, 0xfc, 0x5b, 0xbd, 0xd4,
+                0x46, 0xe1
+            };
+        }
+    }
+}

+ 2 - 2
src/Renci.SshNet.Tests/Common/TestBase.cs

@@ -49,9 +49,9 @@ namespace Renci.SshNet.Tests.Common
             }
             }
         }
         }
 
 
-        protected Stream GetData(string name)
+        protected static Stream GetData(string name)
         {
         {
             return ExecutingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", name));
             return ExecutingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", name));
         }
         }
     }
     }
-}
+}

+ 16 - 0
src/Renci.SshNet/Common/ObjectIdentifier.cs

@@ -1,4 +1,6 @@
 using System;
 using System;
+using System.Linq;
+using System.Security.Cryptography;
 
 
 namespace Renci.SshNet.Common
 namespace Renci.SshNet.Common
 {
 {
@@ -32,5 +34,19 @@ namespace Renci.SshNet.Common
 
 
             Identifiers = identifiers;
             Identifiers = identifiers;
         }
         }
+
+        internal static ObjectIdentifier FromHashAlgorithmName(HashAlgorithmName hashAlgorithmName)
+        {
+            var oid = CryptoConfig.MapNameToOID(hashAlgorithmName.Name);
+
+            if (oid is null)
+            {
+                throw new ArgumentException($"Could not map `{hashAlgorithmName}` to OID.", nameof(hashAlgorithmName));
+            }
+
+            var identifiers = oid.Split('.').Select(ulong.Parse).ToArray();
+
+            return new ObjectIdentifier(identifiers);
+        }
     }
     }
 }
 }

+ 4 - 0
src/Renci.SshNet/ConnectionInfo.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Net;
 using System.Net;
+using System.Security.Cryptography;
 using System.Text;
 using System.Text;
 
 
 using Renci.SshNet.Abstractions;
 using Renci.SshNet.Abstractions;
@@ -9,6 +10,7 @@ using Renci.SshNet.Common;
 using Renci.SshNet.Messages.Authentication;
 using Renci.SshNet.Messages.Authentication;
 using Renci.SshNet.Messages.Connection;
 using Renci.SshNet.Messages.Connection;
 using Renci.SshNet.Security;
 using Renci.SshNet.Security;
+using Renci.SshNet.Security.Cryptography;
 using Renci.SshNet.Security.Cryptography.Ciphers;
 using Renci.SshNet.Security.Cryptography.Ciphers;
 using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
 using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
 
 
@@ -396,6 +398,8 @@ namespace Renci.SshNet
                     { "ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), 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-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data) },
                     { "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) },
                     { "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) },
+                    { "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)); }},
                     { "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data) },
                     { "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data) },
                     { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) },
                     { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) },
                 };
                 };

+ 21 - 0
src/Renci.SshNet/IHostAlgorithmsProvider.cs

@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+using Renci.SshNet.Security;
+
+namespace Renci.SshNet
+{
+    /// <summary>
+    /// Represents a collection of host algorithms.
+    /// </summary>
+    public interface IHostAlgorithmsProvider
+    {
+        /// <summary>
+        /// The host algorithms provided by this <see cref="IHostAlgorithmsProvider"/>.
+        /// </summary>
+        /// <remarks>
+        /// In situations where there is a preferred order of usage of the host algorithms,
+        /// the collection should be ordered from most preferred to least.
+        /// </remarks>
+        IReadOnlyCollection<HostAlgorithm> HostAlgorithms { get; }
+    }
+}

+ 14 - 3
src/Renci.SshNet/IPrivateKeySource.cs

@@ -1,15 +1,26 @@
-using Renci.SshNet.Security;
+using System;
+using System.ComponentModel;
+
+using Renci.SshNet.Security;
 
 
 namespace Renci.SshNet
 namespace Renci.SshNet
 {
 {
     /// <summary>
     /// <summary>
     /// Represents private key source interface.
     /// Represents private key source interface.
     /// </summary>
     /// </summary>
-    public interface IPrivateKeySource
+    /// <remarks>
+    /// This interface has been replaced by <see cref="IHostAlgorithmsProvider"/>
+    /// and is obsolete.
+    /// </remarks>
+    [Obsolete($"Use {nameof(IHostAlgorithmsProvider)} instead. " +
+        $"{nameof(IPrivateKeySource)} may be removed in a future release. " +
+        $"See https://github.com/sshnet/SSH.NET/issues/1174 for details.")]
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public interface IPrivateKeySource : IHostAlgorithmsProvider
     {
     {
         /// <summary>
         /// <summary>
         /// Gets the host key.
         /// Gets the host key.
         /// </summary>
         /// </summary>
         HostAlgorithm HostKey { get; }
         HostAlgorithm HostKey { get; }
     }
     }
-}
+}

+ 2 - 2
src/Renci.SshNet/NetConfClient.cs

@@ -107,7 +107,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
-        public NetConfClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
+        public NetConfClient(string host, int port, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
         {
         {
         }
         }
@@ -120,7 +120,7 @@ namespace Renci.SshNet
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
-        public NetConfClient(string host, string username, params IPrivateKeySource[] keyFiles)
+        public NetConfClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
         {
         {
         }
         }

+ 15 - 12
src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using System.Linq;
 using System.Threading;
 using System.Threading;
 
 
 using Renci.SshNet.Common;
 using Renci.SshNet.Common;
@@ -30,7 +31,7 @@ namespace Renci.SshNet
         /// <summary>
         /// <summary>
         /// Gets the key files used for authentication.
         /// Gets the key files used for authentication.
         /// </summary>
         /// </summary>
-        public ICollection<IPrivateKeySource> KeyFiles { get; private set; }
+        public ICollection<IHostAlgorithmsProvider> KeyFiles { get; private set; }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="PrivateKeyAuthenticationMethod"/> class.
         /// Initializes a new instance of the <see cref="PrivateKeyAuthenticationMethod"/> class.
@@ -38,7 +39,7 @@ namespace Renci.SshNet
         /// <param name="username">The username.</param>
         /// <param name="username">The username.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <exception cref="ArgumentException"><paramref name="username"/> is whitespace or <c>null</c>.</exception>
         /// <exception cref="ArgumentException"><paramref name="username"/> is whitespace or <c>null</c>.</exception>
-        public PrivateKeyAuthenticationMethod(string username, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyAuthenticationMethod(string username, params IHostAlgorithmsProvider[] keyFiles)
             : base(username)
             : base(username)
         {
         {
             if (keyFiles is null)
             if (keyFiles is null)
@@ -46,7 +47,7 @@ namespace Renci.SshNet
                 throw new ArgumentNullException(nameof(keyFiles));
                 throw new ArgumentNullException(nameof(keyFiles));
             }
             }
 
 
-            KeyFiles = new Collection<IPrivateKeySource>(keyFiles);
+            KeyFiles = new Collection<IHostAlgorithmsProvider>(keyFiles);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -64,24 +65,26 @@ namespace Renci.SshNet
 
 
             session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK");
             session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK");
 
 
+            var hostAlgorithms = KeyFiles.SelectMany(x => x.HostAlgorithms).ToList();
+
             try
             try
             {
             {
-                foreach (var keyFile in KeyFiles)
+                foreach (var hostAlgorithm in hostAlgorithms)
                 {
                 {
                     _ = _authenticationCompleted.Reset();
                     _ = _authenticationCompleted.Reset();
                     _isSignatureRequired = false;
                     _isSignatureRequired = false;
 
 
                     var message = new RequestMessagePublicKey(ServiceName.Connection,
                     var message = new RequestMessagePublicKey(ServiceName.Connection,
                                                               Username,
                                                               Username,
-                                                              keyFile.HostKey.Name,
-                                                              keyFile.HostKey.Data);
+                                                              hostAlgorithm.Name,
+                                                              hostAlgorithm.Data);
 
 
-                    if (KeyFiles.Count < 2)
+                    if (hostAlgorithms.Count == 1)
                     {
                     {
                         // If only one key file provided then send signature for very first request
                         // If only one key file provided then send signature for very first request
                         var signatureData = new SignatureData(message, session.SessionId).GetBytes();
                         var signatureData = new SignatureData(message, session.SessionId).GetBytes();
 
 
-                        message.Signature = keyFile.HostKey.Sign(signatureData);
+                        message.Signature = hostAlgorithm.Sign(signatureData);
                     }
                     }
 
 
                     // Send public key authentication request
                     // Send public key authentication request
@@ -95,12 +98,12 @@ namespace Renci.SshNet
 
 
                         var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection,
                         var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection,
                                                                            Username,
                                                                            Username,
-                                                                           keyFile.HostKey.Name,
-                                                                           keyFile.HostKey.Data);
+                                                                           hostAlgorithm.Name,
+                                                                           hostAlgorithm.Data);
 
 
                         var signatureData = new SignatureData(message, session.SessionId).GetBytes();
                         var signatureData = new SignatureData(message, session.SessionId).GetBytes();
 
 
-                        signatureMessage.Signature = keyFile.HostKey.Sign(signatureData);
+                        signatureMessage.Signature = hostAlgorithm.Sign(signatureData);
 
 
                         // Send public key authentication request with signature
                         // Send public key authentication request with signature
                         session.SendMessage(signatureMessage);
                         session.SendMessage(signatureMessage);
@@ -108,7 +111,7 @@ namespace Renci.SshNet
 
 
                     session.WaitOnHandle(_authenticationCompleted);
                     session.WaitOnHandle(_authenticationCompleted);
 
 
-                    if (_authenticationResult == AuthenticationResult.Success)
+                    if (_authenticationResult is AuthenticationResult.Success or AuthenticationResult.PartialSuccess)
                     {
                     {
                         break;
                         break;
                     }
                     }

+ 9 - 9
src/Renci.SshNet/PrivateKeyConnectionInfo.cs

@@ -17,7 +17,7 @@ namespace Renci.SshNet
         /// <summary>
         /// <summary>
         /// Gets the key files used for authentication.
         /// Gets the key files used for authentication.
         /// </summary>
         /// </summary>
-        public ICollection<IPrivateKeySource> KeyFiles { get; private set; }
+        public ICollection<IHostAlgorithmsProvider> KeyFiles { get; private set; }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="PrivateKeyConnectionInfo"/> class.
         /// Initializes a new instance of the <see cref="PrivateKeyConnectionInfo"/> class.
@@ -41,7 +41,7 @@ namespace Renci.SshNet
         /// <param name="port">Connection port.</param>
         /// <param name="port">Connection port.</param>
         /// <param name="username">Connection username.</param>
         /// <param name="username">Connection username.</param>
         /// <param name="keyFiles">Connection key files.</param>
         /// <param name="keyFiles">Connection key files.</param>
-        public PrivateKeyConnectionInfo(string host, int port, string username, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyConnectionInfo(string host, int port, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles)
             : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles)
         {
         {
         }
         }
@@ -56,7 +56,7 @@ namespace Renci.SshNet
         /// <param name="proxyHost">The proxy host.</param>
         /// <param name="proxyHost">The proxy host.</param>
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <param name="keyFiles">The key files.</param>
-        public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles)
             : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles)
         {
         {
         }
         }
@@ -72,7 +72,7 @@ namespace Renci.SshNet
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <param name="keyFiles">The key files.</param>
-        public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles)
             : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles)
         {
         {
         }
         }
@@ -86,7 +86,7 @@ namespace Renci.SshNet
         /// <param name="proxyHost">The proxy host.</param>
         /// <param name="proxyHost">The proxy host.</param>
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <param name="keyFiles">The key files.</param>
-        public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles)
             : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles)
         {
         {
         }
         }
@@ -101,7 +101,7 @@ namespace Renci.SshNet
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="proxyPort">The proxy port.</param>
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <param name="keyFiles">The key files.</param>
-        public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles)
             : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles)
         {
         {
         }
         }
@@ -117,7 +117,7 @@ namespace Renci.SshNet
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="proxyPassword">The proxy password.</param>
         /// <param name="proxyPassword">The proxy password.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <param name="keyFiles">The key files.</param>
-        public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, keyFiles)
             : this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, keyFiles)
         {
         {
         }
         }
@@ -134,10 +134,10 @@ namespace Renci.SshNet
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="proxyUsername">The proxy username.</param>
         /// <param name="proxyPassword">The proxy password.</param>
         /// <param name="proxyPassword">The proxy password.</param>
         /// <param name="keyFiles">The key files.</param>
         /// <param name="keyFiles">The key files.</param>
-        public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles)
+        public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IHostAlgorithmsProvider[] keyFiles)
             : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(username, keyFiles))
             : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(username, keyFiles))
         {
         {
-            KeyFiles = new Collection<IPrivateKeySource>(keyFiles);
+            KeyFiles = new Collection<IHostAlgorithmsProvider>(keyFiles);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 59 - 7
src/Renci.SshNet/PrivateKeyFile.cs

@@ -1,7 +1,9 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
+using System.Security.Cryptography;
 using System.Text;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 
 
@@ -63,18 +65,48 @@ namespace Renci.SshNet
     /// </list>
     /// </list>
     /// </para>
     /// </para>
     /// </remarks>
     /// </remarks>
-    public class PrivateKeyFile : IPrivateKeySource, IDisposable
+    public class PrivateKeyFile : IHostAlgorithmsProvider,
+#pragma warning disable CS0618 // Type or member is obsolete
+        IPrivateKeySource,
+#pragma warning restore CS0618 // Type or member is obsolete
+        IDisposable
     {
     {
         private static readonly Regex PrivateKeyRegex = new Regex(@"^-+ *BEGIN (?<keyName>\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k<keyName> PRIVATE KEY *-+",
         private static readonly Regex PrivateKeyRegex = new Regex(@"^-+ *BEGIN (?<keyName>\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k<keyName> PRIVATE KEY *-+",
             RegexOptions.Compiled | RegexOptions.Multiline);
             RegexOptions.Compiled | RegexOptions.Multiline);
 
 
+        private readonly List<HostAlgorithm> _hostAlgorithms = new List<HostAlgorithm>();
         private Key _key;
         private Key _key;
         private bool _isDisposed;
         private bool _isDisposed;
 
 
         /// <summary>
         /// <summary>
         /// Gets the host key.
         /// Gets the host key.
         /// </summary>
         /// </summary>
-        public HostAlgorithm HostKey { get; private set; }
+        /// <remarks>
+        /// This property returns the first item in <see cref="HostAlgorithms"/>.
+        /// </remarks>
+        public HostAlgorithm HostKey
+        {
+            get
+            {
+                return _hostAlgorithms[0];
+            }
+            private set
+            {
+                Debug.Assert(_hostAlgorithms.Count == 0, $"Only expected to set {nameof(HostKey)} at most once.");
+                _hostAlgorithms.Add(value);
+            }
+        }
+
+        /// <summary>
+        /// The supported host algorithms for this key file.
+        /// </summary>
+        public IReadOnlyCollection<HostAlgorithm> HostAlgorithms
+        {
+            get
+            {
+                return _hostAlgorithms;
+            }
+        }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
         /// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
@@ -92,6 +124,7 @@ namespace Renci.SshNet
         public PrivateKeyFile(Stream privateKey)
         public PrivateKeyFile(Stream privateKey)
         {
         {
             Open(privateKey, passPhrase: null);
             Open(privateKey, passPhrase: null);
+            Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKey)} is not set.");
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -127,6 +160,8 @@ namespace Renci.SshNet
             {
             {
                 Open(keyFile, passPhrase);
                 Open(keyFile, passPhrase);
             }
             }
+
+            Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKey)} is not set.");
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -138,6 +173,7 @@ namespace Renci.SshNet
         public PrivateKeyFile(Stream privateKey, string passPhrase)
         public PrivateKeyFile(Stream privateKey, string passPhrase)
         {
         {
             Open(privateKey, passPhrase);
             Open(privateKey, passPhrase);
+            Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKey)} is not set.");
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -222,8 +258,11 @@ namespace Renci.SshNet
             switch (keyName)
             switch (keyName)
             {
             {
                 case "RSA":
                 case "RSA":
-                    _key = new RsaKey(decryptedData);
-                    HostKey = new KeyHostAlgorithm("ssh-rsa", _key);
+                    var rsaKey = new RsaKey(decryptedData);
+                    _key = rsaKey;
+                    _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA512)));
+                    _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256)));
+                    _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key));
                     break;
                     break;
                 case "DSA":
                 case "DSA":
                     _key = new DsaKey(decryptedData);
                     _key = new DsaKey(decryptedData);
@@ -235,7 +274,17 @@ namespace Renci.SshNet
                     break;
                     break;
                 case "OPENSSH":
                 case "OPENSSH":
                     _key = ParseOpenSshV1Key(decryptedData, passPhrase);
                     _key = ParseOpenSshV1Key(decryptedData, passPhrase);
-                    HostKey = new KeyHostAlgorithm(_key.ToString(), _key);
+                    if (_key is RsaKey parsedRsaKey)
+                    {
+                        _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(parsedRsaKey, HashAlgorithmName.SHA512)));
+                        _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(parsedRsaKey, HashAlgorithmName.SHA256)));
+                        _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key));
+                    }
+                    else
+                    {
+                        HostKey = new KeyHostAlgorithm(_key.ToString(), _key);
+                    }
+
                     break;
                     break;
                 case "SSH2 ENCRYPTED":
                 case "SSH2 ENCRYPTED":
                     var reader = new SshDataReader(decryptedData);
                     var reader = new SshDataReader(decryptedData);
@@ -290,8 +339,11 @@ namespace Renci.SshNet
                         var inverseQ = reader.ReadBigIntWithBits(); // u
                         var inverseQ = reader.ReadBigIntWithBits(); // u
                         var q = reader.ReadBigIntWithBits(); // p
                         var q = reader.ReadBigIntWithBits(); // p
                         var p = reader.ReadBigIntWithBits(); // q
                         var p = reader.ReadBigIntWithBits(); // q
-                        _key = new RsaKey(modulus, exponent, d, p, q, inverseQ);
-                        HostKey = new KeyHostAlgorithm("ssh-rsa", _key);
+                        var decryptedRsaKey = new RsaKey(modulus, exponent, d, p, q, inverseQ);
+                        _key = decryptedRsaKey;
+                        _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(decryptedRsaKey, HashAlgorithmName.SHA512)));
+                        _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(decryptedRsaKey, HashAlgorithmName.SHA256)));
+                        _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key));
                     }
                     }
                     else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}")
                     else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}")
                     {
                     {

+ 2 - 2
src/Renci.SshNet/ScpClient.cs

@@ -146,7 +146,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
-        public ScpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
+        public ScpClient(string host, int port, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
         {
         {
         }
         }
@@ -159,7 +159,7 @@ namespace Renci.SshNet
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
-        public ScpClient(string host, string username, params IPrivateKeySource[] keyFiles)
+        public ScpClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
         {
         {
         }
         }

+ 3 - 4
src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs

@@ -8,8 +8,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
     /// </summary>
     /// </summary>
     public class RsaCipher : AsymmetricCipher
     public class RsaCipher : AsymmetricCipher
     {
     {
-        private readonly bool _isPrivate;
-
         private readonly RsaKey _key;
         private readonly RsaKey _key;
 
 
         /// <summary>
         /// <summary>
@@ -24,7 +22,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
             }
             }
 
 
             _key = key;
             _key = key;
-            _isPrivate = !_key.D.IsZero;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -116,7 +113,9 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 
 
             BigInteger result;
             BigInteger result;
 
 
-            if (_isPrivate)
+            var isPrivate = !_key.D.IsZero;
+
+            if (isPrivate)
             {
             {
                 var random = BigInteger.One;
                 var random = BigInteger.One;
                 var max = _key.Modulus - 1;
                 var max = _key.Modulus - 1;

+ 1 - 1
src/Renci.SshNet/Security/Cryptography/DsaKey.cs

@@ -84,7 +84,7 @@ namespace Renci.SshNet.Security
         /// <summary>
         /// <summary>
         /// Gets the digital signature.
         /// Gets the digital signature.
         /// </summary>
         /// </summary>
-        protected override DigitalSignature DigitalSignature
+        protected internal override DigitalSignature DigitalSignature
         {
         {
             get
             get
             {
             {

+ 1 - 1
src/Renci.SshNet/Security/Cryptography/ED25519Key.cs

@@ -62,7 +62,7 @@ namespace Renci.SshNet.Security
         /// <summary>
         /// <summary>
         /// Gets the digital signature.
         /// Gets the digital signature.
         /// </summary>
         /// </summary>
-        protected override DigitalSignature DigitalSignature
+        protected internal override DigitalSignature DigitalSignature
         {
         {
             get
             get
             {
             {

+ 1 - 1
src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs

@@ -128,7 +128,7 @@ namespace Renci.SshNet.Security
         /// <summary>
         /// <summary>
         /// Gets the digital signature.
         /// Gets the digital signature.
         /// </summary>
         /// </summary>
-        protected override DigitalSignature DigitalSignature
+        protected internal override DigitalSignature DigitalSignature
         {
         {
             get
             get
             {
             {

+ 2 - 2
src/Renci.SshNet/Security/Cryptography/Key.cs

@@ -17,9 +17,9 @@ namespace Renci.SshNet.Security
         protected BigInteger[] _privateKey;
         protected BigInteger[] _privateKey;
 
 
         /// <summary>
         /// <summary>
-        /// Gets the key specific digital signature.
+        /// Gets the default digital signature implementation for this key.
         /// </summary>
         /// </summary>
-        protected abstract DigitalSignature DigitalSignature { get; }
+        protected internal abstract DigitalSignature DigitalSignature { get; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the public key.
         /// Gets or sets the public key.

+ 13 - 4
src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs

@@ -1,6 +1,5 @@
 using System;
 using System;
 using System.Security.Cryptography;
 using System.Security.Cryptography;
-using Renci.SshNet.Abstractions;
 using Renci.SshNet.Common;
 using Renci.SshNet.Common;
 using Renci.SshNet.Security.Cryptography.Ciphers;
 using Renci.SshNet.Security.Cryptography.Ciphers;
 
 
@@ -14,13 +13,23 @@ namespace Renci.SshNet.Security.Cryptography
         private HashAlgorithm _hash;
         private HashAlgorithm _hash;
 
 
         /// <summary>
         /// <summary>
-        /// Initializes a new instance of the <see cref="RsaDigitalSignature"/> class.
+        /// Initializes a new instance of the <see cref="RsaDigitalSignature"/> class with the SHA-1 hash algorithm.
         /// </summary>
         /// </summary>
         /// <param name="rsaKey">The RSA key.</param>
         /// <param name="rsaKey">The RSA key.</param>
         public RsaDigitalSignature(RsaKey rsaKey)
         public RsaDigitalSignature(RsaKey rsaKey)
-            : base(new ObjectIdentifier(1, 3, 14, 3, 2, 26), new RsaCipher(rsaKey))
+            : this(rsaKey, HashAlgorithmName.SHA1)
+        { }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RsaDigitalSignature"/> class.
+        /// </summary>
+        /// <param name="rsaKey">The RSA key.</param>
+        /// <param name="hashAlgorithmName">The hash algorithm to use in the digital signature.</param>
+        public RsaDigitalSignature(RsaKey rsaKey, HashAlgorithmName hashAlgorithmName)
+            : base(ObjectIdentifier.FromHashAlgorithmName(hashAlgorithmName), new RsaCipher(rsaKey))
         {
         {
-            _hash = CryptoAbstraction.CreateSHA1();
+            _hash = CryptoConfig.CreateFromName(hashAlgorithmName.Name) as HashAlgorithm
+                ?? throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithmName}`.", nameof(hashAlgorithmName));
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 6 - 2
src/Renci.SshNet/Security/Cryptography/RsaKey.cs

@@ -153,10 +153,14 @@ namespace Renci.SshNet.Security
         }
         }
 
 
         private RsaDigitalSignature _digitalSignature;
         private RsaDigitalSignature _digitalSignature;
+
         /// <summary>
         /// <summary>
-        /// Gets the digital signature.
+        /// <inheritdoc cref="Key.DigitalSignature"/>
         /// </summary>
         /// </summary>
-        protected override DigitalSignature DigitalSignature
+        /// <returns>
+        /// An implementation of an RSA digital signature using the SHA-1 hash algorithm.
+        /// </returns>
+        protected internal override DigitalSignature DigitalSignature
         {
         {
             get
             get
             {
             {

+ 22 - 0
src/Renci.SshNet/Security/KeyExchange.cs

@@ -318,6 +318,28 @@ namespace Renci.SshNet.Security
         /// <returns>true if exchange hash is valid; otherwise false.</returns>
         /// <returns>true if exchange hash is valid; otherwise false.</returns>
         protected abstract bool ValidateExchangeHash();
         protected abstract bool ValidateExchangeHash();
 
 
+        private protected bool ValidateExchangeHash(byte[] encodedKey, byte[] encodedSignature)
+        {
+            var exchangeHash = CalculateHash();
+
+            var signatureData = new KeyHostAlgorithm.SignatureKeyData();
+            signatureData.Load(encodedSignature);
+
+            var keyAlgorithm = Session.ConnectionInfo.HostKeyAlgorithms[signatureData.AlgorithmName](encodedKey);
+
+            Session.ConnectionInfo.CurrentHostKeyAlgorithm = signatureData.AlgorithmName;
+
+            if (CanTrustHostKey(keyAlgorithm))
+            {
+                // keyAlgorithm.VerifySignature decodes the signature data before verifying.
+                // But as we have already decoded the data to find the signature algorithm,
+                // we just verify the decoded data directly through the DigitalSignature.
+                return keyAlgorithm.DigitalSignature.Verify(exchangeHash, signatureData.Signature);
+            }
+
+            return false;
+        }
+
         /// <summary>
         /// <summary>
         /// Calculates key exchange hash value.
         /// Calculates key exchange hash value.
         /// </summary>
         /// </summary>

+ 1 - 15
src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs

@@ -1,5 +1,4 @@
 using System;
 using System;
-using System.Text;
 
 
 using Renci.SshNet.Common;
 using Renci.SshNet.Common;
 using Renci.SshNet.Messages.Transport;
 using Renci.SshNet.Messages.Transport;
@@ -72,20 +71,7 @@ namespace Renci.SshNet.Security
         /// </returns>
         /// </returns>
         protected override bool ValidateExchangeHash()
         protected override bool ValidateExchangeHash()
         {
         {
-            var exchangeHash = CalculateHash();
-
-            var length = Pack.BigEndianToUInt32(_hostKey);
-            var algorithmName = Encoding.UTF8.GetString(_hostKey, 4, (int)length);
-            var key = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](_hostKey);
-
-            Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName;
-
-            if (CanTrustHostKey(key))
-            {
-                return key.VerifySignature(exchangeHash, _signature);
-            }
-
-            return false;
+            return ValidateExchangeHash(_hostKey, _signature);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 2 - 18
src/Renci.SshNet/Security/KeyExchangeEC.cs

@@ -1,7 +1,4 @@
-using System.Text;
-
-using Renci.SshNet.Common;
-using Renci.SshNet.Messages.Transport;
+using Renci.SshNet.Messages.Transport;
 
 
 namespace Renci.SshNet.Security
 namespace Renci.SshNet.Security
 {
 {
@@ -76,20 +73,7 @@ namespace Renci.SshNet.Security
         /// </returns>
         /// </returns>
         protected override bool ValidateExchangeHash()
         protected override bool ValidateExchangeHash()
         {
         {
-            var exchangeHash = CalculateHash();
-
-            var length = Pack.BigEndianToUInt32(_hostKey);
-            var algorithmName = Encoding.UTF8.GetString(_hostKey, 4, (int)length);
-            var key = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](_hostKey);
-
-            Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName;
-
-            if (CanTrustHostKey(key))
-            {
-                return key.VerifySignature(exchangeHash, _signature);
-            }
-
-            return false;
+            return ValidateExchangeHash(_hostKey, _signature);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 95 - 25
src/Renci.SshNet/Security/KeyHostAlgorithm.cs

@@ -1,6 +1,9 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Text;
+
 using Renci.SshNet.Common;
 using Renci.SshNet.Common;
 using Renci.SshNet.Security.Chaos.NaCl;
 using Renci.SshNet.Security.Chaos.NaCl;
+using Renci.SshNet.Security.Cryptography;
 
 
 namespace Renci.SshNet.Security
 namespace Renci.SshNet.Security
 {
 {
@@ -10,38 +13,76 @@ namespace Renci.SshNet.Security
     public class KeyHostAlgorithm : HostAlgorithm
     public class KeyHostAlgorithm : HostAlgorithm
     {
     {
         /// <summary>
         /// <summary>
-        /// Gets the key.
+        /// The key used in this host key algorithm.
         /// </summary>
         /// </summary>
         public Key Key { get; private set; }
         public Key Key { get; private set; }
 
 
         /// <summary>
         /// <summary>
-        /// Gets the public key data.
+        /// The signature implementation used in this host key algorithm.
+        /// </summary>
+        public DigitalSignature DigitalSignature { get; private set; }
+
+        /// <summary>
+        /// Gets the encoded public key data.
         /// </summary>
         /// </summary>
         public override byte[] Data
         public override byte[] Data
         {
         {
             get
             get
             {
             {
-                return new SshKeyData(Name, Key.Public).GetBytes();
+                var keyFormatIdentifier = Key is RsaKey ? "ssh-rsa" : Name;
+                return new SshKeyData(keyFormatIdentifier, Key.Public).GetBytes();
             }
             }
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="KeyHostAlgorithm"/> class.
         /// Initializes a new instance of the <see cref="KeyHostAlgorithm"/> class.
         /// </summary>
         /// </summary>
-        /// <param name="name">Host key name.</param>
-        /// <param name="key">Host key.</param>
+        /// <param name="name">The signature format identifier.</param>
+        /// <param name="key"><inheritdoc cref="Key" path="/summary"/></param>
+        /// <remarks>
+        /// This constructor is typically passed a private key in order to create an encoded signature for later
+        /// verification by the host.
+        /// </remarks>
         public KeyHostAlgorithm(string name, Key key)
         public KeyHostAlgorithm(string name, Key key)
             : base(name)
             : base(name)
         {
         {
             Key = key;
             Key = key;
+            DigitalSignature = key.DigitalSignature;
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="KeyHostAlgorithm"/> class.
         /// Initializes a new instance of the <see cref="KeyHostAlgorithm"/> class.
         /// </summary>
         /// </summary>
-        /// <param name="name">Host key name.</param>
-        /// <param name="key">Host key.</param>
+        /// <param name="name">The signature format identifier.</param>
+        /// <param name="key"><inheritdoc cref="Key" path="/summary"/></param>
+        /// <param name="digitalSignature"><inheritdoc cref="DigitalSignature" path="/summary"/></param>
+        /// <remarks>
+        /// <para>
+        /// This constructor is typically passed a private key in order to create an encoded signature for later
+        /// verification by the host.
+        /// </para>
+        /// The key used by <paramref name="digitalSignature"/> is intended to be equal to <paramref name="key"/>.
+        /// This is not verified.
+        /// </remarks>
+        public KeyHostAlgorithm(string name, Key key, DigitalSignature digitalSignature)
+            : base(name)
+        {
+            Key = key;
+            DigitalSignature = digitalSignature;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="KeyHostAlgorithm"/> class
+        /// with the given encoded public key data. The data will be decoded into <paramref name="key"/>.
+        /// </summary>
+        /// <param name="name">The signature format identifier.</param>
+        /// <param name="key"><inheritdoc cref="Key" path="/summary"/></param>
         /// <param name="data">Host key encoded data.</param>
         /// <param name="data">Host key encoded data.</param>
+        /// <remarks>
+        /// This constructor is typically passed a new or reusable <see cref="Security.Key"/> 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 <paramref name="data"/>.
+        /// </remarks>
         public KeyHostAlgorithm(string name, Key key, byte[] data)
         public KeyHostAlgorithm(string name, Key key, byte[] data)
             : base(name)
             : base(name)
         {
         {
@@ -50,34 +91,66 @@ namespace Renci.SshNet.Security
             var sshKey = new SshKeyData();
             var sshKey = new SshKeyData();
             sshKey.Load(data);
             sshKey.Load(data);
             Key.Public = sshKey.Keys;
             Key.Public = sshKey.Keys;
+
+            DigitalSignature = key.DigitalSignature;
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Signs the specified data.
+        /// Initializes a new instance of the <see cref="KeyHostAlgorithm"/> class
+        /// with the given encoded public key data. The data will be decoded into <paramref name="key"/>.
         /// </summary>
         /// </summary>
-        /// <param name="data">The data.</param>
+        /// <param name="name">The signature format identifier.</param>
+        /// <param name="key"><inheritdoc cref="Key" path="/summary"/></param>
+        /// <param name="data">Host key encoded data.</param>
+        /// <param name="digitalSignature"><inheritdoc cref="DigitalSignature" path="/summary"/></param>
+        /// <remarks>
+        /// <para>
+        /// This constructor is typically passed a new or reusable <see cref="Security.Key"/> 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 <paramref name="data"/>.
+        /// </para>
+        /// The key used by <paramref name="digitalSignature"/> is intended to be equal to <paramref name="key"/>.
+        /// This is not verified.
+        /// </remarks>
+        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;
+        }
+
+        /// <summary>
+        /// Signs and encodes the specified data.
+        /// </summary>
+        /// <param name="data">The data to be signed.</param>
         /// <returns>
         /// <returns>
-        /// Signed data.
+        /// The encoded signature.
         /// </returns>
         /// </returns>
         public override byte[] Sign(byte[] data)
         public override byte[] Sign(byte[] data)
         {
         {
-            return new SignatureKeyData(Name, Key.Sign(data)).GetBytes();
+            return new SignatureKeyData(Name, DigitalSignature.Sign(data)).GetBytes();
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Verifies the signature.
         /// Verifies the signature.
         /// </summary>
         /// </summary>
-        /// <param name="data">The data.</param>
-        /// <param name="signature">The signature.</param>
+        /// <param name="data">The data to verify the signature against.</param>
+        /// <param name="signature">The encoded signature data.</param>
         /// <returns>
         /// <returns>
-        ///   <c>True</c> is signature was successfully verifies; otherwise <c>false</c>.
+        /// <see langword="true"/> if <paramref name="signature"/> is the result of signing <paramref name="data"/>
+        /// with the corresponding private key to <see cref="Key"/>.
         /// </returns>
         /// </returns>
         public override bool VerifySignature(byte[] data, byte[] signature)
         public override bool VerifySignature(byte[] data, byte[] signature)
         {
         {
             var signatureData = new SignatureKeyData();
             var signatureData = new SignatureKeyData();
             signatureData.Load(signature);
             signatureData.Load(signature);
 
 
-            return Key.VerifySignature(data, signatureData.Signature);
+            return DigitalSignature.Verify(data, signatureData.Signature);
         }
         }
 
 
         private sealed class SshKeyData : SshData
         private sealed class SshKeyData : SshData
@@ -170,15 +243,12 @@ namespace Renci.SshNet.Security
             }
             }
         }
         }
 
 
-        private sealed class SignatureKeyData : SshData
+        internal sealed class SignatureKeyData : SshData
         {
         {
             /// <summary>
             /// <summary>
-            /// Gets or sets the name of the algorithm as UTF-8 encoded byte array.
+            /// Gets or sets the signature format identifier
             /// </summary>
             /// </summary>
-            /// <value>
-            /// The name of the algorithm.
-            /// </value>
-            private byte[] AlgorithmName { get; set; }
+            public string AlgorithmName { get; set; }
 
 
             /// <summary>
             /// <summary>
             /// Gets the signature.
             /// Gets the signature.
@@ -200,7 +270,7 @@ namespace Renci.SshNet.Security
                 {
                 {
                     var capacity = base.BufferCapacity;
                     var capacity = base.BufferCapacity;
                     capacity += 4; // AlgorithmName length
                     capacity += 4; // AlgorithmName length
-                    capacity += AlgorithmName.Length; // AlgorithmName
+                    capacity += Encoding.UTF8.GetByteCount(AlgorithmName); // AlgorithmName
                     capacity += 4; // Signature length
                     capacity += 4; // Signature length
                     capacity += Signature.Length; // Signature
                     capacity += Signature.Length; // Signature
                     return capacity;
                     return capacity;
@@ -213,7 +283,7 @@ namespace Renci.SshNet.Security
 
 
             public SignatureKeyData(string name, byte[] signature)
             public SignatureKeyData(string name, byte[] signature)
             {
             {
-                AlgorithmName = Utf8.GetBytes(name);
+                AlgorithmName = name;
                 Signature = signature;
                 Signature = signature;
             }
             }
 
 
@@ -222,7 +292,7 @@ namespace Renci.SshNet.Security
             /// </summary>
             /// </summary>
             protected override void LoadData()
             protected override void LoadData()
             {
             {
-                AlgorithmName = ReadBinary();
+                AlgorithmName = Encoding.UTF8.GetString(ReadBinary());
                 Signature = ReadBinary();
                 Signature = ReadBinary();
             }
             }
 
 
@@ -231,7 +301,7 @@ namespace Renci.SshNet.Security
             /// </summary>
             /// </summary>
             protected override void SaveData()
             protected override void SaveData()
             {
             {
-                WriteBinaryString(AlgorithmName);
+                WriteBinaryString(Encoding.UTF8.GetBytes(AlgorithmName));
                 WriteBinaryString(Signature);
                 WriteBinaryString(Signature);
             }
             }
         }
         }

+ 2 - 2
src/Renci.SshNet/SftpClient.cs

@@ -218,7 +218,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid. <para>-or-</para> <paramref name="username"/> is nu<b>null</b>ll or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid. <para>-or-</para> <paramref name="username"/> is nu<b>null</b>ll or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
-        public SftpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
+        public SftpClient(string host, int port, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
         {
         {
         }
         }
@@ -231,7 +231,7 @@ namespace Renci.SshNet
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <param name="keyFiles">Authentication private key file(s) .</param>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <b>null</b>.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <b>null</b>.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid. <para>-or-</para> <paramref name="username"/> is <b>null</b> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid. <para>-or-</para> <paramref name="username"/> is <b>null</b> or contains only whitespace characters.</exception>
-        public SftpClient(string host, string username, params IPrivateKeySource[] keyFiles)
+        public SftpClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
         {
         {
         }
         }

+ 2 - 2
src/Renci.SshNet/SshClient.cs

@@ -103,7 +103,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
-        public SshClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
+        public SshClient(string host, int port, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
             : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), ownsConnectionInfo: true)
         {
         {
         }
         }
@@ -120,7 +120,7 @@ namespace Renci.SshNet
         /// </example>
         /// </example>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
         /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
         /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
-        public SshClient(string host, string username, params IPrivateKeySource[] keyFiles)
+        public SshClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
             : this(host, ConnectionInfo.DefaultPort, username, keyFiles)
         {
         {
         }
         }