<PackageReference Include="SSH.NET" Version="2024.2.0" />

KeyExchange

public abstract class KeyExchange : Algorithm, IKeyExchange, IDisposable
Represents base class for different key exchange algorithm implementations.
using Renci.SshNet.Common; using Renci.SshNet.Compression; using Renci.SshNet.Messages; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Security.Cryptography; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; namespace Renci.SshNet.Security { public abstract class KeyExchange : Algorithm, IKeyExchange, IDisposable { private sealed class SessionKeyGeneration : SshData { public byte[] SharedKey { get; set; } public byte[] ExchangeHash { get; set; } public char Char { get; set; } public byte[] SessionId { get; set; } protected override int BufferCapacity => base.BufferCapacity + 4 + SharedKey.Length + ExchangeHash.Length + 1 + SessionId.Length; protected override void LoadData() { throw new NotImplementedException(); } protected override void SaveData() { WriteBinaryString(SharedKey); Write(ExchangeHash); Write((byte)Char); Write(SessionId); } } private sealed class SessionKeyAdjustment : SshData { public byte[] SharedKey { get; set; } public byte[] ExchangeHash { get; set; } public byte[] Key { get; set; } protected override int BufferCapacity => base.BufferCapacity + 4 + SharedKey.Length + ExchangeHash.Length + Key.Length; protected override void LoadData() { throw new NotImplementedException(); } protected override void SaveData() { WriteBinaryString(SharedKey); Write(ExchangeHash); Write(Key); } } private CipherInfo _clientCipherInfo; private CipherInfo _serverCipherInfo; private HashInfo _clientHashInfo; private HashInfo _serverHashInfo; private Func<Compressor> _compressorFactory; private Func<Compressor> _decompressorFactory; private byte[] _exchangeHash; protected Session Session { get; set; } public byte[] SharedKey { get; set; } public byte[] ExchangeHash { get { if (_exchangeHash == null) _exchangeHash = CalculateHash(); return _exchangeHash; } } public event EventHandler<HostKeyEventArgs> HostKeyReceived; public virtual void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage) { Session = session; if (sendClientInitMessage) SendMessage(session.ClientInitMessage); string text = (from b in session.ConnectionInfo.Encryptions.Keys from a in message.EncryptionAlgorithmsClientToServer where a == b select a).FirstOrDefault(); if (string.IsNullOrEmpty(text)) throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed); session.ConnectionInfo.CurrentClientEncryption = text; _clientCipherInfo = session.ConnectionInfo.Encryptions[text]; string text2 = (from b in session.ConnectionInfo.Encryptions.Keys from a in message.EncryptionAlgorithmsServerToClient where a == b select a).FirstOrDefault(); if (string.IsNullOrEmpty(text2)) throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed); session.ConnectionInfo.CurrentServerEncryption = text2; _serverCipherInfo = session.ConnectionInfo.Encryptions[text2]; if (!_clientCipherInfo.IsAead) { string text3 = (from b in session.ConnectionInfo.HmacAlgorithms.Keys from a in message.MacAlgorithmsClientToServer where a == b select a).FirstOrDefault(); if (string.IsNullOrEmpty(text3)) throw new SshConnectionException("Client HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); session.ConnectionInfo.CurrentClientHmacAlgorithm = text3; _clientHashInfo = session.ConnectionInfo.HmacAlgorithms[text3]; } if (!_serverCipherInfo.IsAead) { string text4 = (from b in session.ConnectionInfo.HmacAlgorithms.Keys from a in message.MacAlgorithmsServerToClient where a == b select a).FirstOrDefault(); if (string.IsNullOrEmpty(text4)) throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); session.ConnectionInfo.CurrentServerHmacAlgorithm = text4; _serverHashInfo = session.ConnectionInfo.HmacAlgorithms[text4]; } string text5 = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsClientToServer where a == b select a).FirstOrDefault(); if (string.IsNullOrEmpty(text5)) throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed); session.ConnectionInfo.CurrentClientCompressionAlgorithm = text5; _compressorFactory = session.ConnectionInfo.CompressionAlgorithms[text5]; string text6 = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys from a in message.CompressionAlgorithmsServerToClient where a == b select a).FirstOrDefault(); if (string.IsNullOrEmpty(text6)) throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed); session.ConnectionInfo.CurrentServerCompressionAlgorithm = text6; _decompressorFactory = session.ConnectionInfo.CompressionAlgorithms[text6]; } public virtual void Finish() { if (!ValidateExchangeHash()) throw new SshConnectionException("Key exchange negotiation failed.", DisconnectReason.KeyExchangeFailed); SendMessage(new NewKeysMessage()); } public Cipher CreateServerCipher(out bool isAead) { isAead = _serverCipherInfo.IsAead; byte[] sessionId = Session.SessionId ?? ExchangeHash; byte[] arg = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'B', sessionId)); byte[] key = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'D', sessionId)); key = GenerateSessionKey(SharedKey, ExchangeHash, key, _serverCipherInfo.KeySize / 8); return _serverCipherInfo.Cipher(key, arg); } public Cipher CreateClientCipher(out bool isAead) { isAead = _clientCipherInfo.IsAead; byte[] sessionId = Session.SessionId ?? ExchangeHash; byte[] arg = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'A', sessionId)); byte[] key = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'C', sessionId)); key = GenerateSessionKey(SharedKey, ExchangeHash, key, _clientCipherInfo.KeySize / 8); return _clientCipherInfo.Cipher(key, arg); } public HashAlgorithm CreateServerHash(out bool isEncryptThenMAC) { if (_serverHashInfo == null) { isEncryptThenMAC = false; return null; } isEncryptThenMAC = _serverHashInfo.IsEncryptThenMAC; byte[] sessionId = Session.SessionId ?? ExchangeHash; byte[] arg = GenerateSessionKey(SharedKey, ExchangeHash, Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'F', sessionId)), _serverHashInfo.KeySize / 8); return _serverHashInfo.HashAlgorithm(arg); } public HashAlgorithm CreateClientHash(out bool isEncryptThenMAC) { if (_clientHashInfo == null) { isEncryptThenMAC = false; return null; } isEncryptThenMAC = _clientHashInfo.IsEncryptThenMAC; byte[] sessionId = Session.SessionId ?? ExchangeHash; byte[] arg = GenerateSessionKey(SharedKey, ExchangeHash, Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'E', sessionId)), _clientHashInfo.KeySize / 8); return _clientHashInfo.HashAlgorithm(arg); } public Compressor CreateCompressor() { if (_compressorFactory == null) return null; Compressor compressor = _compressorFactory(); compressor.Init(Session); return compressor; } public Compressor CreateDecompressor() { if (_decompressorFactory == null) return null; Compressor compressor = _decompressorFactory(); compressor.Init(Session); return compressor; } protected bool CanTrustHostKey(KeyHostAlgorithm host) { EventHandler<HostKeyEventArgs> hostKeyReceived = this.HostKeyReceived; if (hostKeyReceived != null) { HostKeyEventArgs hostKeyEventArgs = new HostKeyEventArgs(host); hostKeyReceived(this, hostKeyEventArgs); return hostKeyEventArgs.CanTrust; } return true; } protected abstract bool ValidateExchangeHash(); private protected bool ValidateExchangeHash(byte[] encodedKey, byte[] encodedSignature) { byte[] data = CalculateHash(); KeyHostAlgorithm.SignatureKeyData signatureKeyData = new KeyHostAlgorithm.SignatureKeyData(); signatureKeyData.Load(encodedSignature); string text = default(string); using (SshDataStream sshDataStream = new SshDataStream(encodedKey)) text = sshDataStream.ReadString(null); string text2 = (!signatureKeyData.AlgorithmName.StartsWith("rsa-sha2", StringComparison.Ordinal)) ? text : text.Replace("ssh-rsa", signatureKeyData.AlgorithmName); KeyHostAlgorithm keyHostAlgorithm = Session.ConnectionInfo.HostKeyAlgorithms[text2](encodedKey); Session.ConnectionInfo.CurrentHostKeyAlgorithm = text2; if (keyHostAlgorithm.VerifySignatureBlob(data, signatureKeyData.Signature)) return CanTrustHostKey(keyHostAlgorithm); return false; } protected abstract byte[] CalculateHash(); protected abstract byte[] Hash(byte[] hashData); protected void SendMessage(Message message) { Session.SendMessage(message); } private byte[] GenerateSessionKey(byte[] sharedKey, byte[] exchangeHash, byte[] key, int size) { List<byte> list = new List<byte>(key); while (size > list.Count) { SessionKeyAdjustment sessionKeyAdjustment = new SessionKeyAdjustment { SharedKey = sharedKey, ExchangeHash = exchangeHash, Key = key }; list.AddRange(Hash(sessionKeyAdjustment.GetBytes())); } return list.ToArray(); } private static byte[] GenerateSessionKey(byte[] sharedKey, byte[] exchangeHash, char p, byte[] sessionId) { return new SessionKeyGeneration { SharedKey = sharedKey, ExchangeHash = exchangeHash, Char = p, SessionId = sessionId }.GetBytes(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { } } }