浏览代码

Use BouncyCastle for Diffie-Hellman key exchange (#1654)

Removes another vestige of hand-rolled crypto, and makes the classes public +
configurable for if/when we remove certain algorithms.
Rob Hague 2 月之前
父节点
当前提交
dab8a11738
共有 25 个文件被更改,包括 496 次插入1009 次删除
  1. 9 6
      src/Renci.SshNet/ConnectionInfo.cs
  2. 10 10
      src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs
  3. 6 17
      src/Renci.SshNet/Security/GroupExchangeHashData.cs
  4. 1 1
      src/Renci.SshNet/Security/KeyExchange.cs
  5. 121 105
      src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs
  6. 0 62
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs
  7. 0 62
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha256.cs
  8. 0 66
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup16Sha512.cs
  9. 0 48
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs
  10. 244 0
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchange.cs
  11. 0 41
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs
  12. 0 41
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs
  13. 0 111
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeShaBase.cs
  14. 0 33
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs
  15. 0 33
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha256.cs
  16. 0 33
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha512.cs
  17. 0 80
      src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupShaBase.cs
  18. 2 12
      src/Renci.SshNet/Security/KeyExchangeEC.cs
  19. 0 1
      test/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs
  20. 0 61
      test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha1Test.cs
  21. 0 61
      test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha256Test.cs
  22. 0 71
      test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup16Sha512Test.cs
  23. 0 54
      test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup1Sha1Test.cs
  24. 54 0
      test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroupExchangeTest.cs
  25. 49 0
      test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanTest.cs

+ 9 - 6
src/Renci.SshNet/ConnectionInfo.cs

@@ -7,6 +7,8 @@ using System.Text;
 
 using Microsoft.Extensions.Logging;
 
+using Org.BouncyCastle.Crypto.Agreement;
+
 using Renci.SshNet.Common;
 using Renci.SshNet.Compression;
 using Renci.SshNet.Messages.Authentication;
@@ -357,12 +359,13 @@ namespace Renci.SshNet
                     { "ecdh-sha2-nistp256", () => new KeyExchangeECDH256() },
                     { "ecdh-sha2-nistp384", () => new KeyExchangeECDH384() },
                     { "ecdh-sha2-nistp521", () => new KeyExchangeECDH521() },
-                    { "diffie-hellman-group-exchange-sha256", () => new KeyExchangeDiffieHellmanGroupExchangeSha256() },
-                    { "diffie-hellman-group-exchange-sha1", () => new KeyExchangeDiffieHellmanGroupExchangeSha1() },
-                    { "diffie-hellman-group16-sha512", () => new KeyExchangeDiffieHellmanGroup16Sha512() },
-                    { "diffie-hellman-group14-sha256", () => new KeyExchangeDiffieHellmanGroup14Sha256() },
-                    { "diffie-hellman-group14-sha1", () => new KeyExchangeDiffieHellmanGroup14Sha1() },
-                    { "diffie-hellman-group1-sha1", () => new KeyExchangeDiffieHellmanGroup1Sha1() },
+                    { "diffie-hellman-group-exchange-sha256", () => new KeyExchangeDiffieHellmanGroupExchange("diffie-hellman-group-exchange-sha256", HashAlgorithmName.SHA256) },
+                    { "diffie-hellman-group16-sha512", () => new KeyExchangeDiffieHellman("diffie-hellman-group16-sha512", DHStandardGroups.rfc3526_4096, HashAlgorithmName.SHA512) },
+                    { "diffie-hellman-group18-sha512", () => new KeyExchangeDiffieHellman("diffie-hellman-group18-sha512", DHStandardGroups.rfc3526_8192, HashAlgorithmName.SHA512) },
+                    { "diffie-hellman-group14-sha256", () => new KeyExchangeDiffieHellman("diffie-hellman-group14-sha256", DHStandardGroups.rfc3526_2048, HashAlgorithmName.SHA256) },
+                    { "diffie-hellman-group-exchange-sha1", () => new KeyExchangeDiffieHellmanGroupExchange("diffie-hellman-group-exchange-sha1", HashAlgorithmName.SHA1) },
+                    { "diffie-hellman-group14-sha1", () => new KeyExchangeDiffieHellman("diffie-hellman-group14-sha1", DHStandardGroups.rfc3526_2048, HashAlgorithmName.SHA1) },
+                    { "diffie-hellman-group1-sha1", () => new KeyExchangeDiffieHellman("diffie-hellman-group1-sha1", DHStandardGroups.rfc2409_1024, HashAlgorithmName.SHA1) },
                 };
 
             Encryptions = new OrderedDictionary<string, CipherInfo>

+ 10 - 10
src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs

@@ -9,8 +9,8 @@ namespace Renci.SshNet.Messages.Transport
     /// </summary>
     public class KeyExchangeDhGroupExchangeGroup : Message
     {
-        private byte[] _safePrime;
-        private byte[] _subGroup;
+        internal byte[] SafePrimeBytes { get; private set; }
+        internal byte[] SubGroupBytes { get; private set; }
 
         /// <inheritdoc />
         public override string MessageName
@@ -38,7 +38,7 @@ namespace Renci.SshNet.Messages.Transport
         /// </value>
         public BigInteger SafePrime
         {
-            get { return _safePrime.ToBigInteger(); }
+            get { return SafePrimeBytes.ToBigInteger(); }
         }
 
         /// <summary>
@@ -49,7 +49,7 @@ namespace Renci.SshNet.Messages.Transport
         /// </value>
         public BigInteger SubGroup
         {
-            get { return _subGroup.ToBigInteger(); }
+            get { return SubGroupBytes.ToBigInteger(); }
         }
 
         /// <summary>
@@ -64,9 +64,9 @@ namespace Renci.SshNet.Messages.Transport
             {
                 var capacity = base.BufferCapacity;
                 capacity += 4; // SafePrime length
-                capacity += _safePrime.Length; // SafePrime
+                capacity += SafePrimeBytes.Length; // SafePrime
                 capacity += 4; // SubGroup length
-                capacity += _subGroup.Length; // SubGroup
+                capacity += SubGroupBytes.Length; // SubGroup
 
                 return capacity;
             }
@@ -77,8 +77,8 @@ namespace Renci.SshNet.Messages.Transport
         /// </summary>
         protected override void LoadData()
         {
-            _safePrime = ReadBinary();
-            _subGroup = ReadBinary();
+            SafePrimeBytes = ReadBinary();
+            SubGroupBytes = ReadBinary();
         }
 
         /// <summary>
@@ -86,8 +86,8 @@ namespace Renci.SshNet.Messages.Transport
         /// </summary>
         protected override void SaveData()
         {
-            WriteBinaryString(_safePrime);
-            WriteBinaryString(_subGroup);
+            WriteBinaryString(SafePrimeBytes);
+            WriteBinaryString(SubGroupBytes);
         }
 
         internal override void Process(Session session)

+ 6 - 17
src/Renci.SshNet/Security/GroupExchangeHashData.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Numerics;
 
 using Renci.SshNet.Common;
 
@@ -9,8 +8,6 @@ namespace Renci.SshNet.Security
     {
         private byte[] _serverVersion;
         private byte[] _clientVersion;
-        private byte[] _prime;
-        private byte[] _subGroup;
 
         public string ServerVersion
         {
@@ -36,17 +33,9 @@ namespace Renci.SshNet.Security
 
         public uint MaximumGroupSize { get; set; }
 
-        public BigInteger Prime
-        {
-            private get { return _prime.ToBigInteger(); }
-            set { _prime = value.ToByteArray(isBigEndian: true); }
-        }
+        public byte[] Prime { get; set; }
 
-        public BigInteger SubGroup
-        {
-            private get { return _subGroup.ToBigInteger(); }
-            set { _subGroup = value.ToByteArray(isBigEndian: true); }
-        }
+        public byte[] SubGroup { get; set; }
 
         public byte[] ClientExchangeValue { get; set; }
 
@@ -79,9 +68,9 @@ namespace Renci.SshNet.Security
                 capacity += 4; // PreferredGroupSize
                 capacity += 4; // MaximumGroupSize
                 capacity += 4; // Prime length
-                capacity += _prime.Length; // Prime
+                capacity += Prime.Length; // Prime
                 capacity += 4; // SubGroup length
-                capacity += _subGroup.Length; // SubGroup
+                capacity += SubGroup.Length; // SubGroup
                 capacity += 4; // ClientExchangeValue length
                 capacity += ClientExchangeValue.Length; // ClientExchangeValue
                 capacity += 4; // ServerExchangeValue length
@@ -107,8 +96,8 @@ namespace Renci.SshNet.Security
             Write(MinimumGroupSize);
             Write(PreferredGroupSize);
             Write(MaximumGroupSize);
-            WriteBinaryString(_prime);
-            WriteBinaryString(_subGroup);
+            WriteBinaryString(Prime);
+            WriteBinaryString(SubGroup);
             WriteBinaryString(ClientExchangeValue);
             WriteBinaryString(ServerExchangeValue);
             WriteBinaryString(SharedKey);

+ 1 - 1
src/Renci.SshNet/Security/KeyExchange.cs

@@ -482,7 +482,7 @@ namespace Renci.SshNet.Security
         /// <summary>
         /// Validates the exchange hash.
         /// </summary>
-        /// <returns>true if exchange hash is valid; otherwise false.</returns>
+        /// <returns><see langword="true"/> if exchange hash is valid; otherwise <see langword="false"/>.</returns>
         protected abstract bool ValidateExchangeHash();
 
         private protected bool ValidateExchangeHash(byte[] encodedKey, byte[] encodedSignature)

+ 121 - 105
src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs

@@ -1,5 +1,10 @@
-using System;
-using System.Numerics;
+#nullable enable
+using System;
+using System.Security.Cryptography;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
 
 using Renci.SshNet.Abstractions;
 using Renci.SshNet.Common;
@@ -8,74 +13,65 @@ using Renci.SshNet.Messages.Transport;
 namespace Renci.SshNet.Security
 {
     /// <summary>
-    /// Represents base class for Diffie Hellman key exchange algorithm.
+    /// Provides the implementation of "diffie-hellman-groupN" algorithms.
     /// </summary>
-    internal abstract class KeyExchangeDiffieHellman : KeyExchange
+    public class KeyExchangeDiffieHellman : KeyExchange
     {
-#pragma warning disable SA1401 // Fields should be private
-        /// <summary>
-        /// Specifies key exchange group number.
-        /// </summary>
-        protected BigInteger _group;
-
-        /// <summary>
-        /// Specifies key exchange prime number.
-        /// </summary>
-        protected BigInteger _prime;
-
-        /// <summary>
-        /// Specifies client payload.
-        /// </summary>
-        protected byte[] _clientPayload;
+        private byte[]? _clientPayload;
+        private byte[]? _serverPayload;
+        private byte[]? _clientExchangeValue;
+        private byte[]? _serverExchangeValue;
+        private byte[]? _hostKey;
+        private byte[]? _signature;
 
-        /// <summary>
-        /// Specifies server payload.
-        /// </summary>
-        protected byte[] _serverPayload;
-
-        /// <summary>
-        /// Specifies client exchange number.
-        /// </summary>
-        protected byte[] _clientExchangeValue;
-
-        /// <summary>
-        /// Specifies server exchange number.
-        /// </summary>
-        protected byte[] _serverExchangeValue;
-
-        /// <summary>
-        /// Specifies random generated number.
-        /// </summary>
-        protected BigInteger _privateExponent;
-
-        /// <summary>
-        /// Specifies host key data.
-        /// </summary>
-        protected byte[] _hostKey;
+        /// <inheritdoc/>
+        public override string Name { get; }
 
-        /// <summary>
-        /// Specifies signature data.
-        /// </summary>
-        protected byte[] _signature;
-#pragma warning restore SA1401 // Fields should be private
+        private readonly DHParameters _dhParameters;
+#if NET462
+        private readonly HashAlgorithm _hash;
+#else
+        private readonly IncrementalHash _hash;
+#endif
 
-        /// <summary>
-        /// Gets the size, in bits, of the computed hash code.
-        /// </summary>
-        /// <value>
-        /// The size, in bits, of the computed hash code.
-        /// </value>
-        protected abstract int HashSize { get; }
+        private DHBasicAgreement? _keyAgreement;
 
         /// <summary>
-        /// Validates the exchange hash.
+        /// Initializes a new instance of the <see cref="KeyExchangeDiffieHellman"/> class.
         /// </summary>
-        /// <returns>
-        /// true if exchange hash is valid; otherwise false.
-        /// </returns>
-        protected override bool ValidateExchangeHash()
+        /// <param name="name">The name of the key exchange algorithm.</param>
+        /// <param name="parameters">The Diffie-Hellman parameters to be used.</param>
+        /// <param name="hashAlgorithm">The hash algorithm to be used.</param>
+        /// <exception cref="ArgumentNullException">
+        /// <paramref name="name"/>, <paramref name="parameters"/>, or <see cref="HashAlgorithmName.Name"/> is <see langword="null"/>.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// <paramref name="hashAlgorithm"/> is not a valid hash algorithm.
+        /// </exception>
+        public KeyExchangeDiffieHellman(
+            string name,
+            DHParameters parameters,
+            HashAlgorithmName hashAlgorithm)
         {
-            return ValidateExchangeHash(_hostKey, _signature);
+            ThrowHelper.ThrowIfNull(name);
+            ThrowHelper.ThrowIfNull(parameters);
+            ThrowHelper.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm));
+
+            Name = name;
+            _dhParameters = parameters;
+#if NET462
+            _hash = CryptoConfig.CreateFromName(hashAlgorithm.Name) as HashAlgorithm
+                ?? throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithm}`.", nameof(hashAlgorithm));
+#else
+            try
+            {
+                _hash = IncrementalHash.CreateHash(hashAlgorithm);
+            }
+            catch (CryptographicException cex)
+            {
+                throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithm}`.", nameof(hashAlgorithm), cex);
+            }
+#endif
         }
 
         /// <inheritdoc/>
@@ -85,66 +81,86 @@ namespace Renci.SshNet.Security
 
             _serverPayload = message.GetBytes();
             _clientPayload = Session.ClientInitMessage.GetBytes();
+
+            var g = new DHKeyPairGenerator();
+            g.Init(new DHKeyGenerationParameters(CryptoAbstraction.SecureRandom, _dhParameters));
+
+            var aKeyPair = g.GenerateKeyPair();
+
+            _keyAgreement = new DHBasicAgreement();
+            _keyAgreement.Init(aKeyPair.Private);
+            _clientExchangeValue = ((DHPublicKeyParameters)aKeyPair.Public).Y.ToByteArray();
+
+            Session.RegisterMessage("SSH_MSG_KEXDH_REPLY");
+
+            Session.KeyExchangeDhReplyMessageReceived += Session_KeyExchangeDhReplyMessageReceived;
+
+            SendMessage(new KeyExchangeDhInitMessage(_clientExchangeValue));
         }
 
