浏览代码

KEX with Elliptic Curves ecdh-sha2-nistp{256,384,521}

Add Kex Algos ecdh-sha2-nistp{256,384,521}

We have to use a minimalistic BouncyCastle Import for ECDH, since Microsoft's
System.Security.Cryptography is not usable in this case.

ECDiffieHellmanCng.DeriveKeyMaterial() already does the hashing and it's not
possible to get the unhased key material for further processing.

https://blogs.msdn.microsoft.com/shawnfa/2007/01/22/elliptic-curve-diffie-hellman/
Stefan Rinkes 7 年之前
父节点
当前提交
465451985a

+ 4 - 1
src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj

@@ -351,6 +351,9 @@
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs">
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs">
       <Link>Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs</Link>
       <Link>Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\Renci.SshNet.Tests\Classes\Common\ExtensionsTest_ToBigInteger2.cs">
+      <Link>Classes\Common\ExtensionsTest_ToBigInteger2.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\ExtensionsTest_TrimLeadingZeros.cs">
     <Compile Include="..\Renci.SshNet.Tests\Classes\Common\ExtensionsTest_TrimLeadingZeros.cs">
       <Link>Classes\Common\ExtensionsTest_TrimLeadingZeros.cs</Link>
       <Link>Classes\Common\ExtensionsTest_TrimLeadingZeros.cs</Link>
     </Compile>
     </Compile>
@@ -1750,4 +1753,4 @@
   <Target Name="AfterBuild">
   <Target Name="AfterBuild">
   </Target>
   </Target>
   -->
   -->
-</Project>
+</Project>

+ 40 - 0
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes.Common
+{
+    [TestClass]
+    [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
+    public class ExtensionsTest_ToBigInteger2
+    {
+        [TestMethod]
+        public void ShouldNotAppendZero()
+        {
+            byte[] value = { 0x0a, 0x0d };
+
+            var actual = value.ToBigInteger2().ToByteArray().Reverse();
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(2, actual.Length);
+            Assert.AreEqual(0x0a, actual[0]);
+            Assert.AreEqual(0x0d, actual[1]);
+        }
+
+        [TestMethod]
+        public void ShouldAppendZero()
+        {
+            byte[] value = { 0xff, 0x0a, 0x0d };
+
+            var actual = value.ToBigInteger2().ToByteArray().Reverse();
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(4, actual.Length);
+            Assert.AreEqual(0x00, actual[0]);
+            Assert.AreEqual(0xff, actual[1]);
+            Assert.AreEqual(0x0a, actual[2]);
+            Assert.AreEqual(0x0d, actual[3]);
+        }
+    }
+}

+ 2 - 1
src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -169,6 +169,7 @@
     <Compile Include="Classes\Common\ExtensionsTest_Reverse.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_Reverse.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_Take_Count.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_Take_Count.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs" />
+    <Compile Include="Classes\Common\ExtensionsTest_ToBigInteger2.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_TrimLeadingZeros.cs" />
     <Compile Include="Classes\Common\ExtensionsTest_TrimLeadingZeros.cs" />
     <Compile Include="Classes\Common\PackTest.cs" />
     <Compile Include="Classes\Common\PackTest.cs" />
     <Compile Include="Classes\Common\PosixPathTest_GetFileName.cs" />
     <Compile Include="Classes\Common\PosixPathTest_GetFileName.cs" />
@@ -717,4 +718,4 @@
   <Target Name="AfterBuild">
   <Target Name="AfterBuild">
   </Target>
   </Target>
   -->
   -->
-</Project>
+</Project>

+ 14 - 0
src/Renci.SshNet/Common/Extensions.cs

@@ -72,6 +72,20 @@ namespace Renci.SshNet.Common
             return new BigInteger(reversed.Reverse());
             return new BigInteger(reversed.Reverse());
         }
         }
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BigInteger"/> structure using the SSH BigNum2 Format
+        /// </summary>
+        public static BigInteger ToBigInteger2(this byte[] data)
+        {
+            if ((data[0] & (1 << 7)) != 0)
+            {
+                var buf = new byte[data.Length + 1];
+                Buffer.BlockCopy(data, 0, buf, 1, data.Length);
+                data = buf;
+            }
+            return data.ToBigInteger();
+        }
+
         /// <summary>
         /// <summary>
         /// Reverses the sequence of the elements in the entire one-dimensional <see cref="Array"/>.
         /// Reverses the sequence of the elements in the entire one-dimensional <see cref="Array"/>.
         /// </summary>
         /// </summary>

