@@ -19,6 +19,7 @@ namespace Renci.SshNet.Security
1919 public abstract class KeyExchange : Algorithm , IKeyExchange
2020 {
2121 private readonly ILogger _logger ;
22+ private Func < byte [ ] , KeyHostAlgorithm > _hostKeyAlgorithmFactory ;
2223 private CipherInfo _clientCipherInfo ;
2324 private CipherInfo _serverCipherInfo ;
2425 private HashInfo _clientHashInfo ;
@@ -81,6 +82,33 @@ public virtual void Start(Session session, KeyExchangeInitMessage message, bool
8182 SendMessage ( session . ClientInitMessage ) ;
8283 }
8384
85+ // Determine host key algorithm
86+ var hostKeyAlgorithmName = ( from b in session . ConnectionInfo . HostKeyAlgorithms . Keys
87+ from a in message . ServerHostKeyAlgorithms
88+ where a == b
89+ select a ) . FirstOrDefault ( ) ;
90+
91+ if ( _logger . IsEnabled ( LogLevel . Trace ) )
92+ {
93+ _logger . LogTrace ( "[{SessionId}] Host key algorithm: we offer {WeOffer}" ,
94+ Session . SessionIdHex ,
95+ session . ConnectionInfo . HostKeyAlgorithms . Keys . Join ( "," ) ) ;
96+
97+ _logger . LogTrace ( "[{SessionId}] Host key algorithm: they offer {TheyOffer}" ,
98+ Session . SessionIdHex ,
99+ message . ServerHostKeyAlgorithms . Join ( "," ) ) ;
100+ }
101+
102+ if ( hostKeyAlgorithmName is null )
103+ {
104+ throw new SshConnectionException (
105+ $ "No matching host key algorithm (server offers { message . ServerHostKeyAlgorithms . Join ( "," ) } )",
106+ DisconnectReason . KeyExchangeFailed ) ;
107+ }
108+
109+ session . ConnectionInfo . CurrentHostKeyAlgorithm = hostKeyAlgorithmName ;
110+ _hostKeyAlgorithmFactory = session . ConnectionInfo . HostKeyAlgorithms [ hostKeyAlgorithmName ] ;
111+
84112 // Determine client encryption algorithm
85113 var clientEncryptionAlgorithmName = ( from b in session . ConnectionInfo . Encryptions . Keys
86114 from a in message . EncryptionAlgorithmsClientToServer
@@ -98,9 +126,11 @@ from a in message.EncryptionAlgorithmsClientToServer
98126 message . EncryptionAlgorithmsClientToServer . Join ( "," ) ) ;
99127 }
100128
101- if ( string . IsNullOrEmpty ( clientEncryptionAlgorithmName ) )
129+ if ( clientEncryptionAlgorithmName is null )
102130 {
103- throw new SshConnectionException ( "Client encryption algorithm not found" , DisconnectReason . KeyExchangeFailed ) ;
131+ throw new SshConnectionException (
132+ $ "No matching client encryption algorithm (server offers { message . EncryptionAlgorithmsClientToServer . Join ( "," ) } )",
133+ DisconnectReason . KeyExchangeFailed ) ;
104134 }
105135
106136 session . ConnectionInfo . CurrentClientEncryption = clientEncryptionAlgorithmName ;
@@ -123,9 +153,11 @@ from a in message.EncryptionAlgorithmsServerToClient
123153 message . EncryptionAlgorithmsServerToClient . Join ( "," ) ) ;
124154 }
125155
126- if ( string . IsNullOrEmpty ( serverDecryptionAlgorithmName ) )
156+ if ( serverDecryptionAlgorithmName is null )
127157 {
128- throw new SshConnectionException ( "Server decryption algorithm not found" , DisconnectReason . KeyExchangeFailed ) ;
158+ throw new SshConnectionException (
159+ $ "No matching server encryption algorithm (server offers { message . EncryptionAlgorithmsServerToClient . Join ( "," ) } )",
160+ DisconnectReason . KeyExchangeFailed ) ;
129161 }
130162
131163 session . ConnectionInfo . CurrentServerEncryption = serverDecryptionAlgorithmName ;
@@ -150,9 +182,11 @@ from a in message.MacAlgorithmsClientToServer
150182 message . MacAlgorithmsClientToServer . Join ( "," ) ) ;
151183 }
152184
153- if ( string . IsNullOrEmpty ( clientHmacAlgorithmName ) )
185+ if ( clientHmacAlgorithmName is null )
154186 {
155- throw new SshConnectionException ( "Client HMAC algorithm not found" , DisconnectReason . KeyExchangeFailed ) ;
187+ throw new SshConnectionException (
188+ $ "No matching client MAC algorithm (server offers { message . MacAlgorithmsClientToServer . Join ( "," ) } )",
189+ DisconnectReason . KeyExchangeFailed ) ;
156190 }
157191
158192 session . ConnectionInfo . CurrentClientHmacAlgorithm = clientHmacAlgorithmName ;
@@ -178,9 +212,11 @@ from a in message.MacAlgorithmsServerToClient
178212 message . MacAlgorithmsServerToClient . Join ( "," ) ) ;
179213 }
180214
181- if ( string . IsNullOrEmpty ( serverHmacAlgorithmName ) )
215+ if ( serverHmacAlgorithmName is null )
182216 {
183- throw new SshConnectionException ( "Server HMAC algorithm not found" , DisconnectReason . KeyExchangeFailed ) ;
217+ throw new SshConnectionException (
218+ $ "No matching server MAC algorithm (server offers { message . MacAlgorithmsServerToClient . Join ( "," ) } )",
219+ DisconnectReason . KeyExchangeFailed ) ;
184220 }
185221
186222 session . ConnectionInfo . CurrentServerHmacAlgorithm = serverHmacAlgorithmName ;
@@ -204,9 +240,11 @@ from a in message.CompressionAlgorithmsClientToServer
204240 message . CompressionAlgorithmsClientToServer . Join ( "," ) ) ;
205241 }
206242
207- if ( string . IsNullOrEmpty ( compressionAlgorithmName ) )
243+ if ( compressionAlgorithmName is null )
208244 {
209- throw new SshConnectionException ( "Compression algorithm not found" , DisconnectReason . KeyExchangeFailed ) ;
245+ throw new SshConnectionException (
246+ $ "No matching client compression algorithm (server offers { message . CompressionAlgorithmsClientToServer . Join ( "," ) } )",
247+ DisconnectReason . KeyExchangeFailed ) ;
210248 }
211249
212250 session . ConnectionInfo . CurrentClientCompressionAlgorithm = compressionAlgorithmName ;
@@ -229,9 +267,11 @@ from a in message.CompressionAlgorithmsServerToClient
229267 message . CompressionAlgorithmsServerToClient . Join ( "," ) ) ;
230268 }
231269
232- if ( string . IsNullOrEmpty ( decompressionAlgorithmName ) )
270+ if ( decompressionAlgorithmName is null )
233271 {
234- throw new SshConnectionException ( "Decompression algorithm not found" , DisconnectReason . KeyExchangeFailed ) ;
272+ throw new SshConnectionException (
273+ $ "No matching server compression algorithm (server offers { message . CompressionAlgorithmsServerToClient . Join ( "," ) } )",
274+ DisconnectReason . KeyExchangeFailed ) ;
235275 }
236276
237277 session . ConnectionInfo . CurrentServerCompressionAlgorithm = decompressionAlgorithmName ;
@@ -245,7 +285,7 @@ public virtual void Finish()
245285 {
246286 if ( ! ValidateExchangeHash ( ) )
247287 {
248- throw new SshConnectionException ( "Key exchange negotiation failed ." , DisconnectReason . KeyExchangeFailed ) ;
288+ throw new SshConnectionException ( "Host key could not be verified ." , DisconnectReason . KeyExchangeFailed ) ;
249289 }
250290
251291 SendMessage ( new NewKeysMessage ( ) ) ;
@@ -449,40 +489,9 @@ private protected bool ValidateExchangeHash(byte[] encodedKey, byte[] encodedSig
449489 {
450490 var exchangeHash = CalculateHash ( ) ;
451491
452- // We need to inspect both the key and signature format identifers to find the correct
453- // HostAlgorithm instance. Example cases:
454-
455- // Key identifier Signature identifier | Algorithm name
456- // ssh-rsa ssh-rsa | ssh-rsa
457- // ssh-rsa rsa-sha2-256 | rsa-sha2-256
458- // ssh-rsa-cert-v01@openssh.com ssh-rsa | ssh-rsa-cert-v01@openssh.com
459- // ssh-rsa-cert-v01@openssh.com rsa-sha2-256 | rsa-sha2-256-cert-v01@openssh.com
460-
461- var signatureData = new KeyHostAlgorithm . SignatureKeyData ( ) ;
462- signatureData . Load ( encodedSignature ) ;
463-
464- string keyName ;
465- using ( var keyReader = new SshDataStream ( encodedKey ) )
466- {
467- keyName = keyReader . ReadString ( ) ;
468- }
469-
470- string algorithmName ;
471-
472- if ( signatureData . AlgorithmName . StartsWith ( "rsa-sha2" , StringComparison . Ordinal ) )
473- {
474- algorithmName = keyName . Replace ( "ssh-rsa" , signatureData . AlgorithmName ) ;
475- }
476- else
477- {
478- algorithmName = keyName ;
479- }
480-
481- var keyAlgorithm = Session . ConnectionInfo . HostKeyAlgorithms [ algorithmName ] ( encodedKey ) ;
482-
483- Session . ConnectionInfo . CurrentHostKeyAlgorithm = algorithmName ;
492+ var keyAlgorithm = _hostKeyAlgorithmFactory ( encodedKey ) ;
484493
485- return keyAlgorithm . VerifySignatureBlob ( exchangeHash , signatureData . Signature ) && CanTrustHostKey ( keyAlgorithm ) ;
494+ return keyAlgorithm . VerifySignature ( exchangeHash , encodedSignature ) && CanTrustHostKey ( keyAlgorithm ) ;
486495 }
487496
488497 /// <summary>
0 commit comments