mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 09:15:47 +00:00
Refactor key exchange mechanism to handle key exchange messages issued by the server
This commit is contained in:
@@ -382,6 +382,10 @@ namespace Renci.SshClient.Channels
|
||||
{
|
||||
this._channelClosedWaitHandle.Dispose();
|
||||
}
|
||||
if (this._channelWindowAdjustWaitHandle != null)
|
||||
{
|
||||
this._channelWindowAdjustWaitHandle.Dispose();
|
||||
}
|
||||
|
||||
this.OnDisposing();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Renci.SshClient.Channels
|
||||
}
|
||||
|
||||
public ChannelSftp(Session session, uint channelId)
|
||||
: base(session, channelId, 0x100000, 0x4000)
|
||||
: base(session, channelId, 0x100000, 0x0100)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Runtime.Serialization;
|
||||
|
||||
namespace Renci.SshClient.Common
|
||||
{
|
||||
[Serializable]
|
||||
public class SshException : Exception
|
||||
{
|
||||
public bool ShouldDisconnect { get; private set; }
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
<Compile Include="Security\CryptoPublicKeyDss.cs" />
|
||||
<Compile Include="Security\CryptoPublicKeyRsa.cs" />
|
||||
<Compile Include="Security\KeyExchange.cs" />
|
||||
<Compile Include="Security\KeyExchangeAlgorithm.cs" />
|
||||
<Compile Include="Security\KeyExchangeCompletedEventArgs.cs" />
|
||||
<Compile Include="Security\KeyExchangeDiffieHellman.cs" />
|
||||
<Compile Include="Security\KeyExchangeFailedEventArgs.cs" />
|
||||
|
||||
@@ -1,39 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using Renci.SshClient.Common;
|
||||
using Renci.SshClient.Messages;
|
||||
using Renci.SshClient.Messages.Transport;
|
||||
|
||||
namespace Renci.SshClient.Security
|
||||
{
|
||||
internal abstract class KeyExchange : Algorithm
|
||||
internal class KeyExchange : Algorithm, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the key exchange algorithm to be used for key exchange.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <returns></returns>
|
||||
internal static KeyExchange Create(KeyExchangeInitMessage message, Session session)
|
||||
{
|
||||
|
||||
// TODO: Determine key exchange algorithm
|
||||
var keyExchangeAlgorithm = (from s in message.KeyExchangeAlgorithms
|
||||
from c in Settings.KeyExchangeAlgorithms.Keys
|
||||
where s == c
|
||||
select c).FirstOrDefault();
|
||||
|
||||
// TODO: If dont agree on algorithms then send disconnect message
|
||||
if (keyExchangeAlgorithm == null)
|
||||
{
|
||||
throw new InvalidDataException("Failed to negotiate key exchange algorithm.");
|
||||
}
|
||||
|
||||
return Settings.KeyExchangeAlgorithms[keyExchangeAlgorithm](session);
|
||||
}
|
||||
private KeyExchangeAlgorithm _keyExchangeAlgorithm;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies negotiated algorithm to encrypt information when sent to the server
|
||||
@@ -55,95 +34,110 @@ namespace Renci.SshClient.Security
|
||||
/// </summary>
|
||||
private Func<IEnumerable<byte>, HMAC> _serverHmacAlgorithm;
|
||||
|
||||
private IEnumerable<byte> _exchangeHash;
|
||||
/// <summary>
|
||||
/// Gets hash value
|
||||
/// Gets the key exchange algorithm name.
|
||||
/// </summary>
|
||||
public IEnumerable<byte> ExchangeHash
|
||||
/// <value>Key exchange algorithm name or empty if name not yet defined.</value>
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._exchangeHash == null)
|
||||
{
|
||||
this._exchangeHash = this.CalculateHash();
|
||||
}
|
||||
return this._exchangeHash;
|
||||
if (this._keyExchangeAlgorithm == null)
|
||||
return string.Empty;
|
||||
else
|
||||
return this._keyExchangeAlgorithm.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<byte> SessionId { get; set; }
|
||||
private EventWaitHandle _waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
|
||||
|
||||
public HMAC ServerMac { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the wait handle that signals that key exchange completed
|
||||
/// </summary>
|
||||
/// <value>The wait handle.</value>
|
||||
public EventWaitHandle WaitHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._waitHandle;
|
||||
}
|
||||
}
|
||||
|
||||
public HMAC ClientMac { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the session id.
|
||||
/// </summary>
|
||||
/// <value>The session id.</value>
|
||||
public IEnumerable<byte> SessionId { get; private set; }
|
||||
|
||||
public Cipher ClientCipher { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the server mac algorithm to use.
|
||||
/// </summary>
|
||||
/// <value>The server mac.</value>
|
||||
public HMAC ServerMac { get; private set; }
|
||||
|
||||
public Cipher ServerCipher { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the client mac algorithm to use.
|
||||
/// </summary>
|
||||
/// <value>The client mac.</value>
|
||||
public HMAC ClientMac { get; private set; }
|
||||
|
||||
public Compression ServerDecompression { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the client cipher algorithm to use.
|
||||
/// </summary>
|
||||
/// <value>The client cipher.</value>
|
||||
public Cipher ClientCipher { get; private set; }
|
||||
|
||||
public Compression ClientCompression { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the server cipher algorithm to use.
|
||||
/// </summary>
|
||||
/// <value>The server cipher.</value>
|
||||
public Cipher ServerCipher { get; private set; }
|
||||
|
||||
public bool IsCompleted { get; protected set; }
|
||||
public Compression ServerDecompression { get; private set; }
|
||||
|
||||
public bool IsSuccessed { get; protected set; }
|
||||
public Compression ClientCompression { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether key exchange is in progress.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [in progress]; otherwise, <c>false</c>.</value>
|
||||
public bool InProgress { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the session.
|
||||
/// </summary>
|
||||
/// <value>The session.</value>
|
||||
protected Session Session { get; private set; }
|
||||
|
||||
protected string ClientPayload { get; set; }
|
||||
|
||||
protected string ServerPayload { get; set; }
|
||||
|
||||
protected string HostKey { get; set; }
|
||||
|
||||
protected BigInteger ClientExchangeValue { get; set; }
|
||||
|
||||
protected BigInteger ServerExchangeValue { get; set; }
|
||||
|
||||
protected BigInteger SharedKey { get; set; }
|
||||
|
||||
protected string Signature { get; set; }
|
||||
|
||||
public event EventHandler<KeyExchangeCompletedEventArgs> Completed;
|
||||
|
||||
public event EventHandler<KeyExchangeFailedEventArgs> Failed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeyExchange"/> class.
|
||||
/// </summary>
|
||||
/// <param name="session">The session.</param>
|
||||
public KeyExchange(Session session)
|
||||
{
|
||||
this.Session = session;
|
||||
this.SessionId = session.SessionId;
|
||||
this.ServerDecompression = Compression.None;
|
||||
this.ClientCompression = Compression.None;
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
public void HandleMessage(KeyExchangeInitMessage message)
|
||||
{
|
||||
// TODO: If key exchange initiated by the client no need to send client message again
|
||||
var clientMessage = new KeyExchangeInitMessage()
|
||||
this._waitHandle.Reset();
|
||||
|
||||
this.InProgress = true;
|
||||
|
||||
this.SendMessage(this.Session.ClientInitMessage);
|
||||
|
||||
var keyExchangeAlgorithm = (from s in message.KeyExchangeAlgorithms
|
||||
from c in Settings.KeyExchangeAlgorithms.Keys
|
||||
where s == c
|
||||
select c).FirstOrDefault();
|
||||
|
||||
// TODO: If dont agree on algorithms then send disconnect message
|
||||
if (keyExchangeAlgorithm == null)
|
||||
{
|
||||
KeyExchangeAlgorithms = Settings.KeyExchangeAlgorithms.Keys,
|
||||
ServerHostKeyAlgorithms = Settings.HostKeyAlgorithms.Keys,
|
||||
EncryptionAlgorithmsClientToServer = Settings.Encryptions.Keys,
|
||||
EncryptionAlgorithmsServerToClient = Settings.Encryptions.Keys,
|
||||
MacAlgorithmsClientToSserver = Settings.HmacAlgorithms.Keys,
|
||||
MacAlgorithmsServerToClient = Settings.HmacAlgorithms.Keys,
|
||||
CompressionAlgorithmsClientToServer = new string[] { "none" },
|
||||
CompressionAlgorithmsServerToClient = new string[] { "none" },
|
||||
LanguagesClientToServer = new string[] { string.Empty },
|
||||
LanguagesServerToClient = new string[] { string.Empty },
|
||||
FirstKexPacketFollows = false,
|
||||
Reserved = 0,
|
||||
};
|
||||
|
||||
this.ClientPayload = clientMessage.GetBytes().GetSshString();
|
||||
|
||||
this.SendMessage(clientMessage);
|
||||
}
|
||||
|
||||
public virtual void Start(KeyExchangeInitMessage message)
|
||||
{
|
||||
this.Start();
|
||||
throw new InvalidOperationException("Failed to negotiate key exchange algorithm.");
|
||||
}
|
||||
|
||||
// Determine encryption algorithm
|
||||
var clientEncryptionAlgorithmName = (from a in message.EncryptionAlgorithmsClientToServer
|
||||
@@ -189,26 +183,43 @@ namespace Renci.SshClient.Security
|
||||
}
|
||||
this._serverHmacAlgorithm = Settings.HmacAlgorithms[serverHmacAlgorithmName];
|
||||
|
||||
this._keyExchangeAlgorithm = Settings.KeyExchangeAlgorithms[keyExchangeAlgorithm](this.Session);
|
||||
|
||||
this._keyExchangeAlgorithm.HandleMessage(message);
|
||||
}
|
||||
|
||||
public virtual void Finish()
|
||||
public void HandleMessage(NewKeysMessage message)
|
||||
{
|
||||
// TODO: Validate that all required properties are set
|
||||
// Validate hash
|
||||
var validated = this._keyExchangeAlgorithm.ValidateExchangeHash();
|
||||
|
||||
if (validated)
|
||||
{
|
||||
this.SendMessage(new NewKeysMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Key exchange negotiation failed.");
|
||||
}
|
||||
|
||||
var exchangeHash = this._keyExchangeAlgorithm.ExchangeHash;
|
||||
var sharedKey = this._keyExchangeAlgorithm.SharedKey;
|
||||
|
||||
// Initialize new encryption algorithms
|
||||
if (this.SessionId == null)
|
||||
{
|
||||
this.SessionId = this.ExchangeHash;
|
||||
this.SessionId = exchangeHash;
|
||||
}
|
||||
|
||||
// Initialize client cipher
|
||||
var clientCipher = this._clientCipher();
|
||||
// Calculate client to server initial IV
|
||||
var clientVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', this.SessionId));
|
||||
var clientVector = this.Hash(this.GenerateSessionKey(sharedKey, exchangeHash, 'A', this.SessionId));
|
||||
|
||||
// Calculate client to server encryption
|
||||
var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', this.SessionId));
|
||||
var clientKey = this.Hash(this.GenerateSessionKey(sharedKey, exchangeHash, 'C', this.SessionId));
|
||||
|
||||
clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, clientCipher.KeySize / 8);
|
||||
clientKey = this.GenerateSessionKey(sharedKey, exchangeHash, clientKey, clientCipher.KeySize / 8);
|
||||
|
||||
clientCipher.Init(clientKey, clientVector);
|
||||
|
||||
@@ -216,21 +227,21 @@ namespace Renci.SshClient.Security
|
||||
var serverCipher = this._serverCipher();
|
||||
|
||||
// Calculate server to client initial IV
|
||||
var serverVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', this.SessionId));
|
||||
var serverVector = this.Hash(this.GenerateSessionKey(sharedKey, exchangeHash, 'B', this.SessionId));
|
||||
|
||||
// Calculate server to client encryption
|
||||
var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', this.SessionId));
|
||||
var serverKey = this.Hash(this.GenerateSessionKey(sharedKey, exchangeHash, 'D', this.SessionId));
|
||||
|
||||
serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, serverCipher.KeySize / 8);
|
||||
serverKey = this.GenerateSessionKey(sharedKey, exchangeHash, serverKey, serverCipher.KeySize / 8);
|
||||
|
||||
serverCipher.Init(serverKey, serverVector);
|
||||
|
||||
// Calculate client to server integrity
|
||||
var MACc2s = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', this.SessionId));
|
||||
var MACc2s = this.Hash(this.GenerateSessionKey(sharedKey, exchangeHash, 'E', this.SessionId));
|
||||
var clientMac = this._clientHmacAlgorithm(MACc2s);
|
||||
|
||||
// Calculate server to client integrity
|
||||
var MACs2c = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', this.SessionId));
|
||||
var MACs2c = this.Hash(this.GenerateSessionKey(sharedKey, exchangeHash, 'F', this.SessionId));
|
||||
var serverMac = this._serverHmacAlgorithm(MACs2c);
|
||||
|
||||
// TODO: Create compression and decompression objects if any
|
||||
@@ -242,41 +253,23 @@ namespace Renci.SshClient.Security
|
||||
this.ServerMac = serverMac;
|
||||
this.ClientMac = clientMac;
|
||||
|
||||
this.IsCompleted = true;
|
||||
this.RaiseCompleted();
|
||||
this.InProgress = false;
|
||||
|
||||
// Signal that key exchange completed
|
||||
this._waitHandle.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Completed event.
|
||||
/// </summary>
|
||||
/// <param name="sessionId">The session id.</param>
|
||||
/// <param name="decryption">The decryption to be used.</param>
|
||||
/// <param name="encryption">The encryption to be used.</param>
|
||||
/// <param name="serverDecompression">The server decompression.</param>
|
||||
/// <param name="clientCompression">The client compression.</param>
|
||||
/// <param name="serverMac">The server mac.</param>
|
||||
/// <param name="clientMac">The client mac.</param>
|
||||
protected void RaiseCompleted()
|
||||
public void HandleMessage<T>(T message) where T : Message
|
||||
{
|
||||
if (this.Completed != null)
|
||||
{
|
||||
this.Completed(this, new KeyExchangeCompletedEventArgs());
|
||||
}
|
||||
this._keyExchangeAlgorithm.HandleMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the Failed event.
|
||||
/// </summary>
|
||||
/// <param name="message">The fail reason message.</param>
|
||||
protected void RaiseFailed(string message)
|
||||
private void SendMessage(Message message)
|
||||
{
|
||||
if (this.Failed != null)
|
||||
{
|
||||
this.Failed(this, new KeyExchangeFailedEventArgs(message));
|
||||
}
|
||||
this.Session.SendMessage(message);
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<byte> Hash(IEnumerable<byte> hashBytes)
|
||||
private IEnumerable<byte> Hash(IEnumerable<byte> hashBytes)
|
||||
{
|
||||
using (var md = new System.Security.Cryptography.SHA1CryptoServiceProvider())
|
||||
{
|
||||
@@ -290,45 +283,6 @@ namespace Renci.SshClient.Security
|
||||
}
|
||||
}
|
||||
|
||||
protected bool ValidateExchangeHash()
|
||||
{
|
||||
var bytes = this.HostKey.GetSshBytes();
|
||||
|
||||
var length = (uint)(this.HostKey[0] << 24 | this.HostKey[1] << 16 | this.HostKey[2] << 8 | this.HostKey[3]);
|
||||
|
||||
var algorithmName = bytes.Skip(4).Take((int)length).GetSshString();
|
||||
|
||||
var data = bytes.Skip(4 + algorithmName.Length);
|
||||
|
||||
CryptoPublicKey key = Settings.HostKeyAlgorithms[algorithmName]();
|
||||
|
||||
key.Load(data);
|
||||
|
||||
return key.VerifySignature(this.ExchangeHash, this.Signature.GetSshBytes());
|
||||
}
|
||||
|
||||
protected void SendMessage(Message message)
|
||||
{
|
||||
this.Session.SendMessage(message);
|
||||
}
|
||||
|
||||
private IEnumerable<byte> CalculateHash()
|
||||
{
|
||||
var hashData = new _ExchangeHashData
|
||||
{
|
||||
ClientVersion = this.Session.ClientVersion,
|
||||
ServerVersion = this.Session.ServerVersion,
|
||||
ClientPayload = this.ClientPayload,
|
||||
ServerPayload = this.ServerPayload,
|
||||
HostKey = this.HostKey,
|
||||
ClientExchangeValue = this.ClientExchangeValue,
|
||||
ServerExchangeValue = this.ServerExchangeValue,
|
||||
SharedKey = this.SharedKey,
|
||||
}.GetBytes();
|
||||
|
||||
return this.Hash(hashData);
|
||||
}
|
||||
|
||||
private IEnumerable<byte> GenerateSessionKey(BigInteger sharedKey, IEnumerable<byte> exchangeHash, IEnumerable<byte> key, int size)
|
||||
{
|
||||
var result = new List<byte>(key);
|
||||
@@ -356,58 +310,48 @@ namespace Renci.SshClient.Security
|
||||
}.GetBytes();
|
||||
}
|
||||
|
||||
private class _ExchangeHashData : SshData
|
||||
#region IDisposable Members
|
||||
|
||||
private bool disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
public string ServerVersion { get; set; }
|
||||
Dispose(true);
|
||||
|
||||
public string ClientVersion { get; set; }
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public string ClientPayload { get; set; }
|
||||
|
||||
public string ServerPayload { get; set; }
|
||||
|
||||
public string HostKey { get; set; }
|
||||
|
||||
public UInt32? MinimumGroupSize { get; set; }
|
||||
|
||||
public UInt32? PreferredGroupSize { get; set; }
|
||||
|
||||
public UInt32? MaximumGroupSize { get; set; }
|
||||
|
||||
public IEnumerable<byte> Prime { get; set; }
|
||||
|
||||
public BigInteger ClientExchangeValue { get; set; }
|
||||
|
||||
public BigInteger ServerExchangeValue { get; set; }
|
||||
|
||||
public BigInteger SharedKey { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// Check to see if Dispose has already been called.
|
||||
if (!this.disposed)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed resources.
|
||||
if (this._waitHandle != null)
|
||||
{
|
||||
this._waitHandle.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.Write(this.ClientVersion);
|
||||
this.Write(this.ServerVersion);
|
||||
this.Write(this.ClientPayload);
|
||||
this.Write(this.ServerPayload);
|
||||
this.Write(this.HostKey);
|
||||
if (this.MinimumGroupSize.HasValue)
|
||||
this.Write(this.MinimumGroupSize.Value);
|
||||
if (this.PreferredGroupSize.HasValue)
|
||||
this.Write(this.PreferredGroupSize.Value);
|
||||
if (this.MaximumGroupSize.HasValue)
|
||||
this.Write(this.MaximumGroupSize.Value);
|
||||
if (this.Prime != null)
|
||||
this.Write(this.Prime);
|
||||
this.Write(this.ClientExchangeValue);
|
||||
this.Write(this.ServerExchangeValue);
|
||||
this.Write(this.SharedKey);
|
||||
// Note disposing has been done.
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~KeyExchange()
|
||||
{
|
||||
// Do not re-create Dispose clean-up code here.
|
||||
// Calling Dispose(false) is optimal in terms of
|
||||
// readability and maintainability.
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private class _SessionKeyGeneration : SshData
|
||||
{
|
||||
public BigInteger SharedKey { get; set; }
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Renci.SshClient.Messages;
|
||||
|
||||
namespace Renci.SshClient.Security
|
||||
{
|
||||
internal abstract class KeyExchangeAlgorithm : Algorithm
|
||||
{
|
||||
public BigInteger SharedKey { get; protected set; }
|
||||
|
||||
private IEnumerable<byte> _exchangeHash;
|
||||
/// <summary>
|
||||
/// Gets the exchange hash.
|
||||
/// </summary>
|
||||
/// <value>The exchange hash.</value>
|
||||
public IEnumerable<byte> ExchangeHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._exchangeHash == null)
|
||||
{
|
||||
this._exchangeHash = this.CalculateHash();
|
||||
}
|
||||
return this._exchangeHash;
|
||||
}
|
||||
}
|
||||
|
||||
protected Session Session { get; set; }
|
||||
|
||||
public KeyExchangeAlgorithm(Session session)
|
||||
{
|
||||
this.Session = session;
|
||||
}
|
||||
|
||||
public abstract bool ValidateExchangeHash();
|
||||
|
||||
public abstract void HandleMessage<T>(T message) where T : Message;
|
||||
|
||||
protected abstract IEnumerable<byte> CalculateHash();
|
||||
|
||||
protected IEnumerable<byte> Hash(IEnumerable<byte> hashBytes)
|
||||
{
|
||||
using (var md = new System.Security.Cryptography.SHA1CryptoServiceProvider())
|
||||
{
|
||||
using (var cs = new System.Security.Cryptography.CryptoStream(System.IO.Stream.Null, md, System.Security.Cryptography.CryptoStreamMode.Write))
|
||||
{
|
||||
var hashData = hashBytes.ToArray();
|
||||
cs.Write(hashData, 0, hashData.Length);
|
||||
cs.Close();
|
||||
return md.Hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void SendMessage(Message message)
|
||||
{
|
||||
this.Session.SendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
@@ -8,7 +9,7 @@ using Renci.SshClient.Messages.Transport;
|
||||
|
||||
namespace Renci.SshClient.Security
|
||||
{
|
||||
internal class KeyExchangeDiffieHellman : KeyExchange
|
||||
internal class KeyExchangeDiffieHellman : KeyExchangeAlgorithm
|
||||
{
|
||||
private static RNGCryptoServiceProvider _randomizer = new System.Security.Cryptography.RNGCryptoServiceProvider();
|
||||
|
||||
@@ -32,6 +33,18 @@ namespace Renci.SshClient.Security
|
||||
|
||||
private static BigInteger _group = new BigInteger(new byte[] { 2 });
|
||||
|
||||
private string _clientPayload;
|
||||
|
||||
private string _serverPayload;
|
||||
|
||||
private BigInteger _clientExchangeValue;
|
||||
|
||||
private BigInteger _serverExchangeValue;
|
||||
|
||||
private string _hostKey;
|
||||
|
||||
private string _signature;
|
||||
|
||||
private BigInteger _randomValue;
|
||||
|
||||
public override string Name
|
||||
@@ -48,9 +61,38 @@ namespace Renci.SshClient.Security
|
||||
{
|
||||
}
|
||||
|
||||
public override void Start(KeyExchangeInitMessage message)
|
||||
public override void HandleMessage<T>(T message)
|
||||
{
|
||||
base.Start(message);
|
||||
this.HandleMessage((dynamic)message);
|
||||
}
|
||||
|
||||
public override bool ValidateExchangeHash()
|
||||
{
|
||||
var exchangeHash = this.CalculateHash();
|
||||
|
||||
var hostKey = this._hostKey;
|
||||
|
||||
var signature = this._signature;
|
||||
|
||||
var bytes = hostKey.GetSshBytes();
|
||||
|
||||
var length = (uint)(hostKey[0] << 24 | hostKey[1] << 16 | hostKey[2] << 8 | hostKey[3]);
|
||||
|
||||
var algorithmName = bytes.Skip(4).Take((int)length).GetSshString();
|
||||
|
||||
var data = bytes.Skip(4 + algorithmName.Length);
|
||||
|
||||
CryptoPublicKey key = Settings.HostKeyAlgorithms[algorithmName]();
|
||||
|
||||
key.Load(data);
|
||||
|
||||
return key.VerifySignature(exchangeHash, signature.GetSshBytes());
|
||||
}
|
||||
|
||||
private void HandleMessage(KeyExchangeInitMessage message)
|
||||
{
|
||||
this._serverPayload = message.GetBytes().GetSshString();
|
||||
this._clientPayload = this.Session.ClientInitMessage.GetBytes().GetSshString();
|
||||
|
||||
// TODO: Calculate random value correctly, enforce limits
|
||||
var clientExchangeValue = BigInteger.Zero;
|
||||
@@ -60,65 +102,94 @@ namespace Renci.SshClient.Security
|
||||
clientExchangeValue = System.Numerics.BigInteger.ModPow(KeyExchangeDiffieHellman._group, this._randomValue, KeyExchangeDiffieHellman._prime);
|
||||
}
|
||||
|
||||
this.ServerPayload = message.GetBytes().GetSshString();
|
||||
|
||||
this.ClientExchangeValue = clientExchangeValue;
|
||||
this._clientExchangeValue = clientExchangeValue;
|
||||
|
||||
// Register expected message replies
|
||||
this.Session.RegisterMessageType<KeyExchangeDhReplyMessage>(MessageTypes.KeyExchangeDhReply);
|
||||
|
||||
this.SendMessage(new KeyExchangeDhInitMessage
|
||||
{
|
||||
E = this.ClientExchangeValue,
|
||||
E = this._clientExchangeValue,
|
||||
});
|
||||
|
||||
this.Session.MessageReceived += SessionInfo_MessageReceived;
|
||||
|
||||
}
|
||||
|
||||
public override void Finish()
|
||||
{
|
||||
base.Finish();
|
||||
|
||||
this.Session.MessageReceived -= SessionInfo_MessageReceived;
|
||||
}
|
||||
|
||||
private void SessionInfo_MessageReceived(object sender, MessageReceivedEventArgs e)
|
||||
{
|
||||
this.HandleMessage((dynamic)e.Message);
|
||||
}
|
||||
|
||||
private void HandleMessage<T>(T message) where T : Message, new()
|
||||
{
|
||||
// Do nothing, handle only known messages
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the KeyExchangeDhReplyMessage message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
private void HandleMessage(KeyExchangeDhReplyMessage message)
|
||||
{
|
||||
// Unregister message once received
|
||||
this.Session.UnRegisterMessageType(MessageTypes.KeyExchangeDhReply);
|
||||
|
||||
var sharedKey = System.Numerics.BigInteger.ModPow(message.F, this._randomValue, KeyExchangeDiffieHellman._prime);
|
||||
this._serverExchangeValue = message.F;
|
||||
this._hostKey = message.HostKey;
|
||||
this.SharedKey = System.Numerics.BigInteger.ModPow(message.F, this._randomValue, KeyExchangeDiffieHellman._prime);
|
||||
this._signature = message.Signature;
|
||||
}
|
||||
|
||||
this.ServerExchangeValue = message.F;
|
||||
this.HostKey = message.HostKey;
|
||||
this.SharedKey = sharedKey;
|
||||
this.Signature = message.Signature;
|
||||
|
||||
// Validate hash value
|
||||
if (this.ValidateExchangeHash())
|
||||
protected override IEnumerable<byte> CalculateHash()
|
||||
{
|
||||
var hashData = new _ExchangeHashData
|
||||
{
|
||||
this.IsSuccessed = true;
|
||||
this.SendMessage(new NewKeysMessage());
|
||||
ClientVersion = this.Session.ClientVersion,
|
||||
ServerVersion = this.Session.ServerVersion,
|
||||
ClientPayload = this._clientPayload,
|
||||
ServerPayload = this._serverPayload,
|
||||
HostKey = this._hostKey,
|
||||
ClientExchangeValue = this._clientExchangeValue,
|
||||
ServerExchangeValue = this._serverExchangeValue,
|
||||
SharedKey = this.SharedKey,
|
||||
}.GetBytes();
|
||||
|
||||
return this.Hash(hashData);
|
||||
}
|
||||
|
||||
private class _ExchangeHashData : SshData
|
||||
{
|
||||
public string ServerVersion { get; set; }
|
||||
|
||||
public string ClientVersion { get; set; }
|
||||
|
||||
public string ClientPayload { get; set; }
|
||||
|
||||
public string ServerPayload { get; set; }
|
||||
|
||||
public string HostKey { get; set; }
|
||||
|
||||
public UInt32? MinimumGroupSize { get; set; }
|
||||
|
||||
public UInt32? PreferredGroupSize { get; set; }
|
||||
|
||||
public UInt32? MaximumGroupSize { get; set; }
|
||||
|
||||
public IEnumerable<byte> Prime { get; set; }
|
||||
|
||||
public BigInteger ClientExchangeValue { get; set; }
|
||||
|
||||
public BigInteger ServerExchangeValue { get; set; }
|
||||
|
||||
public BigInteger SharedKey { get; set; }
|
||||
|
||||
protected override void LoadData()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
else
|
||||
|
||||
protected override void SaveData()
|
||||
{
|
||||
this.IsSuccessed = false;
|
||||
this.RaiseFailed("Key negotiationed failed.");
|
||||
this.Write(this.ClientVersion);
|
||||
this.Write(this.ServerVersion);
|
||||
this.Write(this.ClientPayload);
|
||||
this.Write(this.ServerPayload);
|
||||
this.Write(this.HostKey);
|
||||
if (this.MinimumGroupSize.HasValue)
|
||||
this.Write(this.MinimumGroupSize.Value);
|
||||
if (this.PreferredGroupSize.HasValue)
|
||||
this.Write(this.PreferredGroupSize.Value);
|
||||
if (this.MaximumGroupSize.HasValue)
|
||||
this.Write(this.MaximumGroupSize.Value);
|
||||
if (this.Prime != null)
|
||||
this.Write(this.Prime);
|
||||
this.Write(this.ClientExchangeValue);
|
||||
this.Write(this.ServerExchangeValue);
|
||||
this.Write(this.SharedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,11 +62,6 @@ namespace Renci.SshClient
|
||||
/// </summary>
|
||||
private UInt32 _inboundPacketSequence = 0;
|
||||
|
||||
/// <summary>
|
||||
/// WaitHandle to signal that key exchange was finished, weither it was succesfull or not.
|
||||
/// </summary>
|
||||
private EventWaitHandle _keyExhangedFinishedWaitHandle;
|
||||
|
||||
/// <summary>
|
||||
/// WaitHandle to signale that last service request was accepted
|
||||
/// </summary>
|
||||
@@ -107,42 +102,6 @@ namespace Renci.SshClient
|
||||
/// </summary>
|
||||
private BlockingStack<uint> _channelNumbers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the HMAC algorithm to use when receiving message from the server.
|
||||
/// </summary>
|
||||
/// <value>The server mac.</value>
|
||||
private HMAC _serverMac;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the HMAC algorithm to use when sending message to the server.
|
||||
/// </summary>
|
||||
/// <value>The client mac.</value>
|
||||
private HMAC _clientMac;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the client cipher which used to encrypt messages sent to server.
|
||||
/// </summary>
|
||||
/// <value>The client cipher.</value>
|
||||
private Cipher _clientCipher;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server cipher which used to decrypt messages sent by server.
|
||||
/// </summary>
|
||||
/// <value>The server cipher.</value>
|
||||
private Cipher _serverCipher;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compression algorithm to use when receiving message from the server.
|
||||
/// </summary>
|
||||
/// <value>The server decompression.</value>
|
||||
private Compression _serverDecompression;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compression algorithm to use when sending message to the server.
|
||||
/// </summary>
|
||||
/// <value>The client compression.</value>
|
||||
private Compression _clientCompression;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether socket connected.
|
||||
/// </summary>
|
||||
@@ -166,7 +125,44 @@ namespace Renci.SshClient
|
||||
/// Gets or sets the session id.
|
||||
/// </summary>
|
||||
/// <value>The session id.</value>
|
||||
public IEnumerable<byte> SessionId { get; private set; }
|
||||
public IEnumerable<byte> SessionId
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._keyExhcange.SessionId;
|
||||
}
|
||||
}
|
||||
|
||||
private Message _clientInitMessage;
|
||||
/// <summary>
|
||||
/// Gets the client init message.
|
||||
/// </summary>
|
||||
/// <value>The client init message.</value>
|
||||
public Message ClientInitMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._clientInitMessage == null)
|
||||
{
|
||||
this._clientInitMessage = new KeyExchangeInitMessage()
|
||||
{
|
||||
KeyExchangeAlgorithms = Settings.KeyExchangeAlgorithms.Keys,
|
||||
ServerHostKeyAlgorithms = Settings.HostKeyAlgorithms.Keys,
|
||||
EncryptionAlgorithmsClientToServer = Settings.Encryptions.Keys,
|
||||
EncryptionAlgorithmsServerToClient = Settings.Encryptions.Keys,
|
||||
MacAlgorithmsClientToSserver = Settings.HmacAlgorithms.Keys,
|
||||
MacAlgorithmsServerToClient = Settings.HmacAlgorithms.Keys,
|
||||
CompressionAlgorithmsClientToServer = new string[] { "none" },
|
||||
CompressionAlgorithmsServerToClient = new string[] { "none" },
|
||||
LanguagesClientToServer = new string[] { string.Empty },
|
||||
LanguagesServerToClient = new string[] { string.Empty },
|
||||
FirstKexPacketFollows = false,
|
||||
Reserved = 0,
|
||||
};
|
||||
}
|
||||
return this._clientInitMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the server version string.
|
||||
@@ -266,7 +262,6 @@ namespace Renci.SshClient
|
||||
this._channelNumbers.Push((uint)i - 1);
|
||||
}
|
||||
|
||||
|
||||
var ep = new IPEndPoint(Dns.GetHostAddresses(connectionInfo.Host)[0], connectionInfo.Port);
|
||||
this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
@@ -319,11 +314,14 @@ namespace Renci.SshClient
|
||||
this.RegisterMessageType<KeyExchangeInitMessage>(MessageTypes.KeyExchangeInit);
|
||||
this.RegisterMessageType<NewKeysMessage>(MessageTypes.NewKeys);
|
||||
|
||||
|
||||
this._keyExhcange = new KeyExchange(this);
|
||||
|
||||
// Start incoming request listener
|
||||
this._messageListener = Task.Factory.StartNew(() => { this.MessageListener(); });
|
||||
|
||||
// Wait for key exchange to be completed
|
||||
this.WaitHandle(this._keyExhangedFinishedWaitHandle);
|
||||
this.WaitHandle(this._keyExhcange.WaitHandle);
|
||||
|
||||
// If sessionId is not set then its not connected
|
||||
if (this.SessionId == null)
|
||||
@@ -428,8 +426,8 @@ namespace Renci.SshClient
|
||||
if (!this._socket.Connected)
|
||||
return;
|
||||
|
||||
// Messages can be sent by different thread so we need to synchronize it
|
||||
var paddingMultiplier = this._clientCipher == null ? (byte)8 : (byte)this._clientCipher.BlockSize; // Should be recalculate base on cipher min lenght if sipher specified
|
||||
// Messages can be sent by different thread so we need to synchronize it
|
||||
var paddingMultiplier = this._keyExhcange.ClientCipher == null ? (byte)8 : (byte)this._keyExhcange.ClientCipher.BlockSize; // Should be recalculate base on cipher min lenght if sipher specified
|
||||
|
||||
var messageData = message.GetBytes();
|
||||
|
||||
@@ -474,15 +472,15 @@ namespace Renci.SshClient
|
||||
|
||||
// Encrypt packet data
|
||||
var encryptedData = packetData.ToList();
|
||||
if (this._clientCipher != null)
|
||||
if (this._keyExhcange.ClientCipher != null)
|
||||
{
|
||||
encryptedData = new List<byte>(this._clientCipher.Encrypt(packetData));
|
||||
encryptedData = new List<byte>(this._keyExhcange.ClientCipher.Encrypt(packetData));
|
||||
}
|
||||
|
||||
// Add message authentication code (MAC)
|
||||
if (this._clientMac != null)
|
||||
if (this._keyExhcange.ClientMac != null)
|
||||
{
|
||||
var hash = this._clientMac.ComputeHash(hashData.ToArray());
|
||||
var hash = this._keyExhcange.ClientMac.ComputeHash(hashData.ToArray());
|
||||
|
||||
encryptedData.AddRange(hash);
|
||||
}
|
||||
@@ -513,18 +511,18 @@ namespace Renci.SshClient
|
||||
|
||||
List<byte> decryptedData;
|
||||
|
||||
var blockSize = this._serverCipher == null ? (byte)8 : (byte)this._serverCipher.BlockSize;
|
||||
var blockSize = this._keyExhcange.ServerCipher == null ? (byte)8 : (byte)this._keyExhcange.ServerCipher.BlockSize;
|
||||
|
||||
// Read packet lenght first
|
||||
var data = new List<byte>(this.Read(blockSize));
|
||||
|
||||
if (this._serverCipher == null)
|
||||
if (this._keyExhcange.ServerCipher == null)
|
||||
{
|
||||
decryptedData = data.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
decryptedData = new List<byte>(this._serverCipher.Decrypt(data));
|
||||
decryptedData = new List<byte>(this._keyExhcange.ServerCipher.Decrypt(data));
|
||||
}
|
||||
|
||||
var packetLength = (uint)(decryptedData[0] << 24 | decryptedData[1] << 16 | decryptedData[2] << 8 | decryptedData[3]);
|
||||
@@ -536,26 +534,26 @@ namespace Renci.SshClient
|
||||
// Read rest of the packet data
|
||||
int bytesToRead = (int)(packetLength - (blockSize - 4));
|
||||
|
||||
if (this._serverCipher == null)
|
||||
if (this._keyExhcange.ServerCipher == null)
|
||||
{
|
||||
decryptedData.AddRange(this.Read(bytesToRead));
|
||||
}
|
||||
else
|
||||
{
|
||||
decryptedData.AddRange(this._serverCipher.Decrypt(this.Read(bytesToRead)));
|
||||
decryptedData.AddRange(this._keyExhcange.ServerCipher.Decrypt(this.Read(bytesToRead)));
|
||||
}
|
||||
|
||||
// Validate message against MAC
|
||||
if (this._serverMac != null)
|
||||
// Validate message against MAC
|
||||
if (this._keyExhcange.ServerMac != null)
|
||||
{
|
||||
var serverHash = this.Read(this._serverMac.HashSize / 8);
|
||||
var serverHash = this.Read(this._keyExhcange.ServerMac.HashSize / 8);
|
||||
|
||||
var clientHashData = new List<byte>();
|
||||
clientHashData.AddRange(BitConverter.GetBytes(this._inboundPacketSequence).Reverse());
|
||||
clientHashData.AddRange(decryptedData);
|
||||
|
||||
// Calculate packet hash
|
||||
var clientHash = this._serverMac.ComputeHash(clientHashData.ToArray());
|
||||
var clientHash = this._keyExhcange.ServerMac.ComputeHash(clientHashData.ToArray());
|
||||
|
||||
if (!serverHash.SequenceEqual(clientHash))
|
||||
{
|
||||
@@ -563,12 +561,11 @@ namespace Renci.SshClient
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Issue new keys after x number of packets
|
||||
this._inboundPacketSequence++;
|
||||
|
||||
var paddingLength = decryptedData[4];
|
||||
|
||||
// TODO: Decrypt message payload
|
||||
// TODO: Inflate message payload
|
||||
|
||||
var payload = decryptedData.Skip(5).Take((int)(packetLength - paddingLength - 1));
|
||||
|
||||
@@ -623,40 +620,7 @@ namespace Renci.SshClient
|
||||
|
||||
protected virtual void HandleMessage(KeyExchangeInitMessage message)
|
||||
{
|
||||
this._keyExhangedFinishedWaitHandle.Reset();
|
||||
|
||||
if (message.FirstKexPacketFollows)
|
||||
{
|
||||
// TODO: Expect guess packet
|
||||
throw new NotImplementedException("Guess packets are not supported.");
|
||||
}
|
||||
|
||||
// Create key exchange algorithm
|
||||
this._keyExhcange = KeyExchange.Create(message, this);
|
||||
|
||||
this._keyExhcange.Failed += delegate(object sender, KeyExchangeFailedEventArgs e)
|
||||
{
|
||||
this.Disconnect(DisconnectReasonCodes.KeyExchangeFailed, e.Message);
|
||||
throw new InvalidOperationException(e.Message);
|
||||
};
|
||||
|
||||
this._keyExhcange.Start(message);
|
||||
}
|
||||
|
||||
protected virtual void HandleMessage(NewKeysMessage message)
|
||||
{
|
||||
this._keyExhcange.Finish();
|
||||
|
||||
this.SessionId = this._keyExhcange.SessionId;
|
||||
// Update encryption and decryption algorithm
|
||||
this._serverMac = this._keyExhcange.ServerMac;
|
||||
this._clientMac = this._keyExhcange.ClientMac;
|
||||
this._clientCipher = this._keyExhcange.ClientCipher;
|
||||
this._serverCipher = this._keyExhcange.ServerCipher;
|
||||
this._serverDecompression = this._keyExhcange.ServerDecompression;
|
||||
this._clientCompression = this._keyExhcange.ClientCompression;
|
||||
|
||||
this._keyExhangedFinishedWaitHandle.Set();
|
||||
this._keyExhcange.HandleMessage(message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -744,7 +708,7 @@ namespace Renci.SshClient
|
||||
Thread.Sleep(30);
|
||||
}
|
||||
else
|
||||
throw exp; // any serious error occurr
|
||||
throw; // any serious error occurr
|
||||
}
|
||||
} while (received < length);
|
||||
|
||||
@@ -777,21 +741,12 @@ namespace Renci.SshClient
|
||||
Thread.Sleep(30);
|
||||
}
|
||||
else
|
||||
throw ex; // any serious error occurr
|
||||
throw; // any serious error occurr
|
||||
}
|
||||
} while (sent < length);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates new key request by the client
|
||||
/// </summary>
|
||||
protected void RequestNewKeys()
|
||||
{
|
||||
// TODO: Create method to issue new keys when required
|
||||
//this._keyExhcange.Start();
|
||||
}
|
||||
|
||||
#region Message loading functions
|
||||
|
||||
private delegate T LoadFunc<out T>(IEnumerable<byte> data);
|
||||
@@ -852,35 +807,17 @@ namespace Renci.SshClient
|
||||
this._outboundPacketSequence = 0;
|
||||
this._inboundPacketSequence = 0;
|
||||
this._openChannels = new Dictionary<uint, Channel>();
|
||||
this._keyExhangedFinishedWaitHandle = new AutoResetEvent(false);
|
||||
this._serviceAccepted = new AutoResetEvent(false);
|
||||
this._exceptionWaitHandle = new AutoResetEvent(false);
|
||||
this._listenerWaitHandle = new AutoResetEvent(false);
|
||||
this._channelNumbers = new BlockingStack<uint>();
|
||||
this._exceptionToThrow = null;
|
||||
this.SessionId = null;
|
||||
this.ServerVersion = null;
|
||||
this._keyExhcange = null;
|
||||
this._serverMac = null;
|
||||
this._clientMac = null;
|
||||
this._clientCipher = null;
|
||||
this._serverCipher = null;
|
||||
this._serverDecompression = null;
|
||||
this._clientCompression = null;
|
||||
this._isAuthenticated = false;
|
||||
this._isDisconnecting = false;
|
||||
}
|
||||
|
||||
private bool ValidateHash(List<byte> decryptedData, byte[] serverHash, uint packetSequence)
|
||||
{
|
||||
var clientHashData = new List<byte>();
|
||||
clientHashData.AddRange(BitConverter.GetBytes(packetSequence).Reverse());
|
||||
clientHashData.AddRange(decryptedData);
|
||||
|
||||
var clientHash = this._serverMac.ComputeHash(clientHashData.ToArray());
|
||||
return serverHash.SequenceEqual(clientHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perfom neccesary cleanup when client disconects from the server
|
||||
/// </summary>
|
||||
@@ -918,15 +855,28 @@ namespace Renci.SshClient
|
||||
{
|
||||
try
|
||||
{
|
||||
dynamic message = this.ReceiveMessage();
|
||||
var message = this.ReceiveMessage();
|
||||
|
||||
if (message == null)
|
||||
{
|
||||
throw new NullReferenceException("The 'message' variable cannot be null");
|
||||
}
|
||||
|
||||
// Handle session messages first
|
||||
this.HandleMessage(message);
|
||||
else if (message is DisconnectMessage)
|
||||
{
|
||||
// Always handle disconnect message first
|
||||
this.HandleMessage(message);
|
||||
break; // Exit message listener loop, no more messages should be handled
|
||||
}
|
||||
else if (this._keyExhcange.InProgress)
|
||||
{
|
||||
this._keyExhcange.HandleMessage((dynamic)message);
|
||||
continue; // Get next message, all non kexinit messages should be ignored
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle session messages first
|
||||
this.HandleMessage((dynamic)message);
|
||||
}
|
||||
|
||||
// Raise an event that message received
|
||||
this.RaiseMessageReceived(this, new MessageReceivedEventArgs(message));
|
||||
@@ -1011,11 +961,6 @@ namespace Renci.SshClient
|
||||
this._socket.Dispose();
|
||||
}
|
||||
|
||||
if (this._keyExhangedFinishedWaitHandle != null)
|
||||
{
|
||||
this._keyExhangedFinishedWaitHandle.Dispose();
|
||||
}
|
||||
|
||||
if (this._serviceAccepted != null)
|
||||
{
|
||||
this._serviceAccepted.Dispose();
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Renci.SshClient
|
||||
{
|
||||
internal static class Settings
|
||||
{
|
||||
public static IDictionary<string, Func<Session, KeyExchange>> KeyExchangeAlgorithms { get; private set; }
|
||||
public static IDictionary<string, Func<Session, KeyExchangeAlgorithm>> KeyExchangeAlgorithms { get; private set; }
|
||||
|
||||
public static IDictionary<string, Func<Cipher>> Encryptions { get; private set; }
|
||||
|
||||
@@ -20,11 +20,10 @@ namespace Renci.SshClient
|
||||
|
||||
static Settings()
|
||||
{
|
||||
Settings.KeyExchangeAlgorithms = new Dictionary<string, Func<Session, KeyExchange>>()
|
||||
Settings.KeyExchangeAlgorithms = new Dictionary<string, Func<Session, KeyExchangeAlgorithm>>()
|
||||
{
|
||||
{"diffie-hellman-group1-sha1", (a) => { return new KeyExchangeDiffieHellman(a);}}
|
||||
//"diffie-hellman-group-exchange-sha1"
|
||||
|
||||
};
|
||||
|
||||
Settings.Encryptions = new Dictionary<string, Func<Cipher>>()
|
||||
|
||||
Reference in New Issue
Block a user