+ 3 - 4
src/Renci.SshNet/ConnectionInfo.cs

@@ -322,14 +322,13 @@ namespace Renci.SshNet
 
 
             KeyExchangeAlgorithms = new Dictionary<string, Type>
             KeyExchangeAlgorithms = new Dictionary<string, Type>
                 {
                 {
+                    {"ecdh-sha2-nistp256", typeof(KeyExchangeECDH256)},
+                    {"ecdh-sha2-nistp384", typeof(KeyExchangeECDH384)},
+                    {"ecdh-sha2-nistp521", typeof(KeyExchangeECDH521)},
                     {"diffie-hellman-group-exchange-sha256", typeof (KeyExchangeDiffieHellmanGroupExchangeSha256)},
                     {"diffie-hellman-group-exchange-sha256", typeof (KeyExchangeDiffieHellmanGroupExchangeSha256)},
                     {"diffie-hellman-group-exchange-sha1", typeof (KeyExchangeDiffieHellmanGroupExchangeSha1)},
                     {"diffie-hellman-group-exchange-sha1", typeof (KeyExchangeDiffieHellmanGroupExchangeSha1)},
                     {"diffie-hellman-group14-sha1", typeof (KeyExchangeDiffieHellmanGroup14Sha1)},
                     {"diffie-hellman-group14-sha1", typeof (KeyExchangeDiffieHellmanGroup14Sha1)},
                     {"diffie-hellman-group1-sha1", typeof (KeyExchangeDiffieHellmanGroup1Sha1)},
                     {"diffie-hellman-group1-sha1", typeof (KeyExchangeDiffieHellmanGroup1Sha1)},
-                    //{"ecdh-sha2-nistp256", typeof(KeyExchangeEllipticCurveDiffieHellman)},
-                    //{"ecdh-sha2-nistp256", typeof(...)},
-                    //{"ecdh-sha2-nistp384", typeof(...)},
-                    //{"ecdh-sha2-nistp521", typeof(...)},
                     //"gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD
                     //"gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD
                     //"gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD
                     //"gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD
                 };
                 };

+ 16 - 7
src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs

@@ -1,6 +1,4 @@
-#if false
-
-using System;
+using System;
 using Renci.SshNet.Common;
 using Renci.SshNet.Common;
 
 
 namespace Renci.SshNet.Messages.Transport
 namespace Renci.SshNet.Messages.Transport
@@ -8,7 +6,7 @@ namespace Renci.SshNet.Messages.Transport
     /// <summary>
     /// <summary>
     /// Represents SSH_MSG_KEXECDH_INIT message.
     /// Represents SSH_MSG_KEXECDH_INIT message.
     /// </summary>
     /// </summary>
-    [Message("SSH_MSG_KEXECDH_INIT", 30)]
+    [Message("SSH_MSG_KEX_ECDH_INIT", 30)]
     internal class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed
     internal class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed
     {
     {
         /// <summary>
         /// <summary>
@@ -33,6 +31,14 @@ namespace Renci.SshNet.Messages.Transport
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="KeyExchangeEcdhInitMessage"/> class.
+        /// </summary>
+        public KeyExchangeEcdhInitMessage(byte[] q)
+        {
+            QC = q;
+        }
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="KeyExchangeEcdhInitMessage"/> class.
         /// Initializes a new instance of the <see cref="KeyExchangeEcdhInitMessage"/> class.
         /// </summary>
         /// </summary>
@@ -63,7 +69,10 @@ namespace Renci.SshNet.Messages.Transport
         {
         {
             WriteBinaryString(QC);
             WriteBinaryString(QC);
         }
         }
-    }
-}
 
 
-#endif // false
+        internal override void Process(Session session)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 8 - 7
src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs

