Skip to content

Commit

Permalink
Add support for RSA SHA-2 public key algorithms (#1177)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
Rob-Hague and WojciechNagorski authored Sep 23, 2023
1 parent 43329ee commit 8732d3d
Show file tree
Hide file tree
Showing 34 changed files with 767 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 0 additions & 1 deletion src/Renci.SshNet.IntegrationTests/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 && \
Expand Down
72 changes: 25 additions & 47 deletions src/Renci.SshNet.IntegrationTests/HostKeyAlgorithmTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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);
}
}
}
65 changes: 43 additions & 22 deletions src/Renci.SshNet.IntegrationTests/PrivateKeyAuthenticationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Renci.SshNet.IntegrationTests
{
[TestClass]
public class PrivateKeyAuthenticationTests : TestBase
{
{
private IConnectionInfoFactory _connectionInfoFactory;
private RemoteSshdConfig _remoteSshdConfig;

Expand All @@ -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))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<EmbeddedResource Include="resources\client\key_ecdsa_256_openssh" />
<EmbeddedResource Include="resources\client\key_ecdsa_384_openssh" />
<EmbeddedResource Include="resources\client\key_ecdsa_521_openssh" />
<EmbeddedResource Include="resources\client\key_ed25519_openssh" />
<EmbeddedResource Include="resources\issue #70.png" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -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==
ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ==
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAkNGPVOTuzuKTgGfHcve2MRj57yXhmZgkUyi9RpmJrl
7 changes: 5 additions & 2 deletions src/Renci.SshNet.TestTools.OpenSSH/SshdConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
36 changes: 26 additions & 10 deletions src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -144,7 +147,7 @@ public void Test_PrivateKey_RSA()
{
using (var stream = GetData("Key.RSA.txt"))
{
_ = new PrivateKeyFile(stream);
TestRsaKeyFile(new PrivateKeyFile(stream));
}
}

Expand All @@ -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));
}
}

Expand All @@ -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"));
}
}

Expand Down Expand Up @@ -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"));
}
}

Expand All @@ -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"));
}
}

Expand All @@ -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"));
}
}

Expand All @@ -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"));
}
}

Expand All @@ -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"));
}
}

Expand Down Expand Up @@ -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));
}
}

Expand All @@ -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"));
}
}

Expand Down Expand Up @@ -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<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);
}
}
}
Loading

0 comments on commit 8732d3d

Please sign in to comment.