Skip to content

Commit

Permalink
FingerPrints (#1186)
Browse files Browse the repository at this point in the history
  • Loading branch information
WojciechNagorski authored Sep 26, 2023
1 parent fdd1130 commit 4ba591e
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 11 deletions.
66 changes: 66 additions & 0 deletions src/Renci.SshNet.Benchmarks/Common/HostKeyEventArgsBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using BenchmarkDotNet.Attributes;

using Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers;
using Renci.SshNet.Common;
using Renci.SshNet.Security;

namespace Renci.SshNet.Benchmarks.Common
{
[MemoryDiagnoser]
[ShortRunJob]
public class HostKeyEventArgsBenchmarks
{
private readonly KeyHostAlgorithm _keyHostAlgorithm;

public HostKeyEventArgsBenchmarks()
{
_keyHostAlgorithm = GetKeyHostAlgorithm();
}
private static KeyHostAlgorithm GetKeyHostAlgorithm()
{
using (var s = typeof(RsaCipherBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.RSA.txt"))
{
var privateKey = new PrivateKeyFile(s);
return (KeyHostAlgorithm) privateKey.HostKeyAlgorithms.First();
}
}

[Benchmark()]
public HostKeyEventArgs Constructor()
{
return new HostKeyEventArgs(_keyHostAlgorithm);
}

[Benchmark()]
public (string, string) CalculateFingerPrintSHA256AndMD5()
{
var test = new HostKeyEventArgs(_keyHostAlgorithm);

return (test.FingerPrintSHA256, test.FingerPrintMD5);
}

[Benchmark()]
public string CalculateFingerPrintSHA256()
{
var test = new HostKeyEventArgs(_keyHostAlgorithm);

return test.FingerPrintSHA256;
}

[Benchmark()]
public byte[] CalculateFingerPrint()
{
var test = new HostKeyEventArgs(_keyHostAlgorithm);

return test.FingerPrint;
}

[Benchmark()]
public string CalculateFingerPrintMD5()
{
var test = new HostKeyEventArgs(_keyHostAlgorithm);

return test.FingerPrintSHA256;
}
}
}
47 changes: 47 additions & 0 deletions src/Renci.SshNet.IntegrationTests/ConnectivityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,53 @@ public void Common_HostKeyValidation_Success()
Assert.IsTrue(hostValidationSuccessful);
}

[TestMethod]
public void Common_HostKeyValidationSHA256_Success()
{
var hostValidationSuccessful = false;

using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.HostKeyReceived += (sender, e) =>
{
if (e.FingerPrintSHA256 == "9fa6vbz64gimzsGZ/xZi3aaYE1o7E96iU2NjcfQNGwI")
{
hostValidationSuccessful = e.CanTrust;
}
else
{
e.CanTrust = false;
}
};
client.Connect();
}

Assert.IsTrue(hostValidationSuccessful);
}

[TestMethod]
public void Common_HostKeyValidationMD5_Success()
{
var hostValidationSuccessful = false;

using (var client = new SshClient(_connectionInfoFactory.Create()))
{
client.HostKeyReceived += (sender, e) =>
{
if (e.FingerPrintMD5 == "3d:90:d8:0d:d5:e0:b6:13:42:7c:78:1e:19:a3:99:2b")
{
hostValidationSuccessful = e.CanTrust;
}
else
{
e.CanTrust = false;
}
};
client.Connect();
}

Assert.IsTrue(hostValidationSuccessful);
}
/// <summary>
/// Verifies whether we handle a disconnect initiated by the SSH server (through a SSH_MSG_DISCONNECT message).
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Renci.SshNet.IntegrationTests/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ 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 && \
# Set the default RSA key
echo 'HostKey /etc/ssh/ssh_host_rsa_key' >> /etc/ssh/sshd_config && \
chmod 646 /etc/ssh/sshd_config && \
# install and configure sudo
apk add --no-cache sudo && \
Expand Down Expand Up @@ -45,4 +47,4 @@ RUN apk update && apk upgrade --no-cache && \

EXPOSE 22 22

ENTRYPOINT ["/opt/sshnet/start.sh"]
ENTRYPOINT ["/opt/sshnet/start.sh"]
2 changes: 2 additions & 0 deletions src/Renci.SshNet.Tests/Classes/Common/HostKeyEventArgsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public void HostKeyEventArgsConstructorTest_VerifyMD5()
Assert.IsTrue(new byte[] {
0x92, 0xea, 0x54, 0xa1, 0x01, 0xf9, 0x95, 0x9c, 0x71, 0xd9, 0xbb, 0x51, 0xb2, 0x55, 0xf8, 0xd9
}.SequenceEqual(target.FingerPrint));
Assert.AreEqual("92:ea:54:a1:01:f9:95:9c:71:d9:bb:51:b2:55:f8:d9", target.FingerPrintMD5);

}