@@ -1,11 +1,9 @@
-#if false
-
-namespace Renci.SshNet.Messages.Transport
+namespace Renci.SshNet.Messages.Transport
 {
 {
     /// <summary>
     /// <summary>
     /// Represents SSH_MSG_KEXECDH_REPLY message.
     /// Represents SSH_MSG_KEXECDH_REPLY message.
     /// </summary>
     /// </summary>
-    [Message("SSH_MSG_KEXECDH_REPLY", 31)]
+    [Message("SSH_MSG_KEX_ECDH_REPLY", 31)]
     public class KeyExchangeEcdhReplyMessage : Message
     public class KeyExchangeEcdhReplyMessage : Message
     {
     {
         /// <summary>
         /// <summary>
@@ -65,7 +63,10 @@ namespace Renci.SshNet.Messages.Transport
             WriteBinaryString(QS);
             WriteBinaryString(QS);
             WriteBinaryString(Signature);
             WriteBinaryString(Signature);
         }
         }
-    }
-}
 
 
-#endif // false
+        internal override void Process(Session session)
+        {
+            session.OnKeyExchangeEcdhReplyMessageReceived(this);
+        }
+    }
+}

+ 5 - 0
src/Renci.SshNet/Renci.SshNet.csproj

@@ -309,6 +309,11 @@
     <Compile Include="Security\GroupExchangeHashData.cs" />
     <Compile Include="Security\GroupExchangeHashData.cs" />
     <Compile Include="Security\IKeyExchange.cs" />
     <Compile Include="Security\IKeyExchange.cs" />
     <Compile Include="Security\KeyExchangeDiffieHellmanGroupExchangeShaBase.cs" />
     <Compile Include="Security\KeyExchangeDiffieHellmanGroupExchangeShaBase.cs" />
+    <Compile Include="Security\KeyExchangeEC.cs" />
+    <Compile Include="Security\KeyExchangeECDH.cs" />
+    <Compile Include="Security\KeyExchangeECDH521.cs" />
+    <Compile Include="Security\KeyExchangeECDH384.cs" />
+    <Compile Include="Security\KeyExchangeECDH256.cs" />
     <Compile Include="ServiceFactory.cs" />
     <Compile Include="ServiceFactory.cs" />
     <Compile Include="ServiceFactory.NET.cs" />
     <Compile Include="ServiceFactory.NET.cs" />
     <Compile Include="Sftp\ISftpFileReader.cs" />
     <Compile Include="Sftp\ISftpFileReader.cs" />

+ 215 - 0
src/Renci.SshNet/Security/KeyExchangeEC.cs