-        /// <summary>
-        /// Populates the client exchange value.
-        /// </summary>
-        protected void PopulateClientExchangeValue()
+        /// <inheritdoc/>
+        protected override byte[] CalculateHash()
         {
-            if (_group.IsZero)
+            var keyExchangeHashData = new KeyExchangeHashData
             {
-                throw new ArgumentNullException("_group");
-            }
+                ClientVersion = Session.ClientVersion,
+                ServerVersion = Session.ServerVersion,
+                ClientPayload = _clientPayload,
+                ServerPayload = _serverPayload,
+                HostKey = _hostKey,
+                ClientExchangeValue = _clientExchangeValue,
+                ServerExchangeValue = _serverExchangeValue,
+                SharedKey = SharedKey,
+            };
+
+            return Hash(keyExchangeHashData.GetBytes());
+        }
 
-            if (_prime.IsZero)
-            {
-                throw new ArgumentNullException("_prime");
-            }
+        private void Session_KeyExchangeDhReplyMessageReceived(object? sender, MessageEventArgs<KeyExchangeDhReplyMessage> e)
+        {
+            var message = e.Message;
 
-            // generate private exponent that is twice the hash size (RFC 4419) with a minimum
-            // of 1024 bits (whatever is less)
-            var privateExponentSize = Math.Max(HashSize * 2, 1024);
+            Session.KeyExchangeDhReplyMessageReceived -= Session_KeyExchangeDhReplyMessageReceived;
 
-            BigInteger clientExchangeValue;
+            Session.UnRegisterMessage("SSH_MSG_KEXDH_REPLY");
 
-            do
-            {
-                // Create private component
-                _privateExponent = RandomBigInt(privateExponentSize);
+            _serverExchangeValue = message.F;
+            _hostKey = message.HostKey;
+            _signature = message.Signature;
 
-                // Generate public component
-                clientExchangeValue = BigInteger.ModPow(_group, _privateExponent, _prime);
-            }
-            while (clientExchangeValue < 1 || clientExchangeValue > (_prime - 1));
+            var publicKey = new DHPublicKeyParameters(new Org.BouncyCastle.Math.BigInteger(message.F), _dhParameters);
 
-            _clientExchangeValue = clientExchangeValue.ToByteArray(isBigEndian: true);
+            SharedKey = _keyAgreement!.CalculateAgreement(publicKey).ToByteArray();
+
+            Finish();
         }
 
-        /// <summary>
-        /// Generates a new, random <see cref="BigInteger"/> of the specified length.
-        /// </summary>
-        /// <param name="bitLength">The number of bits for the new number.</param>
-        /// <returns>A random number of the specified length.</returns>
-        private static BigInteger RandomBigInt(int bitLength)
+        /// <inheritdoc/>
+        protected override bool ValidateExchangeHash()
         {
-            var bytesArray = CryptoAbstraction.GenerateRandom((bitLength / 8) + (((bitLength % 8) > 0) ? 1 : 0));
-            bytesArray[bytesArray.Length - 1] = (byte)(bytesArray[bytesArray.Length - 1] & 0x7F); // Ensure not a negative value
-            return new BigInteger(bytesArray);
+            return ValidateExchangeHash(_hostKey, _signature);
         }
 
-        /// <summary>
-        /// Handles the server DH reply message.
-        /// </summary>
-        /// <param name="hostKey">The host key.</param>
-        /// <param name="serverExchangeValue">The server exchange value.</param>
-        /// <param name="signature">The signature.</param>
-        protected virtual void HandleServerDhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature)
+        /// <inheritdoc/>
+        protected override byte[] Hash(byte[] hashData)
+        {
+#if NET462
+            return _hash.ComputeHash(hashData);
+#else
+            _hash.AppendData(hashData);
+            return _hash.GetHashAndReset();
+#endif
+        }
+
+        /// <inheritdoc/>
+        protected override void Dispose(bool disposing)
         {
-            _serverExchangeValue = serverExchangeValue;
-            _hostKey = hostKey;
-            SharedKey = BigInteger.ModPow(serverExchangeValue.ToBigInteger(), _privateExponent, _prime).ToByteArray(isBigEndian: true);
-            _signature = signature;
+            if (disposing)
+            {
+                _hash.Dispose();
+            }
+
+            base.Dispose(disposing);
         }
     }
 }

+ 0 - 62
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs

