diff --git a/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs b/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs
index 64676a0d2..af64ff62a 100644
--- a/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs
+++ b/src/Renci.SshNet.IntegrationTests/Common/RemoteSshdConfigExtensions.cs
@@ -20,9 +20,7 @@ public static void Reset(this RemoteSshdConfig remoteSshdConfig)
.ClearCiphers()
.ClearKeyExchangeAlgorithms()
.ClearHostKeyAlgorithms()
- .AddHostKeyAlgorithm(HostKeyAlgorithm.SshRsa)
.ClearPublicKeyAcceptedAlgorithms()
- .AddPublicKeyAcceptedAlgorithms(PublicKeyAlgorithm.SshRsa)
.WithUsePAM(true)
.Update()
.Restart();
diff --git a/src/Renci.SshNet.IntegrationTests/Dockerfile b/src/Renci.SshNet.IntegrationTests/Dockerfile
index 811d51543..160ea6f29 100644
--- a/src/Renci.SshNet.IntegrationTests/Dockerfile
+++ b/src/Renci.SshNet.IntegrationTests/Dockerfile
@@ -14,7 +14,6 @@ RUN apk update && apk upgrade --no-cache && \
chmod 400 /etc/ssh/ssh*key && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /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 && \
# install and configure sudo
apk add --no-cache sudo && \
diff --git a/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs b/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs
index 3732f1e22..7f177a6f4 100644
--- a/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs
+++ b/src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs
@@ -22,63 +22,46 @@ public void TearDown()
{
_remoteSshdConfig?.Reset();
}
-
+
[TestMethod]
[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.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]
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]
public void SshEd25519()
+ {
+ DoTest(HostKeyAlgorithm.SshEd25519, HostKeyFile.Ed25519, 256);
+ }
+
+ private void DoTest(HostKeyAlgorithm hostKeyAlgorithm, HostKeyFile hostKeyFile, int keyLength)
{
_remoteSshdConfig.ClearHostKeyAlgorithms()
- .AddHostKeyAlgorithm(HostKeyAlgorithm.SshEd25519)
+ .AddHostKeyAlgorithm(hostKeyAlgorithm)
.ClearHostKeyFiles()
- .AddHostKeyFile(HostKeyFile.Ed25519.FilePath)
+ .AddHostKeyFile(hostKeyFile.FilePath)
.Update()
.Restart();
@@ -92,14 +75,9 @@ public void SshEd25519()
}
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);
}
}
}
diff --git a/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs b/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs
index 83313f4a8..308f2ab3f 100644
--- a/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs
+++ b/src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs
@@ -5,7 +5,7 @@ namespace Renci.SshNet.IntegrationTests
{
[TestClass]
public class PrivateKeyAuthenticationTests : TestBase
- {
+ {
private IConnectionInfoFactory _connectionInfoFactory;
private RemoteSshdConfig _remoteSshdConfig;
@@ -23,43 +23,64 @@ public void TearDown()
}
[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]
- 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]
- 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()
.Restart();
- var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod("key_ecdsa_521_openssh"));
+ var connectionInfo = _connectionInfoFactory.Create(CreatePrivateKeyAuthenticationMethod(keyResource));
using (var client = new SshClient(connectionInfo))
{
diff --git a/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj b/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
index ae3505f10..db411f361 100644
--- a/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
+++ b/src/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
@@ -60,6 +60,7 @@
+
diff --git a/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys b/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys
index 484a0bdcc..8f0372d84 100644
--- a/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys
+++ b/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
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw=
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ==
-ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ==
\ No newline at end of file
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ==
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAkNGPVOTuzuKTgGfHcve2MRj57yXhmZgkUyi9RpmJrl
diff --git a/src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs b/src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
index b80967113..7dc9f309c 100644
--- a/src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
+++ b/src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
@@ -211,8 +211,11 @@ public void SaveTo(TextWriter writer)
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)
{
_matchFormatter.Format(match, writer);
diff --git a/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs b/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs
index 516e3623f..e7ee53b90 100644
--- a/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs
+++ b/src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs
@@ -42,7 +42,7 @@ public void HostKeyEventArgsConstructorTest()
0xa0, 0x23, 0xaf, 0xff, 0x9c, 0x0f, 0x8c, 0x83, 0x7c, 0xf8, 0xe1, 0x8e, 0x32, 0x8e, 0x61, 0xfc,
0x5b, 0xbd, 0xd4, 0x46, 0xe1
}.SequenceEqual(target.HostKey));
- Assert.AreEqual("ssh-rsa", target.HostKeyName);
+ Assert.AreEqual("rsa-sha2-512", target.HostKeyName);
Assert.AreEqual(2048, target.KeyLength);
}
diff --git a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
index 4ca6e9544..c684f0be7 100644
--- a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
+++ b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
@@ -1,8 +1,11 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Renci.SshNet.Common;
+using Renci.SshNet.Security;
using Renci.SshNet.Tests.Common;
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
namespace Renci.SshNet.Tests.Classes
{
@@ -144,7 +147,7 @@ public void Test_PrivateKey_RSA()
{
using (var stream = GetData("Key.RSA.txt"))
{
- _ = new PrivateKeyFile(stream);
+ TestRsaKeyFile(new PrivateKeyFile(stream));
}
}
@@ -166,7 +169,7 @@ public void Test_PrivateKey_SSH2_RSA()
{
using (var stream = GetData("Key.SSH2.RSA.txt"))
{
- _ = new PrivateKeyFile(stream);
+ TestRsaKeyFile(new PrivateKeyFile(stream));
}
}
@@ -188,7 +191,7 @@ public void Test_PrivateKey_SSH2_Encrypted_RSA_DES_CBC()
{
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 @@ public void Test_PrivateKey_RSA_DES_CBC()
{
using (var stream = GetData("Key.RSA.Encrypted.Des.CBC.12345.txt"))
{
- _ = new PrivateKeyFile(stream, "12345");
+ TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
}
}
@@ -284,7 +287,7 @@ public void Test_PrivateKey_RSA_AES_128_CBC()
{
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 @@ public void Test_PrivateKey_RSA_AES_192_CBC()
{
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 @@ public void Test_PrivateKey_RSA_AES_256_CBC()
{
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 @@ public void Test_PrivateKey_RSA_DES_EDE3_CFB()
{
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 @@ public void Test_PrivateKey_OPENSSH_RSA()
{
using (var stream = GetData("Key.OPENSSH.RSA.txt"))
{
- _ = new PrivateKeyFile(stream);
+ TestRsaKeyFile(new PrivateKeyFile(stream));
}
}
@@ -587,7 +590,7 @@ public void Test_PrivateKey_OPENSSH_RSA_ENCRYPTED()
{
using (var stream = GetData("Key.OPENSSH.RSA.Encrypted.txt"))
{
- _ = new PrivateKeyFile(stream, "12345");
+ TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
}
}
@@ -678,5 +681,18 @@ private string GetTempFileName()
File.Delete(tempFile);
return tempFile;
}
+
+ private static void TestRsaKeyFile(PrivateKeyFile rsaPrivateKeyFile)
+ {
+ Assert.AreEqual(3, rsaPrivateKeyFile.HostAlgorithms.Count);
+
+ List algorithms = rsaPrivateKeyFile.HostAlgorithms.Cast().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);
+ }
}
}
diff --git a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs b/src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs
index 93a76bbee..0c5c7eb02 100644
--- a/src/Renci.SshNet.Tests/Classes/Security/Cryptography/RsaDigitalSignatureTest.cs
+++ b/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.Cryptography;
using Renci.SshNet.Tests.Common;
@@ -11,17 +16,156 @@ namespace Renci.SshNet.Tests.Classes.Security.Cryptography
[TestClass]
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));
+ }
- ///
- ///A test for RsaDigitalSignature Constructor
- ///
[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(
+ () => 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;
+ }
}
///
@@ -37,4 +181,4 @@ public void DisposeTest()
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs b/src/Renci.SshNet.Tests/Classes/Security/KeyAlgorithmTest.cs
new file mode 100644
index 000000000..6f92362b4
--- /dev/null
+++ b/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
+ };
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Common/TestBase.cs b/src/Renci.SshNet.Tests/Common/TestBase.cs
index 4ae86fb41..5973aa4a3 100644
--- a/src/Renci.SshNet.Tests/Common/TestBase.cs
+++ b/src/Renci.SshNet.Tests/Common/TestBase.cs
@@ -49,9 +49,9 @@ protected void CreateTestFile(string fileName, int size)
}
}
- protected Stream GetData(string name)
+ protected static Stream GetData(string name)
{
return ExecutingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", name));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet/Common/ObjectIdentifier.cs b/src/Renci.SshNet/Common/ObjectIdentifier.cs
index f1b2d88df..a02a40908 100644
--- a/src/Renci.SshNet/Common/ObjectIdentifier.cs
+++ b/src/Renci.SshNet/Common/ObjectIdentifier.cs
@@ -1,4 +1,6 @@
using System;
+using System.Linq;
+using System.Security.Cryptography;
namespace Renci.SshNet.Common
{
@@ -32,5 +34,19 @@ public ObjectIdentifier(params ulong[] 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);
+ }
}
}
diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs
index e7bd6cdd2..a8089900a 100644
--- a/src/Renci.SshNet/ConnectionInfo.cs
+++ b/src/Renci.SshNet/ConnectionInfo.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
+using System.Security.Cryptography;
using System.Text;
using Renci.SshNet.Abstractions;
@@ -9,6 +10,7 @@
using Renci.SshNet.Messages.Authentication;
using Renci.SshNet.Messages.Connection;
using Renci.SshNet.Security;
+using Renci.SshNet.Security.Cryptography;
using Renci.SshNet.Security.Cryptography.Ciphers;
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
@@ -396,6 +398,8 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
{ "ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data) },
{ "ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data) },
{ "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) },
+ { "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-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) },
};
diff --git a/src/Renci.SshNet/IHostAlgorithmsProvider.cs b/src/Renci.SshNet/IHostAlgorithmsProvider.cs
new file mode 100644
index 000000000..89ac27128
--- /dev/null
+++ b/src/Renci.SshNet/IHostAlgorithmsProvider.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+using Renci.SshNet.Security;
+
+namespace Renci.SshNet
+{
+ ///
+ /// Represents a collection of host algorithms.
+ ///
+ public interface IHostAlgorithmsProvider
+ {
+ ///
+ /// The host algorithms provided by this .
+ ///
+ ///
+ /// In situations where there is a preferred order of usage of the host algorithms,
+ /// the collection should be ordered from most preferred to least.
+ ///
+ IReadOnlyCollection HostAlgorithms { get; }
+ }
+}
diff --git a/src/Renci.SshNet/IPrivateKeySource.cs b/src/Renci.SshNet/IPrivateKeySource.cs
index fc3405462..466a36d2a 100644
--- a/src/Renci.SshNet/IPrivateKeySource.cs
+++ b/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
{
///
/// Represents private key source interface.
///
- public interface IPrivateKeySource
+ ///
+ /// This interface has been replaced by
+ /// and is obsolete.
+ ///
+ [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
{
///
/// Gets the host key.
///
HostAlgorithm HostKey { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet/NetConfClient.cs b/src/Renci.SshNet/NetConfClient.cs
index bbedca3c2..95cc99f7d 100644
--- a/src/Renci.SshNet/NetConfClient.cs
+++ b/src/Renci.SshNet/NetConfClient.cs
@@ -107,7 +107,7 @@ public NetConfClient(string host, string username, string password)
/// is invalid, -or- is null or contains only whitespace characters.
/// is not within and .
[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)
{
}
@@ -120,7 +120,7 @@ public NetConfClient(string host, int port, string username, params IPrivateKeyS
/// Authentication private key file(s) .
/// is null.
/// is invalid, -or- is null or contains only whitespace characters.
- public NetConfClient(string host, string username, params IPrivateKeySource[] keyFiles)
+ public NetConfClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
diff --git a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
index 960a68b20..cb82cd7ac 100644
--- a/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
+++ b/src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Linq;
using System.Threading;
using Renci.SshNet.Common;
@@ -30,7 +31,7 @@ public override string Name
///
/// Gets the key files used for authentication.
///
- public ICollection KeyFiles { get; private set; }
+ public ICollection KeyFiles { get; private set; }
///
/// Initializes a new instance of the class.
@@ -38,7 +39,7 @@ public override string Name
/// The username.
/// The key files.
/// is whitespace or null.
- public PrivateKeyAuthenticationMethod(string username, params IPrivateKeySource[] keyFiles)
+ public PrivateKeyAuthenticationMethod(string username, params IHostAlgorithmsProvider[] keyFiles)
: base(username)
{
if (keyFiles is null)
@@ -46,7 +47,7 @@ public PrivateKeyAuthenticationMethod(string username, params IPrivateKeySource[
throw new ArgumentNullException(nameof(keyFiles));
}
- KeyFiles = new Collection(keyFiles);
+ KeyFiles = new Collection(keyFiles);
}
///
@@ -64,24 +65,26 @@ public override AuthenticationResult Authenticate(Session session)
session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK");
+ var hostAlgorithms = KeyFiles.SelectMany(x => x.HostAlgorithms).ToList();
+
try
{
- foreach (var keyFile in KeyFiles)
+ foreach (var hostAlgorithm in hostAlgorithms)
{
_ = _authenticationCompleted.Reset();
_isSignatureRequired = false;
var message = new RequestMessagePublicKey(ServiceName.Connection,
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
var signatureData = new SignatureData(message, session.SessionId).GetBytes();
- message.Signature = keyFile.HostKey.Sign(signatureData);
+ message.Signature = hostAlgorithm.Sign(signatureData);
}
// Send public key authentication request
@@ -95,12 +98,12 @@ public override AuthenticationResult Authenticate(Session session)
var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection,
Username,
- keyFile.HostKey.Name,
- keyFile.HostKey.Data);
+ hostAlgorithm.Name,
+ hostAlgorithm.Data);
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
session.SendMessage(signatureMessage);
@@ -108,7 +111,7 @@ public override AuthenticationResult Authenticate(Session session)
session.WaitOnHandle(_authenticationCompleted);
- if (_authenticationResult == AuthenticationResult.Success)
+ if (_authenticationResult is AuthenticationResult.Success or AuthenticationResult.PartialSuccess)
{
break;
}
diff --git a/src/Renci.SshNet/PrivateKeyConnectionInfo.cs b/src/Renci.SshNet/PrivateKeyConnectionInfo.cs
index 29b48ffaa..5ea0dacfd 100644
--- a/src/Renci.SshNet/PrivateKeyConnectionInfo.cs
+++ b/src/Renci.SshNet/PrivateKeyConnectionInfo.cs
@@ -17,7 +17,7 @@ public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable
///
/// Gets the key files used for authentication.
///
- public ICollection KeyFiles { get; private set; }
+ public ICollection KeyFiles { get; private set; }
///
/// Initializes a new instance of the class.
@@ -41,7 +41,7 @@ public PrivateKeyConnectionInfo(string host, string username, params PrivateKeyF
/// Connection port.
/// Connection username.
/// Connection key files.
- 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)
{
}
@@ -56,7 +56,7 @@ public PrivateKeyConnectionInfo(string host, int port, string username, params I
/// The proxy host.
/// The proxy port.
/// The key files.
- 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)
{
}
@@ -72,7 +72,7 @@ public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTyp
/// The proxy port.
/// The proxy username.
/// The key files.
- 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)
{
}
@@ -86,7 +86,7 @@ public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTyp
/// The proxy host.
/// The proxy port.
/// The key files.
- 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)
{
}
@@ -101,7 +101,7 @@ public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyTy
/// The proxy port.
/// The proxy username.
/// The key files.
- 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)
{
}
@@ -117,7 +117,7 @@ public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyTy
/// The proxy username.
/// The proxy password.
/// The key files.
- 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)
{
}
@@ -134,10 +134,10 @@ public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyTy
/// The proxy username.
/// The proxy password.
/// The key files.
- 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))
{
- KeyFiles = new Collection(keyFiles);
+ KeyFiles = new Collection(keyFiles);
}
///
diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs
index 3a2dbe049..ebd50fa42 100644
--- a/src/Renci.SshNet/PrivateKeyFile.cs
+++ b/src/Renci.SshNet/PrivateKeyFile.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
using System.IO;
+using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
@@ -63,18 +65,48 @@ namespace Renci.SshNet
///
///
///
- 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 (?\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k PRIVATE KEY *-+",
RegexOptions.Compiled | RegexOptions.Multiline);
+ private readonly List _hostAlgorithms = new List();
private Key _key;
private bool _isDisposed;
///
/// Gets the host key.
///
- public HostAlgorithm HostKey { get; private set; }
+ ///
+ /// This property returns the first item in .
+ ///
+ 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);
+ }
+ }
+
+ ///
+ /// The supported host algorithms for this key file.
+ ///
+ public IReadOnlyCollection HostAlgorithms
+ {
+ get
+ {
+ return _hostAlgorithms;
+ }
+ }
///
/// Initializes a new instance of the class.
@@ -92,6 +124,7 @@ public PrivateKeyFile(Key key)
public PrivateKeyFile(Stream privateKey)
{
Open(privateKey, passPhrase: null);
+ Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKey)} is not set.");
}
///
@@ -127,6 +160,8 @@ public PrivateKeyFile(string fileName, string passPhrase)
{
Open(keyFile, passPhrase);
}
+
+ Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKey)} is not set.");
}
///
@@ -138,6 +173,7 @@ public PrivateKeyFile(string fileName, string passPhrase)
public PrivateKeyFile(Stream privateKey, string passPhrase)
{
Open(privateKey, passPhrase);
+ Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKey)} is not set.");
}
///
@@ -222,8 +258,11 @@ private void Open(Stream privateKey, string passPhrase)
switch (keyName)
{
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;
case "DSA":
_key = new DsaKey(decryptedData);
@@ -235,7 +274,17 @@ private void Open(Stream privateKey, string passPhrase)
break;
case "OPENSSH":
_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;
case "SSH2 ENCRYPTED":
var reader = new SshDataReader(decryptedData);
@@ -290,8 +339,11 @@ private void Open(Stream privateKey, string passPhrase)
var inverseQ = reader.ReadBigIntWithBits(); // u
var q = reader.ReadBigIntWithBits(); // p
var p = reader.ReadBigIntWithBits(); // q
- _key = new RsaKey(modulus, exponent, d, p, q, inverseQ);
- HostKey = new KeyHostAlgorithm("ssh-rsa", _key);
+ 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}}")
{
diff --git a/src/Renci.SshNet/ScpClient.cs b/src/Renci.SshNet/ScpClient.cs
index 177a83bb5..fc12413de 100644
--- a/src/Renci.SshNet/ScpClient.cs
+++ b/src/Renci.SshNet/ScpClient.cs
@@ -146,7 +146,7 @@ public ScpClient(string host, string username, string password)
/// is invalid, -or- is null or contains only whitespace characters.
/// is not within and .
[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)
{
}
@@ -159,7 +159,7 @@ public ScpClient(string host, int port, string username, params IPrivateKeySourc
/// Authentication private key file(s) .
/// is null.
/// is invalid, -or- is null or contains only whitespace characters.
- public ScpClient(string host, string username, params IPrivateKeySource[] keyFiles)
+ public ScpClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
diff --git a/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs b/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs
index fda7fc5b5..749083c03 100644
--- a/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs
@@ -8,8 +8,6 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
///
public class RsaCipher : AsymmetricCipher
{
- private readonly bool _isPrivate;
-
private readonly RsaKey _key;
///
@@ -24,7 +22,6 @@ public RsaCipher(RsaKey key)
}
_key = key;
- _isPrivate = !_key.D.IsZero;
}
///
@@ -116,7 +113,9 @@ private byte[] Transform(byte[] data, int offset, int length)
BigInteger result;
- if (_isPrivate)
+ var isPrivate = !_key.D.IsZero;
+
+ if (isPrivate)
{
var random = BigInteger.One;
var max = _key.Modulus - 1;
diff --git a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs
index d9664bdd9..1c239aebe 100644
--- a/src/Renci.SshNet/Security/Cryptography/DsaKey.cs
+++ b/src/Renci.SshNet/Security/Cryptography/DsaKey.cs
@@ -84,7 +84,7 @@ public override int KeyLength
///
/// Gets the digital signature.
///
- protected override DigitalSignature DigitalSignature
+ protected internal override DigitalSignature DigitalSignature
{
get
{
diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
index 99892fa59..84dc61178 100644
--- a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
+++ b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
@@ -62,7 +62,7 @@ public override int KeyLength
///
/// Gets the digital signature.
///
- protected override DigitalSignature DigitalSignature
+ protected internal override DigitalSignature DigitalSignature
{
get
{
diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
index 0958ef0ce..f6bd50bd0 100644
--- a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
+++ b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
@@ -128,7 +128,7 @@ public override int KeyLength
///
/// Gets the digital signature.
///
- protected override DigitalSignature DigitalSignature
+ protected internal override DigitalSignature DigitalSignature
{
get
{
diff --git a/src/Renci.SshNet/Security/Cryptography/Key.cs b/src/Renci.SshNet/Security/Cryptography/Key.cs
index e23673b1e..fcc01efbc 100644
--- a/src/Renci.SshNet/Security/Cryptography/Key.cs
+++ b/src/Renci.SshNet/Security/Cryptography/Key.cs
@@ -17,9 +17,9 @@ public abstract class Key
protected BigInteger[] _privateKey;
///
- /// Gets the key specific digital signature.
+ /// Gets the default digital signature implementation for this key.
///
- protected abstract DigitalSignature DigitalSignature { get; }
+ protected internal abstract DigitalSignature DigitalSignature { get; }
///
/// Gets or sets the public key.
diff --git a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
index 9af398b46..790a0da64 100644
--- a/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
+++ b/src/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs
@@ -1,6 +1,5 @@
using System;
using System.Security.Cryptography;
-using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography.Ciphers;
@@ -14,13 +13,23 @@ public class RsaDigitalSignature : CipherDigitalSignature, IDisposable
private HashAlgorithm _hash;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class with the SHA-1 hash algorithm.
///
/// The RSA key.
public RsaDigitalSignature(RsaKey rsaKey)
- : base(new ObjectIdentifier(1, 3, 14, 3, 2, 26), new RsaCipher(rsaKey))
+ : this(rsaKey, HashAlgorithmName.SHA1)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The RSA key.
+ /// The hash algorithm to use in the digital signature.
+ 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));
}
///
diff --git a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs
index dbb5ebb02..7b088ea6e 100644
--- a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs
+++ b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs
@@ -153,10 +153,14 @@ public override int KeyLength
}
private RsaDigitalSignature _digitalSignature;
+
///
- /// Gets the digital signature.
+ ///
///
- protected override DigitalSignature DigitalSignature
+ ///
+ /// An implementation of an RSA digital signature using the SHA-1 hash algorithm.
+ ///
+ protected internal override DigitalSignature DigitalSignature
{
get
{
diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs
index 771ac6bf4..96a912b66 100644
--- a/src/Renci.SshNet/Security/KeyExchange.cs
+++ b/src/Renci.SshNet/Security/KeyExchange.cs
@@ -318,6 +318,28 @@ protected bool CanTrustHostKey(KeyHostAlgorithm host)
/// true if exchange hash is valid; otherwise false.
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;
+ }
+
///
/// Calculates key exchange hash value.
///
diff --git a/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs b/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
index 836c607f3..5cdba5b4d 100644
--- a/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
+++ b/src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
@@ -1,5 +1,4 @@
using System;
-using System.Text;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
@@ -72,20 +71,7 @@ internal abstract class KeyExchangeDiffieHellman : KeyExchange
///
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);
}
///
diff --git a/src/Renci.SshNet/Security/KeyExchangeEC.cs b/src/Renci.SshNet/Security/KeyExchangeEC.cs
index 864f55d2f..f2ae22fb7 100644
--- a/src/Renci.SshNet/Security/KeyExchangeEC.cs
+++ b/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
{
@@ -76,20 +73,7 @@ protected override byte[] CalculateHash()
///
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);
}
///
diff --git a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs
index aad8e20c1..f0d2b41d8 100644
--- a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs
+++ b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs
@@ -1,6 +1,9 @@
using System.Collections.Generic;
+using System.Text;
+
using Renci.SshNet.Common;
using Renci.SshNet.Security.Chaos.NaCl;
+using Renci.SshNet.Security.Cryptography;
namespace Renci.SshNet.Security
{
@@ -10,38 +13,76 @@ namespace Renci.SshNet.Security
public class KeyHostAlgorithm : HostAlgorithm
{
///
- /// Gets the key.
+ /// The key used in this host key algorithm.
///
public Key Key { get; private set; }
///
- /// Gets the public key data.
+ /// The signature implementation used in this host key algorithm.
+ ///
+ public DigitalSignature DigitalSignature { get; private set; }
+
+ ///
+ /// Gets the encoded public key data.
///
public override byte[] Data
{
get
{
- return new SshKeyData(Name, Key.Public).GetBytes();
+ var keyFormatIdentifier = Key is RsaKey ? "ssh-rsa" : Name;
+ return new SshKeyData(keyFormatIdentifier, Key.Public).GetBytes();
}
}
///
/// Initializes a new instance of the class.
///
- /// Host key name.
- /// Host key.
+ /// The signature format identifier.
+ ///
+ ///
+ /// This constructor is typically passed a private key in order to create an encoded signature for later
+ /// verification by the host.
+ ///
public KeyHostAlgorithm(string name, Key key)
: base(name)
{
Key = key;
+ DigitalSignature = key.DigitalSignature;
}
///
/// Initializes a new instance of the class.
///
- /// Host key name.
- /// Host key.
+ /// The signature format identifier.
+ ///
+ ///
+ ///
+ ///
+ /// This constructor is typically passed a private key in order to create an encoded signature for later
+ /// verification by the host.
+ ///
+ /// The key used by is intended to be equal to .
+ /// This is not verified.
+ ///
+ public KeyHostAlgorithm(string name, Key key, DigitalSignature digitalSignature)
+ : base(name)
+ {
+ Key = key;
+ DigitalSignature = digitalSignature;
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with the given encoded public key data. The data will be decoded into .
+ ///
+ /// The signature format identifier.
+ ///
/// Host key encoded data.
+ ///
+ /// This constructor is typically passed a new or reusable instance in
+ /// order to verify an encoded signature sent by the host, created by the private counterpart
+ /// to the host's public key, which is encoded in .
+ ///
public KeyHostAlgorithm(string name, Key key, byte[] data)
: base(name)
{
@@ -50,34 +91,66 @@ public KeyHostAlgorithm(string name, Key key, byte[] data)
var sshKey = new SshKeyData();
sshKey.Load(data);
Key.Public = sshKey.Keys;
+
+ DigitalSignature = key.DigitalSignature;
}
///
- /// Signs the specified data.
+ /// Initializes a new instance of the class
+ /// with the given encoded public key data. The data will be decoded into .
///
- /// The data.
+ /// The signature format identifier.
+ ///
+ /// Host key encoded data.
+ ///
+ ///
+ ///
+ /// This constructor is typically passed a new or reusable instance in
+ /// order to verify an encoded signature sent by the host, created by the private counterpart
+ /// to the host's public key, which is encoded in .
+ ///
+ /// The key used by is intended to be equal to .
+ /// This is not verified.
+ ///
+ public KeyHostAlgorithm(string name, Key key, byte[] data, DigitalSignature digitalSignature)
+ : base(name)
+ {
+ Key = key;
+
+ var sshKey = new SshKeyData();
+ sshKey.Load(data);
+ Key.Public = sshKey.Keys;
+
+ DigitalSignature = digitalSignature;
+ }
+
+ ///
+ /// Signs and encodes the specified data.
+ ///
+ /// The data to be signed.
///
- /// Signed data.
+ /// The encoded signature.
///
public override byte[] Sign(byte[] data)
{
- return new SignatureKeyData(Name, Key.Sign(data)).GetBytes();
+ return new SignatureKeyData(Name, DigitalSignature.Sign(data)).GetBytes();
}
///
/// Verifies the signature.
///
- /// The data.
- /// The signature.
+ /// The data to verify the signature against.
+ /// The encoded signature data.
///
- /// True is signature was successfully verifies; otherwise false.
+ /// if is the result of signing
+ /// with the corresponding private key to .
///
public override bool VerifySignature(byte[] data, byte[] signature)
{
var signatureData = new SignatureKeyData();
signatureData.Load(signature);
- return Key.VerifySignature(data, signatureData.Signature);
+ return DigitalSignature.Verify(data, signatureData.Signature);
}
private sealed class SshKeyData : SshData
@@ -170,15 +243,12 @@ protected override void SaveData()
}
}
- private sealed class SignatureKeyData : SshData
+ internal sealed class SignatureKeyData : SshData
{
///
- /// Gets or sets the name of the algorithm as UTF-8 encoded byte array.
+ /// Gets or sets the signature format identifier
///
- ///
- /// The name of the algorithm.
- ///
- private byte[] AlgorithmName { get; set; }
+ public string AlgorithmName { get; set; }
///
/// Gets the signature.
@@ -200,7 +270,7 @@ protected override int BufferCapacity
{
var capacity = base.BufferCapacity;
capacity += 4; // AlgorithmName length
- capacity += AlgorithmName.Length; // AlgorithmName
+ capacity += Encoding.UTF8.GetByteCount(AlgorithmName); // AlgorithmName
capacity += 4; // Signature length
capacity += Signature.Length; // Signature
return capacity;
@@ -213,7 +283,7 @@ public SignatureKeyData()
public SignatureKeyData(string name, byte[] signature)
{
- AlgorithmName = Utf8.GetBytes(name);
+ AlgorithmName = name;
Signature = signature;
}
@@ -222,7 +292,7 @@ public SignatureKeyData(string name, byte[] signature)
///
protected override void LoadData()
{
- AlgorithmName = ReadBinary();
+ AlgorithmName = Encoding.UTF8.GetString(ReadBinary());
Signature = ReadBinary();
}
@@ -231,7 +301,7 @@ protected override void LoadData()
///
protected override void SaveData()
{
- WriteBinaryString(AlgorithmName);
+ WriteBinaryString(Encoding.UTF8.GetBytes(AlgorithmName));
WriteBinaryString(Signature);
}
}
diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs
index e6f9cb2d6..b5f757b96 100644
--- a/src/Renci.SshNet/SftpClient.cs
+++ b/src/Renci.SshNet/SftpClient.cs
@@ -218,7 +218,7 @@ public SftpClient(string host, string username, string password)
/// is invalid. -or- is nunullll or contains only whitespace characters.
/// is not within and .
[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)
{
}
@@ -231,7 +231,7 @@ public SftpClient(string host, int port, string username, params IPrivateKeySour
/// Authentication private key file(s) .
/// is null.
/// is invalid. -or- is null or contains only whitespace characters.
- public SftpClient(string host, string username, params IPrivateKeySource[] keyFiles)
+ public SftpClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
diff --git a/src/Renci.SshNet/SshClient.cs b/src/Renci.SshNet/SshClient.cs
index fb8bb37f0..e596caaa0 100644
--- a/src/Renci.SshNet/SshClient.cs
+++ b/src/Renci.SshNet/SshClient.cs
@@ -103,7 +103,7 @@ public SshClient(string host, string username, string password)
/// is invalid, -or- is null or contains only whitespace characters.
/// is not within and .
[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)
{
}
@@ -120,7 +120,7 @@ public SshClient(string host, int port, string username, params IPrivateKeySourc
///
/// is null.
/// is invalid, -or- is null or contains only whitespace characters.
- public SshClient(string host, string username, params IPrivateKeySource[] keyFiles)
+ public SshClient(string host, string username, params IHostAlgorithmsProvider[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}