@@ -0,0 +1,215 @@
+using System;
+using System.Text;
+using Renci.SshNet.Messages.Transport;
+using Renci.SshNet.Common;
+using Renci.SshNet.Abstractions;
+
+namespace Renci.SshNet.Security
+{
+    internal abstract class KeyExchangeEC : KeyExchange
+    {
+        /// <summary>
+        /// Specifies client payload
+        /// </summary>
+        protected byte[] _clientPayload;
+
+        /// <summary>
+        /// Specifies server payload
+        /// </summary>
+        protected byte[] _serverPayload;
+
+        /// <summary>
+        /// Specifies client exchange.
+        /// </summary>
+        protected byte[] _clientExchangeValue;
+
+        /// <summary>
+        /// Specifies server exchange.
+        /// </summary>
+        protected byte[] _serverExchangeValue;
+
+        /// <summary>
+        /// Specifies host key data.
+        /// </summary>
+        protected byte[] _hostKey;
+
+        /// <summary>
+        /// Specifies signature data.
+        /// </summary>
+        protected byte[] _signature;
+
+        /// <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; }
+
+        /// <summary>
+        /// Hashes the specified data bytes.
+        /// </summary>
+        /// <param name="hashData">The hash data.</param>
+        /// <returns>
+        /// Hashed bytes
+        /// </returns>
+        protected override byte[] Hash(byte[] hashData)
+        {
+            using (var sha256 = CryptoAbstraction.CreateSHA256())
+            {
+                return sha256.ComputeHash(hashData, 0, hashData.Length);
+            }
+        }
+
+        /// <summary>
+        /// Calculates key exchange hash value.
+        /// </summary>
+        /// <returns>
+        /// Key exchange hash.
+        /// </returns>
+        protected override byte[] CalculateHash()
+        {
+            var hashData = new _ExchangeHashData
+            {
+                ClientVersion = Session.ClientVersion,
+                ServerVersion = Session.ServerVersion,
+                ClientPayload = _clientPayload,
+                ServerPayload = _serverPayload,
+                HostKey = _hostKey,
+                ClientExchangeValue = _clientExchangeValue,
+                ServerExchangeValue = _serverExchangeValue,
+                SharedKey = SharedKey,
+            }.GetBytes();
+
+            return Hash(hashData);
+        }
+
+        /// <summary>
+        /// Validates the exchange hash.
+        /// </summary>
+        /// <returns>
+        /// true if exchange hash is valid; otherwise false.
+        /// </returns>
+        protected override bool ValidateExchangeHash()
+        {
+            var exchangeHash = CalculateHash();
+
+            var length = Pack.BigEndianToUInt32(_hostKey);
+            var algorithmName = Encoding.UTF8.GetString(_hostKey, 4, (int)length);
+            var key = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](_hostKey);
+
+            Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName;
+
+            if (CanTrustHostKey(key))
+            {
+                return key.VerifySignature(exchangeHash, _signature);
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Starts key exchange algorithm
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="message">Key exchange init message.</param>
+        public override void Start(Session session, KeyExchangeInitMessage message)
+        {
+            base.Start(session, message);
+
+            _serverPayload = message.GetBytes();
+            _clientPayload = Session.ClientInitMessage.GetBytes();
+        }
+
+        /// <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 HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature)
+        {
+        }
+
+        internal class _ExchangeHashData : SshData
+        {
+            private byte[] _serverVersion;
+            private byte[] _clientVersion;
+            private byte[] _sharedKey;
+
+            public string ServerVersion
+            {
+                private get { return Utf8.GetString(_serverVersion, 0, _serverVersion.Length); }
+                set { _serverVersion = Utf8.GetBytes(value); }
+            }
+
+            public string ClientVersion
+            {
+                private get { return Utf8.GetString(_clientVersion, 0, _clientVersion.Length); }
+                set { _clientVersion = Utf8.GetBytes(value); }
+            }
+
+            public byte[] ClientPayload { get; set; }
+
+            public byte[] ServerPayload { get; set; }
+
+            public byte[] HostKey { get; set; }
+
+            public byte[] ClientExchangeValue { get; set; }
+
+            public byte[] ServerExchangeValue { get; set; }
+
+            public BigInteger SharedKey
+            {
+                private get { return _sharedKey.ToBigInteger(); }
+                set { _sharedKey = value.ToByteArray().Reverse(); }
+            }
+            /// <summary>
+            /// Gets the size of the message in bytes.
+            /// </summary>
+            /// <value>
+            /// The size of the messages in bytes.
+            /// </value>
+            protected override int BufferCapacity
+            {
+                get
+                {
+                    var capacity = base.BufferCapacity;
+                    capacity += 4; // ClientVersion length
+                    capacity += _clientVersion.Length; // ClientVersion
+                    capacity += 4; // ServerVersion length
+                    capacity += _serverVersion.Length; // ServerVersion
+                    capacity += 4; // ClientPayload length
+                    capacity += ClientPayload.Length; // ClientPayload
+                    capacity += 4; // ServerPayload length
+                    capacity += ServerPayload.Length; // ServerPayload
+                    capacity += 4; // HostKey length
+                    capacity += HostKey.Length; // HostKey
+                    capacity += 4; // ClientExchangeValue length
+                    capacity += ClientExchangeValue.Length; // ClientExchangeValue
+                    capacity += 4; // ServerExchangeValue length
+                    capacity += ServerExchangeValue.Length; // ServerExchangeValue
+                    capacity += 4; // SharedKey length
+                    capacity += _sharedKey.Length; // SharedKey
+                    return capacity;
+                }
+            }
+
+            protected override void LoadData()
+            {
+                throw new NotImplementedException();
+            }
+
+            protected override void SaveData()
+            {
+                WriteBinaryString(_clientVersion);
+                WriteBinaryString(_serverVersion);
+                WriteBinaryString(ClientPayload);
+                WriteBinaryString(ServerPayload);
+                WriteBinaryString(HostKey);
+                WriteBinaryString(ClientExchangeValue);
+                WriteBinaryString(ServerExchangeValue);
+                WriteBinaryString(_sharedKey);
+           }
+        }
+    }
+}

+ 107 - 0
src/Renci.SshNet/Security/KeyExchangeECDH.cs

@@ -0,0 +1,107 @@
+using System;
+using Renci.SshNet.Common;
+using Renci.SshNet.Messages.Transport;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators;
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters;
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Agreement;
+
+namespace Renci.SshNet.Security
+{
+    internal abstract class KeyExchangeECDH : KeyExchangeEC
+    {
+        /// <summary>
+        /// Gets the parameter of the curve.
+        /// </summary>
+        /// <value>
+        /// The parameter of the curve.
+        /// </value>
+        protected abstract X9ECParameters CurveParameter { get; }
+
+        protected ECDHCBasicAgreement KeyAgreement;
+        protected ECDomainParameters DomainParameters;
+
+        /// <summary>
+        /// Starts key exchange algorithm
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="message">Key exchange init message.</param>
+        public override void Start(Session session, KeyExchangeInitMessage message)
+        {
+            base.Start(session, message);
+
+            Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY");
+
+            Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived;
+
+            DomainParameters = new ECDomainParameters(CurveParameter.Curve,
+                                                      CurveParameter.G,
+                                                      CurveParameter.N,
+                                                      CurveParameter.H,
+                                                      CurveParameter.GetSeed());
+
+            var g = new ECKeyPairGenerator();
+            g.Init(new ECKeyGenerationParameters(DomainParameters, new SecureRandom()));
+
+            var aKeyPair = g.GenerateKeyPair();
+            KeyAgreement = new ECDHCBasicAgreement();
+            KeyAgreement.Init(aKeyPair.Private);
+            _clientExchangeValue = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded();
+
+            SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue));
+        }
+
+        /// <summary>
+        /// Finishes key exchange algorithm.
+        /// </summary>
+        public override void Finish()
+        {
+            base.Finish();
+
+            Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived;
+        }
+
+        private void Session_KeyExchangeEcdhReplyMessageReceived(object sender, MessageEventArgs<KeyExchangeEcdhReplyMessage> e)
+        {
+            var message = e.Message;
+
+            //  Unregister message once received
+            Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY");
+
+            HandleServerEcdhReply(message.KS, message.QS, message.Signature);
+
+            //  When SSH_MSG_KEXDH_REPLY received key exchange is completed
+            Finish();
+        }
+
+        /// <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 override void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature)
+        {
+            _serverExchangeValue = serverExchangeValue;
+            _hostKey = hostKey;
+            _serverExchangeValue = serverExchangeValue;
+            _signature = signature;
+
+            var cordSize = (serverExchangeValue.Length - 1) / 2;
+            var x = new byte[cordSize];
+            Buffer.BlockCopy(serverExchangeValue, 1, x, 0, x.Length); // first byte is format. should be checked and passed to bouncy castle?
+            var y = new byte[cordSize];
+            Buffer.BlockCopy(serverExchangeValue, cordSize + 1, y, 0, y.Length);
+
+            var c = (FpCurve)DomainParameters.Curve;
+            var q = c.CreatePoint(new Org.BouncyCastle.Math.BigInteger(1, x), new Org.BouncyCastle.Math.BigInteger(1, y));
+            var publicKey = new ECPublicKeyParameters("ECDH", q, DomainParameters);
+
+            var k1 = KeyAgreement.CalculateAgreement(publicKey);
+            SharedKey = k1.ToByteArray().ToBigInteger2();
+        }
+    }
+}