/// <summary>
Expand Down
57 changes: 47 additions & 10 deletions src/Renci.SshNet/Common/HostKeyEventArgs.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;

using Renci.SshNet.Abstractions;
using Renci.SshNet.Security;

Expand All @@ -9,6 +10,10 @@ namespace Renci.SshNet.Common
/// </summary>
public class HostKeyEventArgs : EventArgs
{
private readonly Lazy<byte[]> _lazyFingerPrint;
private readonly Lazy<string> _lazyFingerPrintSHA256;
private readonly Lazy<string> _lazyFingerPrintMD5;

/// <summary>
/// Gets or sets a value indicating whether host key can be trusted.
/// </summary>
Expand All @@ -33,15 +38,42 @@ public class HostKeyEventArgs : EventArgs
/// <value>
/// MD5 fingerprint as byte array.
/// </value>
public byte[] FingerPrint { get; private set; }
public byte[] FingerPrint
{
get
{
return _lazyFingerPrint.Value;
}
}

/// <summary>
/// Gets the SHA256 fingerprint.
/// Gets the SHA256 fingerprint of the host key in the same format as the ssh command,
/// i.e. non-padded base64, but without the <c>SHA256:</c> prefix.
/// </summary>
/// <example><c>ohD8VZEXGWo6Ez8GSEJQ9WpafgLFsOfLOtGGQCQo6Og</c></example>
/// <value>
/// Base64 encoded SHA256 fingerprint with padding (equals sign) removed.
/// </value>
public string FingerPrintSHA256 { get; private set; }
public string FingerPrintSHA256
{
get
{
return _lazyFingerPrintSHA256.Value;
}
}

/// <summary>
/// Gets the MD5 fingerprint of the host key in the same format as the ssh command,
/// i.e. hexadecimal bytes separated by colons, but without the <c>MD5:</c> prefix.
/// </summary>
/// <example><c>97:70:33:82:fd:29:3a:73:39:af:6a:07:ad:f8:80:49</c></example>
public string FingerPrintMD5
{
get
{
return _lazyFingerPrintMD5.Value;
}
}

/// <summary>
/// Gets the length of the key in bits.
Expand All @@ -61,16 +93,21 @@ public HostKeyEventArgs(KeyHostAlgorithm host)
HostKey = host.Data;
HostKeyName = host.Name;
KeyLength = host.Key.KeyLength;

using (var md5 = CryptoAbstraction.CreateMD5())
_lazyFingerPrint = new Lazy<byte[]>(() =>
{
FingerPrint = md5.ComputeHash(host.Data);
}
using var md5 = CryptoAbstraction.CreateMD5();
return md5.ComputeHash(HostKey);
});

using (var sha256 = CryptoAbstraction.CreateSHA256())
_lazyFingerPrintSHA256 = new Lazy<string>(() =>
{
FingerPrintSHA256 = Convert.ToBase64String(sha256.ComputeHash(host.Data)).Replace("=", "");
}
using var sha256 = CryptoAbstraction.CreateSHA256();
return Convert.ToBase64String(sha256.ComputeHash(HostKey)).Replace("=", "");
});

_lazyFingerPrintMD5 = new Lazy<string>(() =>
BitConverter.ToString(FingerPrint).Replace("-", ":").ToLowerInvariant());
}
}
}

0 comments on commit 4ba591e

Please sign in to comment.