@@ -1,62 +0,0 @@
-using System.Numerics;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Represents "diffie-hellman-group14-sha1" algorithm implementation.
-    /// </summary>
-    internal sealed class KeyExchangeDiffieHellmanGroup14Sha1 : KeyExchangeDiffieHellmanGroupSha1
-    {
-        /// <summary>
-        /// Defined in https://tools.ietf.org/html/rfc2409#section-6.2.
-        /// </summary>
-        private static readonly BigInteger SecondOkleyGroupReversed = new BigInteger(
-        [
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x68, 0xaa, 0xac, 0x8a,
-            0x5a, 0x8e, 0x72, 0x15, 0x10, 0x05, 0xfa, 0x98, 0x18, 0x26, 0xd2, 0x15,
-            0xe5, 0x6a, 0x95, 0xea, 0x7c, 0x49, 0x95, 0x39, 0x18, 0x17, 0x58, 0x95,
-            0xf6, 0xcb, 0x2b, 0xde, 0xc9, 0x52, 0x4c, 0x6f, 0xf0, 0x5d, 0xc5, 0xb5,
-            0x8f, 0xa2, 0x07, 0xec, 0xa2, 0x83, 0x27, 0x9b, 0x03, 0x86, 0x0e, 0x18,
-            0x2c, 0x77, 0x9e, 0xe3, 0x3b, 0xce, 0x36, 0x2e, 0x46, 0x5e, 0x90, 0x32,
-            0x7c, 0x21, 0x18, 0xca, 0x08, 0x6c, 0x74, 0xf1, 0x04, 0x98, 0xbc, 0x4a,
-            0x4e, 0x35, 0x0c, 0x67, 0x6d, 0x96, 0x96, 0x70, 0x07, 0x29, 0xd5, 0x9e,
-            0xbb, 0x52, 0x85, 0x20, 0x56, 0xf3, 0x62, 0x1c, 0x96, 0xad, 0xa3, 0xdc,
-            0x23, 0x5d, 0x65, 0x83, 0x5f, 0xcf, 0x24, 0xfd, 0xa8, 0x3f, 0x16, 0x69,
-            0x9a, 0xd3, 0x55, 0x1c, 0x36, 0x48, 0xda, 0x98, 0x05, 0xbf, 0x63, 0xa1,
-            0xb8, 0x7c, 0x00, 0xc2, 0x3d, 0x5b, 0xe4, 0xec, 0x51, 0x66, 0x28, 0x49,
-            0xe6, 0x1f, 0x4b, 0x7c, 0x11, 0x24, 0x9f, 0xae, 0xa5, 0x9f, 0x89, 0x5a,
-            0xfb, 0x6b, 0x38, 0xee, 0xed, 0xb7, 0x06, 0xf4, 0xb6, 0x5c, 0xff, 0x0b,
-            0x6b, 0xed, 0x37, 0xa6, 0xe9, 0x42, 0x4c, 0xf4, 0xc6, 0x7e, 0x5e, 0x62,
-            0x76, 0xb5, 0x85, 0xe4, 0x45, 0xc2, 0x51, 0x6d, 0x6d, 0x35, 0xe1, 0x4f,
-            0x37, 0x14, 0x5f, 0xf2, 0x6d, 0x0a, 0x2b, 0x30, 0x1b, 0x43, 0x3a, 0xcd,
-            0xb3, 0x19, 0x95, 0xef, 0xdd, 0x04, 0x34, 0x8e, 0x79, 0x08, 0x4a, 0x51,
-            0x22, 0x9b, 0x13, 0x3b, 0xa6, 0xbe, 0x0b, 0x02, 0x74, 0xcc, 0x67, 0x8a,
-            0x08, 0x4e, 0x02, 0x29, 0xd1, 0x1c, 0xdc, 0x80, 0x8b, 0x62, 0xc6, 0xc4,
-            0x34, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff,
-            0x00
-        ]);
-
-        /// <summary>
-        /// Gets algorithm name.
-        /// </summary>
-        public override string Name
-        {
-            get { return "diffie-hellman-group14-sha1"; }
-        }
-
-        /// <summary>
-        /// Gets the group prime.
-        /// </summary>
-        /// <value>
-        /// The group prime.
-        /// </value>
-        public override BigInteger GroupPrime
-        {
-            get
-            {
-                return SecondOkleyGroupReversed;
-            }
-        }
-    }
-}

+ 0 - 62
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha256.cs

@@ -1,62 +0,0 @@
-using System.Numerics;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Represents "diffie-hellman-group14-sha256" algorithm implementation.
-    /// </summary>
-    internal sealed class KeyExchangeDiffieHellmanGroup14Sha256 : KeyExchangeDiffieHellmanGroupSha256
-    {
-        /// <summary>
-        /// Defined in https://tools.ietf.org/html/rfc2409#section-6.2.
-        /// </summary>
-        private static readonly BigInteger SecondOkleyGroupReversed = new BigInteger(
-        [
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x68, 0xaa, 0xac, 0x8a,
-            0x5a, 0x8e, 0x72, 0x15, 0x10, 0x05, 0xfa, 0x98, 0x18, 0x26, 0xd2, 0x15,
-            0xe5, 0x6a, 0x95, 0xea, 0x7c, 0x49, 0x95, 0x39, 0x18, 0x17, 0x58, 0x95,
-            0xf6, 0xcb, 0x2b, 0xde, 0xc9, 0x52, 0x4c, 0x6f, 0xf0, 0x5d, 0xc5, 0xb5,
-            0x8f, 0xa2, 0x07, 0xec, 0xa2, 0x83, 0x27, 0x9b, 0x03, 0x86, 0x0e, 0x18,
-            0x2c, 0x77, 0x9e, 0xe3, 0x3b, 0xce, 0x36, 0x2e, 0x46, 0x5e, 0x90, 0x32,
-            0x7c, 0x21, 0x18, 0xca, 0x08, 0x6c, 0x74, 0xf1, 0x04, 0x98, 0xbc, 0x4a,
-            0x4e, 0x35, 0x0c, 0x67, 0x6d, 0x96, 0x96, 0x70, 0x07, 0x29, 0xd5, 0x9e,
-            0xbb, 0x52, 0x85, 0x20, 0x56, 0xf3, 0x62, 0x1c, 0x96, 0xad, 0xa3, 0xdc,
-            0x23, 0x5d, 0x65, 0x83, 0x5f, 0xcf, 0x24, 0xfd, 0xa8, 0x3f, 0x16, 0x69,
-            0x9a, 0xd3, 0x55, 0x1c, 0x36, 0x48, 0xda, 0x98, 0x05, 0xbf, 0x63, 0xa1,
-            0xb8, 0x7c, 0x00, 0xc2, 0x3d, 0x5b, 0xe4, 0xec, 0x51, 0x66, 0x28, 0x49,
-            0xe6, 0x1f, 0x4b, 0x7c, 0x11, 0x24, 0x9f, 0xae, 0xa5, 0x9f, 0x89, 0x5a,
-            0xfb, 0x6b, 0x38, 0xee, 0xed, 0xb7, 0x06, 0xf4, 0xb6, 0x5c, 0xff, 0x0b,
-            0x6b, 0xed, 0x37, 0xa6, 0xe9, 0x42, 0x4c, 0xf4, 0xc6, 0x7e, 0x5e, 0x62,
-            0x76, 0xb5, 0x85, 0xe4, 0x45, 0xc2, 0x51, 0x6d, 0x6d, 0x35, 0xe1, 0x4f,
-            0x37, 0x14, 0x5f, 0xf2, 0x6d, 0x0a, 0x2b, 0x30, 0x1b, 0x43, 0x3a, 0xcd,
-            0xb3, 0x19, 0x95, 0xef, 0xdd, 0x04, 0x34, 0x8e, 0x79, 0x08, 0x4a, 0x51,
-            0x22, 0x9b, 0x13, 0x3b, 0xa6, 0xbe, 0x0b, 0x02, 0x74, 0xcc, 0x67, 0x8a,
-            0x08, 0x4e, 0x02, 0x29, 0xd1, 0x1c, 0xdc, 0x80, 0x8b, 0x62, 0xc6, 0xc4,
-            0x34, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0xff, 0xff, 0xff, 0xff,
-            0xff, 0xff, 0xff, 0xff,
-            0x00
-        ]);
-
-        /// <summary>
-        /// Gets algorithm name.
-        /// </summary>
-        public override string Name
-        {
-            get { return "diffie-hellman-group14-sha256"; }
-        }
-
-        /// <summary>
-        /// Gets the group prime.
-        /// </summary>
-        /// <value>
-        /// The group prime.
-        /// </value>
-        public override BigInteger GroupPrime
-        {
-            get
-            {
-                return SecondOkleyGroupReversed;
-            }
-        }
-    }
-}

+ 0 - 66
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup16Sha512.cs

