mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 09:15:47 +00:00
FingerPrints (#1186)
This commit is contained in:
committed by
GitHub
parent
fdd113022b
commit
4ba591ee0b
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user