FingerPrints (#1186)

This commit is contained in:
Wojciech Nagórski
2023-09-26 21:00:20 +02:00
committed by GitHub
parent fdd113022b
commit 4ba591ee0b
5 changed files with 165 additions and 11 deletions
@@ -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;
}
}
}
@@ -380,6 +380,53 @@ namespace Renci.SshNet.IntegrationTests
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>
+3 -1
View File
@@ -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 && \
@@ -45,4 +47,4 @@ RUN apk update && apk upgrade --no-cache && \
EXPOSE 22 22
ENTRYPOINT ["/opt/sshnet/start.sh"]
ENTRYPOINT ["/opt/sshnet/start.sh"]
@@ -56,6 +56,8 @@ namespace Renci.SshNet.Tests.Classes.Common
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>
+47 -10
View File
@@ -1,4 +1,5 @@
using System;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Security;
@@ -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>
@@ -33,15 +38,42 @@ namespace Renci.SshNet.Common
/// <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.
@@ -61,16 +93,21 @@ namespace Renci.SshNet.Common
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());
}
}
}