@@ -1,66 +0,0 @@
-using System.Numerics;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Represents "diffie-hellman-group16-sha512" algorithm implementation.
-    /// </summary>
-    internal sealed class KeyExchangeDiffieHellmanGroup16Sha512 : KeyExchangeDiffieHellmanGroupSha512
-    {
-        /// <summary>
-        /// Defined in https://tools.ietf.org/html/rfc3526#section-5.
-        /// </summary>
-        private static readonly BigInteger MoreModularExponentialGroup16Reversed = new BigInteger(
-        [
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x99, 0x31, 0x06, 0x34, 0xc9, 0x35, 0xf4, 0x4d,
-            0x8f, 0xc0, 0xa6, 0x90, 0xdc, 0xb7, 0xff, 0x86, 0xc1, 0xdd, 0x8f, 0x8d, 0x98, 0xea, 0xb4, 0x93,
-            0xa9, 0x5a, 0xb0, 0xd5, 0x27, 0x91, 0x06, 0xd0, 0x1c, 0x48, 0x70, 0x21, 0x76, 0xdd, 0x1b, 0xb8,
-            0xaf, 0xd7, 0xe2, 0xce, 0x70, 0x29, 0x61, 0x1f, 0xed, 0xe7, 0x5b, 0x51, 0x86, 0xa1, 0x3b, 0x23,
-            0xa2, 0xc3, 0x90, 0xa0, 0x4f, 0x96, 0xb2, 0x99, 0x5d, 0xc0, 0x6b, 0x4e, 0x47, 0x59, 0x7c, 0x28,
-            0xa6, 0xca, 0xbe, 0x1f, 0x14, 0xfc, 0x8e, 0x2e, 0xf9, 0x8e, 0xde, 0x04, 0xdb, 0xc2, 0xbb, 0xdb,
-            0xe8, 0x4c, 0xd4, 0x2a, 0xca, 0xe9, 0x83, 0x25, 0xda, 0x0b, 0x15, 0xb6, 0x34, 0x68, 0x94, 0x1a,
-            0x3c, 0xe2, 0xf4, 0x6a, 0x18, 0x27, 0xc3, 0x99, 0x26, 0x5b, 0xba, 0xbd, 0x10, 0x9a, 0x71, 0x88,
-            0xd7, 0xe6, 0x87, 0xa7, 0x12, 0x3c, 0x72, 0x1a, 0x01, 0x08, 0x21, 0xa9, 0x20, 0xd1, 0x82, 0x4b,
-            0x8e, 0x10, 0xfd, 0xe0, 0xfc, 0x5b, 0xdb, 0x43, 0x31, 0xab, 0xe5, 0x74, 0xa0, 0x4f, 0xe2, 0x08,
-            0xe2, 0x46, 0xd9, 0xba, 0xc0, 0x88, 0x09, 0x77, 0x6c, 0x5d, 0x61, 0x7a, 0x57, 0x17, 0xe1, 0xbb,
-            0x0c, 0x20, 0x7b, 0x17, 0x18, 0x2b, 0x1f, 0x52, 0x64, 0x6a, 0xc8, 0x3e, 0x73, 0x02, 0x76, 0xd8,
-            0x64, 0x08, 0x8a, 0xd9, 0x06, 0xfa, 0x2f, 0xf1, 0x6b, 0xee, 0xd2, 0x1a, 0x26, 0xd2, 0xe3, 0xce,
-            0x9d, 0x61, 0x25, 0x4a, 0xe0, 0x94, 0x8c, 0x1e, 0xd7, 0x33, 0x09, 0xdb, 0x8c, 0xae, 0xf5, 0xab,
-            0xc7, 0xe4, 0xe1, 0xa6, 0x85, 0x0f, 0x97, 0xb3, 0x7d, 0x0c, 0x06, 0x5d, 0x57, 0x71, 0xea, 0x8a,
-            0x0a, 0xef, 0xdb, 0x58, 0x04, 0x85, 0xfb, 0xec, 0x64, 0xba, 0x1c, 0xdf, 0xab, 0x21, 0x55, 0xa8,
-            0x33, 0x7a, 0x50, 0x04, 0x0d, 0x17, 0x33, 0xad, 0x2d, 0xc4, 0xaa, 0x8a, 0x5a, 0x8e, 0x72, 0x15,
-            0x10, 0x05, 0xfa, 0x98, 0x18, 0x26, 0xd2, 0x15, 0xe5, 0x6a, 0x95, 0xea, 0x7c, 0x49, 0x95, 0x39,
-            0x18, 0x17, 0x58, 0x95, 0xf6, 0xcb, 0x2b, 0xde, 0xc9, 0x52, 0x4c, 0x6f, 0xf0, 0x5d, 0xc5, 0xb5,
-            0x8f, 0xa2, 0x07, 0xec, 0xa2, 0x83, 0x27, 0x9b, 0x03, 0x86, 0x0e, 0x18, 0x2c, 0x77, 0x9e, 0xe3,
-            0x3b, 0xce, 0x36, 0x2e, 0x46, 0x5e, 0x90, 0x32, 0x7c, 0x21, 0x18, 0xca, 0x08, 0x6c, 0x74, 0xf1,
-            0x04, 0x98, 0xbc, 0x4a, 0x4e, 0x35, 0x0c, 0x67, 0x6d, 0x96, 0x96, 0x70, 0x07, 0x29, 0xd5, 0x9e,
-            0xbb, 0x52, 0x85, 0x20, 0x56, 0xf3, 0x62, 0x1c, 0x96, 0xad, 0xa3, 0xdc, 0x23, 0x5d, 0x65, 0x83,
-            0x5f, 0xcf, 0x24, 0xfd, 0xa8, 0x3f, 0x16, 0x69, 0x9a, 0xd3, 0x55, 0x1c, 0x36, 0x48, 0xda, 0x98,
-            0x05, 0xbf, 0x63, 0xa1, 0xb8, 0x7c, 0x00, 0xc2, 0x3d, 0x5b, 0xe4, 0xec, 0x51, 0x66, 0x28, 0x49,
-            0xe6, 0x1f, 0x4b, 0x7c, 0x11, 0x24, 0x9f, 0xae, 0xa5, 0x9f, 0x89, 0x5a, 0xfb, 0x6b, 0x38, 0xee,
-            0xed, 0xb7, 0x06, 0xf4, 0xb6, 0x5c, 0xff, 0x0b, 0x6b, 0xed, 0x37, 0xa6, 0xe9, 0x42, 0x4c, 0xf4,
-            0xc6, 0x7e, 0x5e, 0x62, 0x76, 0xb5, 0x85, 0xe4, 0x45, 0xc2, 0x51, 0x6d, 0x6d, 0x35, 0xe1, 0x4f,
-            0x37, 0x14, 0x5f, 0xf2, 0x6d, 0x0a, 0x2b, 0x30, 0x1b, 0x43, 0x3a, 0xcd, 0xb3, 0x19, 0x95, 0xef,
-            0xdd, 0x04, 0x34, 0x8e, 0x79, 0x08, 0x4a, 0x51, 0x22, 0x9b, 0x13, 0x3b, 0xa6, 0xbe, 0x0b, 0x02,
-            0x74, 0xcc, 0x67, 0x8a, 0x08, 0x4e, 0x02, 0x29, 0xd1, 0x1c, 0xdc, 0x80, 0x8b, 0x62, 0xc6, 0xc4,
-            0x34, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-            0x00
-        ]);
-
-        public override BigInteger GroupPrime
-        {
-            get
-            {
-                return MoreModularExponentialGroup16Reversed;
-            }
-        }
-
-        /// <summary>
-        /// Gets algorithm name.
-        /// </summary>
-        public override string Name
-        {
-            get { return "diffie-hellman-group16-sha512"; }
-        }
-    }
-}

+ 0 - 48
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs

@@ -1,48 +0,0 @@
-using System.Numerics;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Represents "diffie-hellman-group1-sha1" algorithm implementation.
-    /// </summary>
-    internal sealed class KeyExchangeDiffieHellmanGroup1Sha1 : KeyExchangeDiffieHellmanGroupSha1
-    {
-        private static readonly BigInteger SecondOkleyGroupReversed = new BigInteger(
-        [
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0x53, 0xe6, 0xec,
-            0x51, 0x66, 0x28, 0x49, 0xe6, 0x1f, 0x4b, 0x7c, 0x11, 0x24, 0x9f, 0xae,
-            0xa5, 0x9f, 0x89, 0x5a, 0xfb, 0x6b, 0x38, 0xee, 0xed, 0xb7, 0x06, 0xf4,
-            0xb6, 0x5c, 0xff, 0x0b, 0x6b, 0xed, 0x37, 0xa6, 0xe9, 0x42, 0x4c, 0xf4,
-            0xc6, 0x7e, 0x5e, 0x62, 0x76, 0xb5, 0x85, 0xe4, 0x45, 0xc2, 0x51, 0x6d,
-            0x6d, 0x35, 0xe1, 0x4f, 0x37, 0x14, 0x5f, 0xf2, 0x6d, 0x0a, 0x2b, 0x30,
-            0x1b, 0x43, 0x3a, 0xcd, 0xb3, 0x19, 0x95, 0xef, 0xdd, 0x04, 0x34, 0x8e,
-            0x79, 0x08, 0x4a, 0x51, 0x22, 0x9b, 0x13, 0x3b, 0xa6, 0xbe, 0x0b, 0x02,
-            0x74, 0xcc, 0x67, 0x8a, 0x08, 0x4e, 0x02, 0x29, 0xd1, 0x1c, 0xdc, 0x80,
-            0x8b, 0x62, 0xc6, 0xc4, 0x34, 0xc2, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9,
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-            0x00
-        ]);
-
-        /// <summary>
-        /// Gets algorithm name.
-        /// </summary>
-        public override string Name
-        {
-            get { return "diffie-hellman-group1-sha1"; }
-        }
-
-        /// <summary>
-        /// Gets the group prime.
-        /// </summary>
-        /// <value>
-        /// The group prime.
-        /// </value>
-        public override BigInteger GroupPrime
-        {
-            get
-            {
-                return SecondOkleyGroupReversed;
-            }
-        }
-    }
-}

+ 244 - 0
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchange.cs