+ 38 - 0
src/Renci.SshNet/Security/KeyExchangeECDH256.cs

@@ -0,0 +1,38 @@
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec;
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
+
+namespace Renci.SshNet.Security
+{
+    internal class KeyExchangeECDH256 : KeyExchangeECDH
+    {
+        /// <summary>
+        /// Gets algorithm name.
+        /// </summary>
+        public override string Name
+        {
+            get { return "ecdh-sha2-nistp256"; }
+        }
+
+        /// <summary>
+        /// Gets Curve Parameter.
+        /// </summary>
+        protected override X9ECParameters CurveParameter
+        {
+            get
+            {
+                return SecNamedCurves.GetByName("P-256");
+            }
+        }
+
+        /// <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; }
+        }
+    }
+}

+ 54 - 0
src/Renci.SshNet/Security/KeyExchangeECDH384.cs

@@ -0,0 +1,54 @@
+using Renci.SshNet.Abstractions;
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec;
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
+
+namespace Renci.SshNet.Security
+{
+    internal class KeyExchangeECDH384 : KeyExchangeECDH
+    {
+        /// <summary>
+        /// Gets algorithm name.
+        /// </summary>
+        public override string Name
+        {
+            get { return "ecdh-sha2-nistp384"; }
+        }
+
+        /// <summary>
+        /// Gets Curve Parameter.
+        /// </summary>
+        protected override X9ECParameters CurveParameter
+        {
+            get
+            {
+                return SecNamedCurves.GetByName("P-384");
+            }
+        }
+
+        /// <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 384; }
+        }
+
+        /// <summary>
+        /// Hashes the specified data bytes.
+        /// </summary>
+        /// <param name="hashData">The hash data.</param>
+        /// <returns>
+        /// Hashed bytes
+        /// </returns>
+        protected override byte[] Hash(byte[] hashData)
+        {
+            using (var sha384 = CryptoAbstraction.CreateSHA384())
+            {
+                return sha384.ComputeHash(hashData, 0, hashData.Length);
+            }
+        }
+    }
+}

