@@ -19,6 +19,7 @@ namespace Renci.SshNet.Security
19
19
public abstract class KeyExchange : Algorithm , IKeyExchange
20
20
{
21
21
private readonly ILogger _logger ;
22
+ private Func < byte [ ] , KeyHostAlgorithm > _hostKeyAlgorithmFactory ;
22
23
private CipherInfo _clientCipherInfo ;
23
24
private CipherInfo _serverCipherInfo ;
24
25
private HashInfo _clientHashInfo ;
@@ -81,6 +82,33 @@ public virtual void Start(Session session, KeyExchangeInitMessage message, bool
81
82
SendMessage ( session . ClientInitMessage ) ;
82
83
}
83
84
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
+
84
112
// Determine client encryption algorithm
85
113
var clientEncryptionAlgorithmName = ( from b in session . ConnectionInfo . Encryptions . Keys
86
114
from a in message . EncryptionAlgorithmsClientToServer
@@ -98,9 +126,11 @@ from a in message.EncryptionAlgorithmsClientToServer
98
126
message . EncryptionAlgorithmsClientToServer . Join ( "," ) ) ;
99
127
}
100
128
101
- if ( string . IsNullOrEmpty ( clientEncryptionAlgorithmName ) )
129
+ if ( clientEncryptionAlgorithmName is null )
102
130
{
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 ) ;
104
134
}
105
135
106
136
session . ConnectionInfo . CurrentClientEncryption = clientEncryptionAlgorithmName ;
@@ -123,9 +153,11 @@ from a in message.EncryptionAlgorithmsServerToClient
123
153
message . EncryptionAlgorithmsServerToClient . Join ( "," ) ) ;
124
154
}
125
155
126
- if ( string . IsNullOrEmpty ( serverDecryptionAlgorithmName ) )
156
+ if ( serverDecryptionAlgorithmName is null )
127
157
{
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 ) ;
129
161
}
130
162
131
163
session . ConnectionInfo . CurrentServerEncryption = serverDecryptionAlgorithmName ;
@@ -150,9 +182,11 @@ from a in message.MacAlgorithmsClientToServer
150
182
message . MacAlgorithmsClientToServer . Join ( "," ) ) ;
151
183
}
152
184
153
- if ( string . IsNullOrEmpty ( clientHmacAlgorithmName ) )
185
+ if ( clientHmacAlgorithmName is null )
154
186
{
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 ) ;
156
190
}
157
191
158
192
session . ConnectionInfo . CurrentClientHmacAlgorithm = clientHmacAlgorithmName ;
@@ -178,9 +212,11 @@ from a in message.MacAlgorithmsServerToClient
178
212
message . MacAlgorithmsServerToClient . Join ( "," ) ) ;
179
213
}
180
214
181
- if ( string . IsNullOrEmpty ( serverHmacAlgorithmName ) )
215
+ if ( serverHmacAlgorithmName is null )
182
216
{
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 ) ;
184
220
}
185
221
186
222
session . ConnectionInfo . CurrentServerHmacAlgorithm = serverHmacAlgorithmName ;
@@ -204,9 +240,11 @@ from a in message.CompressionAlgorithmsClientToServer
204
240
message . CompressionAlgorithmsClientToServer . Join ( "," ) ) ;
205
241
}
206
242
207
- if ( string . IsNullOrEmpty ( compressionAlgorithmName ) )
243
+ if ( compressionAlgorithmName is null )
208
244
{
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 ) ;
210
248
}
211
249
212
250
session . ConnectionInfo . CurrentClientCompressionAlgorithm = compressionAlgorithmName ;
@@ -229,9 +267,11 @@ from a in message.CompressionAlgorithmsServerToClient
229
267
message . CompressionAlgorithmsServerToClient . Join ( "," ) ) ;
230
268
}
231
269
232
- if ( string . IsNullOrEmpty ( decompressionAlgorithmName ) )
270
+ if ( decompressionAlgorithmName is null )
233
271
{
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 ) ;
235
275
}
236
276
237
277
session . ConnectionInfo . CurrentServerCompressionAlgorithm = decompressionAlgorithmName ;
@@ -245,7 +285,7 @@ public virtual void Finish()
245
285
{
246
286
if ( ! ValidateExchangeHash ( ) )
247
287
{
248
- throw new SshConnectionException ( "Key exchange negotiation failed ." , DisconnectReason . KeyExchangeFailed ) ;
288
+ throw new SshConnectionException ( "Host key could not be verified ." , DisconnectReason . KeyExchangeFailed ) ;
249
289
}
250
290
251
291
SendMessage ( new NewKeysMessage ( ) ) ;
@@ -449,40 +489,9 @@ private protected bool ValidateExchangeHash(byte[] encodedKey, byte[] encodedSig
449
489
{
450
490
var exchangeHash = CalculateHash ( ) ;
451
491
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
-
459
-
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 ) ;
484
493
485
- return keyAlgorithm . VerifySignatureBlob ( exchangeHash , signatureData . Signature ) && CanTrustHostKey ( keyAlgorithm ) ;
494
+ return keyAlgorithm . VerifySignature ( exchangeHash , encodedSignature ) && CanTrustHostKey ( keyAlgorithm ) ;
486
495
}
487
496
488
497
/// <summary>
0 commit comments