@@ -0,0 +1,244 @@
+#nullable enable
+using System;
+using System.Security.Cryptography;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+
+using Renci.SshNet.Abstractions;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Transport;
+
+namespace Renci.SshNet.Security
+{
+    /// <summary>
+    /// Provides the implementation of "diffie-hellman-group-exchange" algorithms.
+    /// </summary>
+    public class KeyExchangeDiffieHellmanGroupExchange : KeyExchange
+    {
+        private byte[]? _clientPayload;
+        private byte[]? _serverPayload;
+        private byte[]? _clientExchangeValue;
+        private byte[]? _serverExchangeValue;
+        private byte[]? _prime;
+        private byte[]? _group;
+        private byte[]? _hostKey;
+        private byte[]? _signature;
+
+        /// <inheritdoc/>
+        public override string Name { get; }
+#if NET
+        private readonly IncrementalHash _hash;
+#else
+        private readonly HashAlgorithm _hash;
+#endif
+        private readonly int _hashLengthInBits;
+        private readonly uint _minimumGroupSize;
+        private readonly uint _preferredGroupSize;
+        private readonly uint _maximumGroupSize;
+
+        private DHParameters? _dhParameters;
+        private DHBasicAgreement? _keyAgreement;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="KeyExchangeDiffieHellmanGroupExchange"/> class.
+        /// </summary>
+        /// <param name="name">The name of the key exchange algorithm.</param>
+        /// <param name="hashAlgorithm">The hash algorithm to be used.</param>
+        /// <exception cref="ArgumentNullException">
+        /// <paramref name="name"/> or <see cref="HashAlgorithmName.Name"/> is <see langword="null"/>.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// <paramref name="hashAlgorithm"/> is not a valid hash algorithm.
+        /// </exception>
+        public KeyExchangeDiffieHellmanGroupExchange(
+            string name,
+            HashAlgorithmName hashAlgorithm)
+            : this(name, hashAlgorithm, 2048, 4096, 8192)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="KeyExchangeDiffieHellmanGroupExchange"/> class.
+        /// </summary>
+        /// <param name="name">The name of the key exchange algorithm.</param>
+        /// <param name="hashAlgorithm">The hash algorithm to be used.</param>
+        /// <param name="minimumGroupSize">The minimum size in bits of an acceptable group.</param>
+        /// <param name="preferredGroupSize">The preferred size in bits of an acceptable group.</param>
+        /// <param name="maximumGroupSize">The maximum size in bits of an acceptable group.</param>
+        /// <exception cref="ArgumentNullException">
+        /// <paramref name="name"/> or <see cref="HashAlgorithmName.Name"/> is <see langword="null"/>.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// <paramref name="hashAlgorithm"/> is not a valid hash algorithm.
+        /// </exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// <paramref name="preferredGroupSize"/> is not between <paramref name="minimumGroupSize"/> and <paramref name="maximumGroupSize"/>.
+        /// </exception>
+        public KeyExchangeDiffieHellmanGroupExchange(
+            string name,
+            HashAlgorithmName hashAlgorithm,
+            uint minimumGroupSize,
+            uint preferredGroupSize,
+            uint maximumGroupSize)
+        {
+            ThrowHelper.ThrowIfNull(name);
+            ThrowHelper.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm));
+
+            if (preferredGroupSize < minimumGroupSize || preferredGroupSize > maximumGroupSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(preferredGroupSize));
+            }
+
+            Name = name;
+            _minimumGroupSize = minimumGroupSize;
+            _preferredGroupSize = preferredGroupSize;
+            _maximumGroupSize = maximumGroupSize;
+#if NET
+            try
+            {
+                _hash = IncrementalHash.CreateHash(hashAlgorithm);
+                _hashLengthInBits = _hash.HashLengthInBytes * 8;
+            }
+            catch (CryptographicException cex)
+            {
+                throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithm}`.", nameof(hashAlgorithm), cex);
+            }
+#else
+            _hash = CryptoConfig.CreateFromName(hashAlgorithm.Name) as HashAlgorithm
+                ?? throw new ArgumentException($"Could not create {nameof(HashAlgorithm)} from `{hashAlgorithm}`.", nameof(hashAlgorithm));
+            _hashLengthInBits = _hash.HashSize;
+#endif
+        }
+
+        /// <inheritdoc/>
+        protected override byte[] CalculateHash()
+        {
+            var groupExchangeHashData = new GroupExchangeHashData
+            {
+                ClientVersion = Session.ClientVersion,
+                ServerVersion = Session.ServerVersion,
+                ClientPayload = _clientPayload,
+                ServerPayload = _serverPayload,
+                HostKey = _hostKey,
+                MinimumGroupSize = _minimumGroupSize,
+                PreferredGroupSize = _preferredGroupSize,
+                MaximumGroupSize = _maximumGroupSize,
+                Prime = _prime,
+                SubGroup = _group,
+                ClientExchangeValue = _clientExchangeValue,
+                ServerExchangeValue = _serverExchangeValue,
+                SharedKey = SharedKey,
+            };
+
+            return Hash(groupExchangeHashData.GetBytes());
+        }
+
+        /// <inheritdoc/>
+        public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage)
+        {
+            base.Start(session, message, sendClientInitMessage);
+
+            _serverPayload = message.GetBytes();
+            _clientPayload = Session.ClientInitMessage.GetBytes();
+
+            Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP");
+
+            Session.KeyExchangeDhGroupExchangeGroupReceived += Session_KeyExchangeDhGroupExchangeGroupReceived;
+
+            // 1. client sends SSH_MSG_KEY_DH_GEX_REQUEST
+            SendMessage(new KeyExchangeDhGroupExchangeRequest(_minimumGroupSize, _preferredGroupSize, _maximumGroupSize));
+        }
+
+        private void Session_KeyExchangeDhGroupExchangeGroupReceived(object? sender, MessageEventArgs<KeyExchangeDhGroupExchangeGroup> e)
+        {
+            // 2. server sends SSH_MSG_KEX_DH_GEX_GROUP
+            var groupMessage = e.Message;
+
+            Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP");
+
+            Session.KeyExchangeDhGroupExchangeGroupReceived -= Session_KeyExchangeDhGroupExchangeGroupReceived;
+
+            _prime = groupMessage.SafePrimeBytes;
+            _group = groupMessage.SubGroupBytes;
+
+            // https://datatracker.ietf.org/doc/html/rfc4419#section-6.2
+            var minimumBitLength = 2 * _hashLengthInBits;
+
+            _dhParameters = new DHParameters(
+                new Org.BouncyCastle.Math.BigInteger(_prime),
+                new Org.BouncyCastle.Math.BigInteger(_group),
+                q: null,
+                m: minimumBitLength,
+                l: 0);
+
+            var g = new DHKeyPairGenerator();
+            g.Init(new DHKeyGenerationParameters(CryptoAbstraction.SecureRandom, _dhParameters));
+
+            var aKeyPair = g.GenerateKeyPair();
+
+            _keyAgreement = new DHBasicAgreement();
+            _keyAgreement.Init(aKeyPair.Private);
+            _clientExchangeValue = ((DHPublicKeyParameters)aKeyPair.Public).Y.ToByteArray();
+
+            Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY");
+
+            Session.KeyExchangeDhGroupExchangeReplyReceived += Session_KeyExchangeDhGroupExchangeReplyReceived;
+
+            // 3. client sends SSH_MSG_KEX_DH_GEX_INIT
+            SendMessage(new KeyExchangeDhGroupExchangeInit(_clientExchangeValue));
+        }
+
+        private void Session_KeyExchangeDhGroupExchangeReplyReceived(object? sender, MessageEventArgs<KeyExchangeDhGroupExchangeReply> e)
+        {
+            // 4. server sends SSH_MSG_KEX_DH_GEX_REPLY
+            var message = e.Message;
+
+            Session.KeyExchangeDhGroupExchangeReplyReceived -= Session_KeyExchangeDhGroupExchangeReplyReceived;
+
+            Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY");
+
+            Session.KeyExchangeDhGroupExchangeReplyReceived -= Session_KeyExchangeDhGroupExchangeReplyReceived;
+
+            _serverExchangeValue = message.F;
+            _hostKey = message.HostKey;
+            _signature = message.Signature;
+
+            var publicKey = new DHPublicKeyParameters(new Org.BouncyCastle.Math.BigInteger(message.F), _dhParameters);
+
+            SharedKey = _keyAgreement!.CalculateAgreement(publicKey).ToByteArray();
+
+            // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed
+            Finish();
+        }
+
+        /// <inheritdoc/>
+        protected override bool ValidateExchangeHash()
+        {
+            return ValidateExchangeHash(_hostKey, _signature);
+        }
+
+        /// <inheritdoc/>
+        protected override byte[] Hash(byte[] hashData)
+        {
+#if NET
+            _hash.AppendData(hashData);
+            return _hash.GetHashAndReset();
+#else
+            return _hash.ComputeHash(hashData);
+#endif
+        }
+
+        /// <inheritdoc/>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                _hash.Dispose();
+            }
+
+            base.Dispose(disposing);
+        }
+    }
+}

+ 0 - 41
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs

@@ -1,41 +0,0 @@
-using Renci.SshNet.Abstractions;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Represents "diffie-hellman-group-exchange-sha1" algorithm implementation.
-    /// </summary>
-    internal sealed class KeyExchangeDiffieHellmanGroupExchangeSha1 : KeyExchangeDiffieHellmanGroupExchangeShaBase
-    {
-        /// <summary>
-        /// Gets algorithm name.
-        /// </summary>
-        public override string Name
-        {
-            get { return "diffie-hellman-group-exchange-sha1"; }
-        }
-
-        /// <summary>
-        /// Gets the size, in bits, of the computed hash code.
-        /// </summary>
-        /// <value>
-        /// The size, in bits, of the computed hash code.
-        /// </value>
-        protected override int HashSize
-        {
-            get { return 160; }
-        }
-
-        /// <summary>
-        /// Hashes the specified data bytes.
-        /// </summary>
-        /// <param name="hashData">The hash data.</param>
-        /// <returns>
-        /// The hash of the data.
-        /// </returns>
-        protected override byte[] Hash(byte[] hashData)
-        {
-            return CryptoAbstraction.HashSHA1(hashData);
-        }
-    }
-}

+ 0 - 41
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs

@@ -1,41 +0,0 @@
-using Renci.SshNet.Abstractions;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Represents "diffie-hellman-group-exchange-sha256" algorithm implementation.
-    /// </summary>
-    internal sealed class KeyExchangeDiffieHellmanGroupExchangeSha256 : KeyExchangeDiffieHellmanGroupExchangeShaBase
-    {
-        /// <summary>
-        /// Gets algorithm name.
-        /// </summary>
-        public override string Name
-        {
-            get { return "diffie-hellman-group-exchange-sha256"; }
-        }
-
-        /// <summary>
-        /// Gets the size, in bits, of the computed hash code.
-        /// </summary>
-        /// <value>
-        /// The size, in bits, of the computed hash code.
-        /// </value>
-        protected override int HashSize
-        {
-            get { return 256; }
-        }
-
-        /// <summary>
-        /// Hashes the specified data bytes.
-        /// </summary>
-        /// <param name="hashData">Data to hash.</param>
-        /// <returns>
-        /// The hash of the data.
-        /// </returns>
-        protected override byte[] Hash(byte[] hashData)
-        {
-            return CryptoAbstraction.HashSHA256(hashData);
-        }
-    }
-}

+ 0 - 111
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeShaBase.cs

@@ -1,111 +0,0 @@
-using Renci.SshNet.Messages.Transport;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Base class for "diffie-hellman-group-exchange" algorithms.
-    /// </summary>
-    internal abstract class KeyExchangeDiffieHellmanGroupExchangeShaBase : KeyExchangeDiffieHellman
-    {
-        private const int MinimumGroupSize = 1024;
-        private const int PreferredGroupSize = 1024;
-        private const int MaximumProupSize = 8192;
-
-        /// <summary>
-        /// Calculates key exchange hash value.
-        /// </summary>
-        /// <returns>
-        /// Key exchange hash.
-        /// </returns>
-        protected override byte[] CalculateHash()
-        {
-            var groupExchangeHashData = new GroupExchangeHashData
-            {
-                ClientVersion = Session.ClientVersion,
-                ServerVersion = Session.ServerVersion,
-                ClientPayload = _clientPayload,
-                ServerPayload = _serverPayload,
-                HostKey = _hostKey,
-                MinimumGroupSize = MinimumGroupSize,
-                PreferredGroupSize = PreferredGroupSize,
-                MaximumGroupSize = MaximumProupSize,
-                Prime = _prime,
-                SubGroup = _group,
-                ClientExchangeValue = _clientExchangeValue,
-                ServerExchangeValue = _serverExchangeValue,
-                SharedKey = SharedKey,
-            };
-
-            return Hash(groupExchangeHashData.GetBytes());
-        }
-
-        /// <inheritdoc/>
-        public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage)
-        {
-            base.Start(session, message, sendClientInitMessage);
-
-            // Register SSH_MSG_KEX_DH_GEX_GROUP message
-            Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP");
-
-            // Subscribe to KeyExchangeDhGroupExchangeGroupReceived events
-            Session.KeyExchangeDhGroupExchangeGroupReceived += Session_KeyExchangeDhGroupExchangeGroupReceived;
-
-            // 1. client sends SSH_MSG_KEY_DH_GEX_REQUEST
-            SendMessage(new KeyExchangeDhGroupExchangeRequest(MinimumGroupSize, PreferredGroupSize, MaximumProupSize));
-        }
-
-        /// <summary>
-        /// Finishes key exchange algorithm.
-        /// </summary>
-        public override void Finish()
-        {
-            base.Finish();
-
-            Session.KeyExchangeDhGroupExchangeGroupReceived -= Session_KeyExchangeDhGroupExchangeGroupReceived;
-            Session.KeyExchangeDhGroupExchangeReplyReceived -= Session_KeyExchangeDhGroupExchangeReplyReceived;
-        }
-
-        private void Session_KeyExchangeDhGroupExchangeGroupReceived(object sender, MessageEventArgs<KeyExchangeDhGroupExchangeGroup> e)
-        {
-            // 2. server sends SSH_MSG_KEX_DH_GEX_GROUP
-            var groupMessage = e.Message;
-
-            // Unregister SSH_MSG_KEX_DH_GEX_GROUP message once received
-            Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP");
-
-            // Unsubscribe from KeyExchangeDhGroupExchangeGroupReceived events
-            Session.KeyExchangeDhGroupExchangeGroupReceived -= Session_KeyExchangeDhGroupExchangeGroupReceived;
-
-            // Register in order to be able to receive SSH_MSG_KEX_DH_GEX_REPLY message
-            Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY");
-
-            // Subscribe to KeyExchangeDhGroupExchangeReplyReceived events
-            Session.KeyExchangeDhGroupExchangeReplyReceived += Session_KeyExchangeDhGroupExchangeReplyReceived;
-
-            _prime = groupMessage.SafePrime;
-            _group = groupMessage.SubGroup;
-
-            PopulateClientExchangeValue();
-
-            // 3. client sends SSH_MSG_KEX_DH_GEX_INIT
-            SendMessage(new KeyExchangeDhGroupExchangeInit(_clientExchangeValue));
-        }
-
-        private void Session_KeyExchangeDhGroupExchangeReplyReceived(object sender, MessageEventArgs<KeyExchangeDhGroupExchangeReply> e)
-        {
-            // 4. server sends SSH_MSG_KEX_DH_GEX_REPLY
-            var replyMessage = e.Message;
-
-            // Unregister SSH_MSG_KEX_DH_GEX_REPLY message once received
-            Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY");
-
-            // Unsubscribe from KeyExchangeDhGroupExchangeReplyReceived events
-            Session.KeyExchangeDhGroupExchangeReplyReceived -= Session_KeyExchangeDhGroupExchangeReplyReceived;
-
-            HandleServerDhReply(replyMessage.HostKey, replyMessage.F, replyMessage.Signature);
-
-            // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed
-            Finish();
-        }
-    }
-}

+ 0 - 33
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs

@@ -1,33 +0,0 @@
-using Renci.SshNet.Abstractions;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Represents "diffie-hellman-group1-sha1" algorithm implementation.
-    /// </summary>
-    internal abstract class KeyExchangeDiffieHellmanGroupSha1 : KeyExchangeDiffieHellmanGroupShaBase
-    {
-        /// <summary>
-        /// Gets the size, in bits, of the computed hash code.
-        /// </summary>
-        /// <value>
-        /// The size, in bits, of the computed hash code.
-        /// </value>
-        protected override int HashSize
-        {
-            get { return 160; }
-        }
-
-        /// <summary>
-        /// Hashes the specified data bytes.
-        /// </summary>
-        /// <param name="hashData">The hash data.</param>
-        /// <returns>
-        /// The hash of the data.
-        /// </returns>
-        protected override byte[] Hash(byte[] hashData)
-        {
-            return CryptoAbstraction.HashSHA1(hashData);
-        }
-    }
-}

+ 0 - 33
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha256.cs

@@ -1,33 +0,0 @@
-using Renci.SshNet.Abstractions;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Base class for "diffie-hellman" SHA-256 group algorithm implementations.
-    /// </summary>
-    internal abstract class KeyExchangeDiffieHellmanGroupSha256 : KeyExchangeDiffieHellmanGroupShaBase
-    {
-        /// <summary>
-        /// Gets the size, in bits, of the computed hash code.
-        /// </summary>
-        /// <value>
-        /// The size, in bits, of the computed hash code.
-        /// </value>
-        protected override int HashSize
-        {
-            get { return 256; }
-        }
-
-        /// <summary>
-        /// Hashes the specified data bytes.
-        /// </summary>
-        /// <param name="hashData">The hash data.</param>
-        /// <returns>
-        /// The hash of the data.
-        /// </returns>
-        protected override byte[] Hash(byte[] hashData)
-        {
-            return CryptoAbstraction.HashSHA256(hashData);
-        }
-    }
-}

+ 0 - 33
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha512.cs

@@ -1,33 +0,0 @@
-using Renci.SshNet.Abstractions;
-
-namespace Renci.SshNet.Security
-{
-    /// <summary>
-    /// Base class for "diffie-hellman" SHA-512 group algorithm implementations.
-    /// </summary>
-    internal abstract class KeyExchangeDiffieHellmanGroupSha512 : KeyExchangeDiffieHellmanGroupShaBase
-    {
-        /// <summary>
-        /// Gets the size, in bits, of the computed hash code.
-        /// </summary>
-        /// <value>
-        /// The size, in bits, of the computed hash code.
-        /// </value>
-        protected override int HashSize
-        {
-            get { return 512; }
-        }
-
-        /// <summary>
-        /// Hashes the specified data bytes.
-        /// </summary>
-        /// <param name="hashData">The hash data.</param>
-        /// <returns>
-        /// The hash of the data.
-        /// </returns>
-        protected override byte[] Hash(byte[] hashData)
-        {
-            return CryptoAbstraction.HashSHA512(hashData);
-        }
-    }
-}

+ 0 - 80
src/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupShaBase.cs

@@ -1,80 +0,0 @@
-using System.Numerics;
-
-using Renci.SshNet.Messages.Transport;
-
-namespace Renci.SshNet.Security
-{
-    internal abstract class KeyExchangeDiffieHellmanGroupShaBase : KeyExchangeDiffieHellman
-    {
-        /// <summary>
-        /// Gets the group prime.
-        /// </summary>
-        /// <value>
-        /// The group prime.
-        /// </value>
-        public abstract BigInteger GroupPrime { get; }
-
-        /// <inheritdoc/>
-        public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage)
-        {
-            base.Start(session, message, sendClientInitMessage);
-
-            Session.RegisterMessage("SSH_MSG_KEXDH_REPLY");
-
-            Session.KeyExchangeDhReplyMessageReceived += Session_KeyExchangeDhReplyMessageReceived;
-
-            _prime = GroupPrime;
-            _group = 2;
-
-            PopulateClientExchangeValue();
-
-            SendMessage(new KeyExchangeDhInitMessage(_clientExchangeValue));
-        }
-
-        /// <summary>
-        /// Finishes key exchange algorithm.
-        /// </summary>
-        public override void Finish()
-        {
-            base.Finish();
-
-            Session.KeyExchangeDhReplyMessageReceived -= Session_KeyExchangeDhReplyMessageReceived;
-        }
-
-        /// <summary>
-        /// Calculates key exchange hash value.
-        /// </summary>
-        /// <returns>
-        /// Key exchange hash.
-        /// </returns>
-        protected override byte[] CalculateHash()
-        {
-            var keyExchangeHashData = new KeyExchangeHashData
-            {
-                ClientVersion = Session.ClientVersion,
-                ServerVersion = Session.ServerVersion,
-                ClientPayload = _clientPayload,
-                ServerPayload = _serverPayload,
-                HostKey = _hostKey,
-                ClientExchangeValue = _clientExchangeValue,
-                ServerExchangeValue = _serverExchangeValue,
-                SharedKey = SharedKey,
-            };
-
-            return Hash(keyExchangeHashData.GetBytes());
-        }
-
-        private void Session_KeyExchangeDhReplyMessageReceived(object sender, MessageEventArgs<KeyExchangeDhReplyMessage> e)
-        {
-            var message = e.Message;
-
-            // Unregister message once received
-            Session.UnRegisterMessage("SSH_MSG_KEXDH_REPLY");
-
-            HandleServerDhReply(message.HostKey, message.F, message.Signature);
-
-            // When SSH_MSG_KEXDH_REPLY received key exchange is completed
-            Finish();
-        }
-    }
-}

+ 2 - 12
src/Renci.SshNet/Security/KeyExchangeEC.cs

@@ -44,12 +44,7 @@ namespace Renci.SshNet.Security
         /// </value>
         protected abstract int HashSize { get; }
 
-        /// <summary>
-        /// Calculates key exchange hash value.
-        /// </summary>
-        /// <returns>
-        /// Key exchange hash.
-        /// </returns>
+        /// <inheritdoc/>
         protected override byte[] CalculateHash()
         {
             var hashData = new KeyExchangeHashData
@@ -67,12 +62,7 @@ namespace Renci.SshNet.Security
             return Hash(hashData.GetBytes());
         }
 
-        /// <summary>
-        /// Validates the exchange hash.
-        /// </summary>
-        /// <returns>
-        /// true if exchange hash is valid; otherwise false.
-        /// </returns>
+        /// <inheritdoc/>
         protected override bool ValidateExchangeHash()
         {
             return ValidateExchangeHash(_hostKey, _signature);

+ 0 - 1
test/Renci.SshNet.IntegrationTests/KeyExchangeAlgorithmTests.cs

@@ -158,7 +158,6 @@ namespace Renci.SshNet.IntegrationTests
         }
 
         [TestMethod]
-        [Ignore]
         public void DiffieHellmanGroup18Sha512()
         {
             _remoteSshdConfig.ClearKeyExchangeAlgorithms()

+ 0 - 61
test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha1Test.cs

@@ -1,61 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-using Renci.SshNet.Common;
-using Renci.SshNet.Security;
-using Renci.SshNet.Tests.Common;
-
-namespace Renci.SshNet.Tests.Classes.Security
-{
-    [TestClass]
-    public class KeyExchangeDiffieHellmanGroup14Sha1Test : TestBase
-    {
-        private static readonly byte[] SecondOkleyGroup =
-            {
-                0x00,
-                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2,
-                0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
-                0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
-                0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
-                0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d,
-                0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
-                0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9,
-                0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
-                0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11,
-                0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d,
-                0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, 0x98, 0xda, 0x48, 0x36,
-                0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f,
-                0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56,
-                0x20, 0x85, 0x52, 0xbb, 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d,
-                0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, 0xf1, 0x74, 0x6c, 0x08,
-                0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b,
-                0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2,
-                0xec, 0x07, 0xa2, 0x8f, 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9,
-                0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7c,
-                0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10,
-                0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xac, 0xaa, 0x68, 0xff, 0xff, 0xff, 0xff,
-                0xff, 0xff, 0xff, 0xff
-            };
-
-        private KeyExchangeDiffieHellmanGroup14Sha1 _group14;
-
-        protected override void OnInit()
-        {
-            base.OnInit();
-
-            _group14 = new KeyExchangeDiffieHellmanGroup14Sha1();
-        }
-
-        [TestMethod]
-        public void GroupPrimeShouldBeSecondOakleyGroup()
-        {
-            var bytes = _group14.GroupPrime.ToByteArray(isBigEndian: true);
-            Assert.IsTrue(SecondOkleyGroup.IsEqualTo(bytes));
-        }
-
-        [TestMethod]
-        public void NameShouldBeDiffieHellmanGroup14Sha1()
-        {
-            Assert.AreEqual("diffie-hellman-group14-sha1", _group14.Name);
-        }
-    }
-}

+ 0 - 61
test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup14Sha256Test.cs

@@ -1,61 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-using Renci.SshNet.Common;
-using Renci.SshNet.Security;
-using Renci.SshNet.Tests.Common;
-
-namespace Renci.SshNet.Tests.Classes.Security
-{
-    [TestClass]
-    public class KeyExchangeDiffieHellmanGroup14Sha256Test : TestBase
-    {
-        private static readonly byte[] SecondOkleyGroup =
-            {
-                0x00,
-                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2,
-                0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
-                0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
-                0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
-                0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d,
-                0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
-                0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9,
-                0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
-                0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11,
-                0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d,
-                0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, 0x98, 0xda, 0x48, 0x36,
-                0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f,
-                0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56,
-                0x20, 0x85, 0x52, 0xbb, 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d,
-                0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, 0xf1, 0x74, 0x6c, 0x08,
-                0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b,
-                0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2,
-                0xec, 0x07, 0xa2, 0x8f, 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9,
-                0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7c,
-                0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10,
-                0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xac, 0xaa, 0x68, 0xff, 0xff, 0xff, 0xff,
-                0xff, 0xff, 0xff, 0xff
-            };
-
-        private KeyExchangeDiffieHellmanGroup14Sha256 _group14;
-
-        protected override void OnInit()
-        {
-            base.OnInit();
-
-            _group14 = new KeyExchangeDiffieHellmanGroup14Sha256();
-        }
-
-        [TestMethod]
-        public void GroupPrimeShouldBeSecondOakleyGroup()
-        {
-            var bytes = _group14.GroupPrime.ToByteArray(isBigEndian: true);
-            Assert.IsTrue(SecondOkleyGroup.IsEqualTo(bytes));
-        }
-
-        [TestMethod]
-        public void NameShouldBeDiffieHellmanGroup14Sha256()
-        {
-            Assert.AreEqual("diffie-hellman-group14-sha256", _group14.Name);
-        }
-    }
-}

+ 0 - 71
test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup16Sha512Test.cs

@@ -1,71 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-using Renci.SshNet.Common;
-using Renci.SshNet.Security;
-using Renci.SshNet.Tests.Common;
-
-namespace Renci.SshNet.Tests.Classes.Security
-{
-    [TestClass]
-    public class KeyExchangeDiffieHellmanGroup16Sha512Test : TestBase
-    {
-        private static readonly byte[] MoreModularExponentialGroup16 =
-            {
-                0x00,
-                0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc9,0x0f,0xda,0xa2,0x21,0x68,0xc2,0x34,
-                0xc4,0xc6,0x62,0x8b,0x80,0xdc,0x1c,0xd1,0x29,0x02,0x4e,0x08,0x8a,0x67,0xcc,0x74,
-                0x02,0x0b,0xbe,0xa6,0x3b,0x13,0x9b,0x22,0x51,0x4a,0x08,0x79,0x8e,0x34,0x04,0xdd,
-                0xef,0x95,0x19,0xb3,0xcd,0x3a,0x43,0x1b,0x30,0x2b,0x0a,0x6d,0xf2,0x5f,0x14,0x37,
-                0x4f,0xe1,0x35,0x6d,0x6d,0x51,0xc2,0x45,0xe4,0x85,0xb5,0x76,0x62,0x5e,0x7e,0xc6,
-                0xf4,0x4c,0x42,0xe9,0xa6,0x37,0xed,0x6b,0x0b,0xff,0x5c,0xb6,0xf4,0x06,0xb7,0xed,
-                0xee,0x38,0x6b,0xfb,0x5a,0x89,0x9f,0xa5,0xae,0x9f,0x24,0x11,0x7c,0x4b,0x1f,0xe6,
-                0x49,0x28,0x66,0x51,0xec,0xe4,0x5b,0x3d,0xc2,0x00,0x7c,0xb8,0xa1,0x63,0xbf,0x05,
-                0x98,0xda,0x48,0x36,0x1c,0x55,0xd3,0x9a,0x69,0x16,0x3f,0xa8,0xfd,0x24,0xcf,0x5f,
-                0x83,0x65,0x5d,0x23,0xdc,0xa3,0xad,0x96,0x1c,0x62,0xf3,0x56,0x20,0x85,0x52,0xbb,
-                0x9e,0xd5,0x29,0x07,0x70,0x96,0x96,0x6d,0x67,0x0c,0x35,0x4e,0x4a,0xbc,0x98,0x04,
-                0xf1,0x74,0x6c,0x08,0xca,0x18,0x21,0x7c,0x32,0x90,0x5e,0x46,0x2e,0x36,0xce,0x3b,
-                0xe3,0x9e,0x77,0x2c,0x18,0x0e,0x86,0x03,0x9b,0x27,0x83,0xa2,0xec,0x07,0xa2,0x8f,
-                0xb5,0xc5,0x5d,0xf0,0x6f,0x4c,0x52,0xc9,0xde,0x2b,0xcb,0xf6,0x95,0x58,0x17,0x18,
-                0x39,0x95,0x49,0x7c,0xea,0x95,0x6a,0xe5,0x15,0xd2,0x26,0x18,0x98,0xfa,0x05,0x10,
-                0x15,0x72,0x8e,0x5a,0x8a,0xaa,0xc4,0x2d,0xad,0x33,0x17,0x0d,0x04,0x50,0x7a,0x33,
-                0xa8,0x55,0x21,0xab,0xdf,0x1c,0xba,0x64,0xec,0xfb,0x85,0x04,0x58,0xdb,0xef,0x0a,
-                0x8a,0xea,0x71,0x57,0x5d,0x06,0x0c,0x7d,0xb3,0x97,0x0f,0x85,0xa6,0xe1,0xe4,0xc7,
-                0xab,0xf5,0xae,0x8c,0xdb,0x09,0x33,0xd7,0x1e,0x8c,0x94,0xe0,0x4a,0x25,0x61,0x9d,
-                0xce,0xe3,0xd2,0x26,0x1a,0xd2,0xee,0x6b,0xf1,0x2f,0xfa,0x06,0xd9,0x8a,0x08,0x64,
-                0xd8,0x76,0x02,0x73,0x3e,0xc8,0x6a,0x64,0x52,0x1f,0x2b,0x18,0x17,0x7b,0x20,0x0c,
-                0xbb,0xe1,0x17,0x57,0x7a,0x61,0x5d,0x6c,0x77,0x09,0x88,0xc0,0xba,0xd9,0x46,0xe2,
-                0x08,0xe2,0x4f,0xa0,0x74,0xe5,0xab,0x31,0x43,0xdb,0x5b,0xfc,0xe0,0xfd,0x10,0x8e,
-                0x4b,0x82,0xd1,0x20,0xa9,0x21,0x08,0x01,0x1a,0x72,0x3c,0x12,0xa7,0x87,0xe6,0xd7,
-                0x88,0x71,0x9a,0x10,0xbd,0xba,0x5b,0x26,0x99,0xc3,0x27,0x18,0x6a,0xf4,0xe2,0x3c,
-                0x1a,0x94,0x68,0x34,0xb6,0x15,0x0b,0xda,0x25,0x83,0xe9,0xca,0x2a,0xd4,0x4c,0xe8,
-                0xdb,0xbb,0xc2,0xdb,0x04,0xde,0x8e,0xf9,0x2e,0x8e,0xfc,0x14,0x1f,0xbe,0xca,0xa6,
-                0x28,0x7c,0x59,0x47,0x4e,0x6b,0xc0,0x5d,0x99,0xb2,0x96,0x4f,0xa0,0x90,0xc3,0xa2,
-                0x23,0x3b,0xa1,0x86,0x51,0x5b,0xe7,0xed,0x1f,0x61,0x29,0x70,0xce,0xe2,0xd7,0xaf,
-                0xb8,0x1b,0xdd,0x76,0x21,0x70,0x48,0x1c,0xd0,0x06,0x91,0x27,0xd5,0xb0,0x5a,0xa9,
-                0x93,0xb4,0xea,0x98,0x8d,0x8f,0xdd,0xc1,0x86,0xff,0xb7,0xdc,0x90,0xa6,0xc0,0x8f,
-                0x4d,0xf4,0x35,0xc9,0x34,0x06,0x31,0x99,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
-            };
-
-        private KeyExchangeDiffieHellmanGroup16Sha512 _group16;
-
-        protected override void OnInit()
-        {
-            base.OnInit();
-
-            _group16 = new KeyExchangeDiffieHellmanGroup16Sha512();
-        }
-
-        [TestMethod]
-        public void GroupPrimeShouldBeMoreModularExponentialGroup16()
-        {
-            var bytes = _group16.GroupPrime.ToByteArray(isBigEndian: true);
-            Assert.IsTrue(MoreModularExponentialGroup16.IsEqualTo(bytes));
-        }
-
-        [TestMethod]
-        public void NameShouldBeDiffieHellmanGroup16Sha512()
-        {
-            Assert.AreEqual("diffie-hellman-group16-sha512", _group16.Name);
-        }
-    }
-}

+ 0 - 54
test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroup1Sha1Test.cs

@@ -1,54 +0,0 @@
-using System.Linq;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-using Renci.SshNet.Common;
-using Renci.SshNet.Security;
-using Renci.SshNet.Tests.Common;
-
-namespace Renci.SshNet.Tests.Classes.Security
-{
-    [TestClass]
-    public class KeyExchangeDiffieHellmanGroup1Sha1Test : TestBase
-    {
-        private static readonly byte[] SecondOkleyGroup =
-            {
-                0x00,
-                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2,
-                0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
-                0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
-                0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
-                0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d,
-                0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
-                0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9,
-                0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
-                0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11,
-                0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81,
-                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-            };
-
-        private KeyExchangeDiffieHellmanGroup1Sha1 _group1;
-
-        protected override void OnInit()
-        {
-            base.OnInit();
-
-            _group1 = new KeyExchangeDiffieHellmanGroup1Sha1();
-        }
-
-        [TestMethod]
-        public void GroupPrimeShouldBeSecondOakleyGroup()
-        {
-            var bytes = _group1.GroupPrime.ToByteArray(isBigEndian: true);
-            Assert.IsTrue(SecondOkleyGroup.IsEqualTo(bytes));
-
-            SecondOkleyGroup.Reverse().DebugPrint();
-        }
-
-        [TestMethod]
-        public void NameShouldBeDiffieHellmanGroup1Sha1()
-        {
-            Assert.AreEqual("diffie-hellman-group1-sha1", _group1.Name);
-        }
-    }
-}

+ 54 - 0
test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanGroupExchangeTest.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Security.Cryptography;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Renci.SshNet.Security;
+
+namespace Renci.SshNet.Tests.Classes.Security
+{
+    [TestClass]
+    public class KeyExchangeDiffieHellmanGroupExchangeTest
+    {
+        [TestMethod]
+        public void NameShouldBeCtorValue()
+        {
+            KeyExchangeDiffieHellmanGroupExchange kex = new("diffie-hellman-group-exchange-sha256", HashAlgorithmName.SHA512);
+
+            Assert.AreEqual("diffie-hellman-group-exchange-sha256", kex.Name);
+        }
+
+        [TestMethod]
+        public void Ctor_ArgumentNullException()
+        {
+            var ex = Assert.Throws<ArgumentNullException>(() => new KeyExchangeDiffieHellmanGroupExchange(name: null, HashAlgorithmName.SHA512));
+            Assert.AreEqual("name", ex.ParamName);
+
+            ex = Assert.Throws<ArgumentNullException>(() => new KeyExchangeDiffieHellmanGroupExchange("kex", default));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+
+            ex = Assert.Throws<ArgumentNullException>(() => new KeyExchangeDiffieHellmanGroupExchange("kex", new HashAlgorithmName(null)));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+        }
+
+        [TestMethod]
+        public void Ctor_InvalidHashAlgorithm_ThrowsArgumentException()
+        {
+            var ex = Assert.ThrowsExactly<ArgumentException>(() => new KeyExchangeDiffieHellmanGroupExchange("kex", new HashAlgorithmName("bad")));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+
+            ex = Assert.ThrowsExactly<ArgumentException>(() => new KeyExchangeDiffieHellmanGroupExchange("kex", new HashAlgorithmName("")));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+        }
+
+        [TestMethod]
+        public void Ctor_InvalidGroupSizes_ThrowsArgumentOutOfRangeException()
+        {
+            var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new KeyExchangeDiffieHellmanGroupExchange("kex", HashAlgorithmName.SHA512, 1024, 4096, 2048));
+            Assert.AreEqual("preferredGroupSize", ex.ParamName);
+
+            ex = Assert.Throws<ArgumentOutOfRangeException>(() => new KeyExchangeDiffieHellmanGroupExchange("kex", HashAlgorithmName.SHA512, 8192, 4096, 2048));
+            Assert.AreEqual("preferredGroupSize", ex.ParamName);
+        }
+    }
+}

+ 49 - 0
test/Renci.SshNet.Tests/Classes/Security/KeyExchangeDiffieHellmanTest.cs

@@ -0,0 +1,49 @@
+using System;
+using System.Security.Cryptography;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Org.BouncyCastle.Crypto.Agreement;
+
+using Renci.SshNet.Security;
+
+namespace Renci.SshNet.Tests.Classes.Security
+{
+    [TestClass]
+    public class KeyExchangeDiffieHellmanTest
+    {
+        [TestMethod]
+        public void NameShouldBeCtorValue()
+        {
+            KeyExchangeDiffieHellman kex = new("diffie-hellman-group16-sha512", DHStandardGroups.rfc3526_4096, HashAlgorithmName.SHA512);
+
+            Assert.AreEqual("diffie-hellman-group16-sha512", kex.Name);
+        }
+
+        [TestMethod]
+        public void Ctor_ArgumentNullException()
+        {
+            var ex = Assert.Throws<ArgumentNullException>(() => new KeyExchangeDiffieHellman(name: null, DHStandardGroups.rfc3526_4096, HashAlgorithmName.SHA512));
+            Assert.AreEqual("name", ex.ParamName);
+
+            ex = Assert.Throws<ArgumentNullException>(() => new KeyExchangeDiffieHellman("kex", parameters: null, HashAlgorithmName.SHA512));
+            Assert.AreEqual("parameters", ex.ParamName);
+
+            ex = Assert.Throws<ArgumentNullException>(() => new KeyExchangeDiffieHellman("kex", DHStandardGroups.rfc3526_4096, default));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+
+            ex = Assert.Throws<ArgumentNullException>(() => new KeyExchangeDiffieHellman("kex", DHStandardGroups.rfc3526_4096, new HashAlgorithmName(null)));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+        }
+
+        [TestMethod]
+        public void Ctor_InvalidHashAlgorithm_ThrowsArgumentException()
+        {
+            var ex = Assert.ThrowsExactly<ArgumentException>(() => new KeyExchangeDiffieHellman("kex", DHStandardGroups.rfc3526_4096, new HashAlgorithmName("bad")));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+
+            ex = Assert.ThrowsExactly<ArgumentException>(() => new KeyExchangeDiffieHellman("kex", DHStandardGroups.rfc3526_4096, new HashAlgorithmName("")));
+            Assert.AreEqual("hashAlgorithm", ex.ParamName);
+        }
+    }
+}