+ 54 - 0
src/Renci.SshNet/Security/KeyExchangeECDH521.cs

@@ -0,0 +1,54 @@
+using Renci.SshNet.Abstractions;
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec;
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
+
+namespace Renci.SshNet.Security
+{
+    internal class KeyExchangeECDH521 : KeyExchangeECDH
+    {
+        /// <summary>
+        /// Gets algorithm name.
+        /// </summary>
+        public override string Name
+        {
+            get { return "ecdh-sha2-nistp521"; }
+        }
+
+        /// <summary>
+        /// Gets Curve Parameter.
+        /// </summary>
+        protected override X9ECParameters CurveParameter
+        {
+            get
+            {
+                return SecNamedCurves.GetByName("P-521");
+            }
+        }
+
+        /// <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>
+        /// Hashed bytes
+        /// </returns>
+        protected override byte[] Hash(byte[] hashData)
+        {
+            using (var sha512 = CryptoAbstraction.CreateSHA512())
+            {
+                return sha512.ComputeHash(hashData, 0, hashData.Length);
+            }
+        }
+    }
+}

+ 12 - 0
src/Renci.SshNet/Session.cs

@@ -439,6 +439,11 @@ namespace Renci.SshNet
         /// </summary>
         /// </summary>
         internal event EventHandler<MessageEventArgs<KeyExchangeDhReplyMessage>> KeyExchangeDhReplyMessageReceived;
         internal event EventHandler<MessageEventArgs<KeyExchangeDhReplyMessage>> KeyExchangeDhReplyMessageReceived;
 
 
+        /// <summary>
+        /// Occurs when a <see cref="KeyExchangeEcdhReplyMessage"/> message is received from the SSH server.
+        /// </summary>
+        internal event EventHandler<MessageEventArgs<KeyExchangeEcdhReplyMessage>> KeyExchangeEcdhReplyMessageReceived;
+
         /// <summary>
         /// <summary>
         /// Occurs when <see cref="NewKeysMessage"/> message received
         /// Occurs when <see cref="NewKeysMessage"/> message received
         /// </summary>
         /// </summary>
@@ -1322,6 +1327,13 @@ namespace Renci.SshNet
                 handlers(this, new MessageEventArgs<KeyExchangeDhReplyMessage>(message));
                 handlers(this, new MessageEventArgs<KeyExchangeDhReplyMessage>(message));
         }
         }
 
 
+        internal void OnKeyExchangeEcdhReplyMessageReceived(KeyExchangeEcdhReplyMessage message)
+        {
+            var handlers = KeyExchangeEcdhReplyMessageReceived;
+            if (handlers != null)
+                handlers(this, new MessageEventArgs<KeyExchangeEcdhReplyMessage>(message));
+        }
+
         /// <summary>
         /// <summary>
         /// Called when <see cref="NewKeysMessage"/> message received.
         /// Called when <see cref="NewKeysMessage"/> message received.
         /// </summary>
         /// </summary>

+ 3 - 2
src/Renci.SshNet/SshMessageFactory.cs

@@ -25,7 +25,7 @@ namespace Renci.SshNet
         /// <summary>
         /// <summary>
         /// Defines the total number of supported messages.
         /// Defines the total number of supported messages.
         /// </summary>
         /// </summary>
-        internal const int TotalMessageCount = 31;
+        internal const int TotalMessageCount = 32;
 
 
         static SshMessageFactory()
         static SshMessageFactory()
         {
         {
@@ -61,7 +61,8 @@ namespace Renci.SshNet
                 new MessageMetadata<ServiceAcceptMessage> (27, "SSH_MSG_SERVICE_ACCEPT", 6),
                 new MessageMetadata<ServiceAcceptMessage> (27, "SSH_MSG_SERVICE_ACCEPT", 6),
                 new MessageMetadata<KeyExchangeDhGroupExchangeGroup> (28, "SSH_MSG_KEX_DH_GEX_GROUP", 31),
                 new MessageMetadata<KeyExchangeDhGroupExchangeGroup> (28, "SSH_MSG_KEX_DH_GEX_GROUP", 31),
                 new MessageMetadata<KeyExchangeDhReplyMessage> (29, "SSH_MSG_KEXDH_REPLY", 31),
                 new MessageMetadata<KeyExchangeDhReplyMessage> (29, "SSH_MSG_KEXDH_REPLY", 31),
-                new MessageMetadata<KeyExchangeDhGroupExchangeReply> (30, "SSH_MSG_KEX_DH_GEX_REPLY", 33)
+                new MessageMetadata<KeyExchangeDhGroupExchangeReply> (30, "SSH_MSG_KEX_DH_GEX_REPLY", 33),
+                new MessageMetadata<KeyExchangeEcdhReplyMessage> (31, "SSH_MSG_KEX_ECDH_REPLY", 31)
             };
             };
 
 
             MessagesByName = new Dictionary<string, MessageMetadata>(AllMessages.Length);
             MessagesByName = new Dictionary<string, MessageMetadata>(AllMessages.Length);