From 901fd5a840f24d8cbedc244cb0e824300738dc1d Mon Sep 17 00:00:00 2001 From: Scott Xu Date: Sat, 6 Apr 2024 20:27:49 +0800 Subject: [PATCH] Use BCL ECDiffieHellman for KeyExchange (.NET 8.0 onward only) --- src/Renci.SshNet/Security/KeyExchangeECDH.cs | 79 ++++++++++++++++--- .../Security/KeyExchangeECDH256.cs | 11 +-- .../Security/KeyExchangeECDH384.cs | 11 +-- .../Security/KeyExchangeECDH521.cs | 11 +-- 4 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH.cs b/src/Renci.SshNet/Security/KeyExchangeECDH.cs index c756fb6cb..a2b6442da 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH.cs @@ -2,7 +2,7 @@ using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec; using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Agreement; using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators; using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters; @@ -13,13 +13,17 @@ namespace Renci.SshNet.Security { internal abstract class KeyExchangeECDH : KeyExchangeEC { +#if NET8_0_OR_GREATER + private System.Security.Cryptography.ECDiffieHellman _clientECDH; +#endif + /// - /// Gets the parameter of the curve. + /// Gets the name of the curve. /// /// - /// The parameter of the curve. + /// The name of the curve. /// - protected abstract X9ECParameters CurveParameter { get; } + protected abstract string CurveName { get; } private ECDHCBasicAgreement _keyAgreement; private ECDomainParameters _domainParameters; @@ -33,11 +37,30 @@ public override void Start(Session session, KeyExchangeInitMessage message, bool Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; - _domainParameters = new ECDomainParameters(CurveParameter.Curve, - CurveParameter.G, - CurveParameter.N, - CurveParameter.H, - CurveParameter.GetSeed()); +#if NET8_0_OR_GREATER + if (IsNonWindowsOrWindowsVersionAtLeast(10)) + { + _clientECDH = System.Security.Cryptography.ECDiffieHellman.Create(); + _clientECDH.GenerateKey(System.Security.Cryptography.ECCurve.CreateFromFriendlyName(CurveName)); + + var q = _clientECDH.PublicKey.ExportParameters().Q; + + _clientExchangeValue = new byte[1 + q.X.Length + q.Y.Length]; + _clientExchangeValue[0] = 0x04; + Buffer.BlockCopy(q.X, 0, _clientExchangeValue, 1, q.X.Length); + Buffer.BlockCopy(q.Y, 0, _clientExchangeValue, q.X.Length + 1, q.Y.Length); + + SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); + + return; + } +#endif + var curveParameter = SecNamedCurves.GetByName(CurveName); + _domainParameters = new ECDomainParameters(curveParameter.Curve, + curveParameter.G, + curveParameter.N, + curveParameter.H, + curveParameter.GetSeed()); var g = new ECKeyPairGenerator(); g.Init(new ECKeyGenerationParameters(_domainParameters, new SecureRandom())); @@ -46,7 +69,6 @@ public override void Start(Session session, KeyExchangeInitMessage message, bool _keyAgreement = new ECDHCBasicAgreement(); _keyAgreement.Init(aKeyPair.Private); _clientExchangeValue = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded(); - SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); } @@ -91,6 +113,25 @@ private void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, b var y = new byte[cordSize]; Buffer.BlockCopy(serverExchangeValue, cordSize + 1, y, 0, y.Length); +#if NET8_0_OR_GREATER + if (IsNonWindowsOrWindowsVersionAtLeast(10)) + { + using var serverECDH = System.Security.Cryptography.ECDiffieHellman.Create(new System.Security.Cryptography.ECParameters + { + Curve = System.Security.Cryptography.ECCurve.CreateFromFriendlyName(CurveName), + Q = + { + X = x, + Y = y, + }, + }); + + var k = _clientECDH.DeriveRawSecretAgreement(serverECDH.PublicKey); + SharedKey = k.ToBigInteger2().ToByteArray().Reverse(); + + return; + } +#endif 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); @@ -98,5 +139,23 @@ private void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, b var k1 = _keyAgreement.CalculateAgreement(publicKey); SharedKey = k1.ToByteArray().ToBigInteger2().ToByteArray().Reverse(); } + +#if NET8_0_OR_GREATER + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + _clientECDH?.Dispose(); + } + } + + private static bool IsNonWindowsOrWindowsVersionAtLeast(int major) + { + return Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major >= major; + } +#endif } } diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH256.cs b/src/Renci.SshNet/Security/KeyExchangeECDH256.cs index b09652de8..29d3dd587 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH256.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH256.cs @@ -1,6 +1,4 @@ using Renci.SshNet.Abstractions; -using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec; -using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; namespace Renci.SshNet.Security { @@ -15,14 +13,11 @@ public override string Name } /// - /// Gets Curve Parameter. + /// Gets curve name. /// - protected override X9ECParameters CurveParameter + protected override string CurveName { - get - { - return SecNamedCurves.GetByName("P-256"); - } + get { return "secp256r1"; } } /// diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH384.cs b/src/Renci.SshNet/Security/KeyExchangeECDH384.cs index cba304305..c77ae8899 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH384.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH384.cs @@ -1,6 +1,4 @@ using Renci.SshNet.Abstractions; -using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec; -using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; namespace Renci.SshNet.Security { @@ -15,14 +13,11 @@ public override string Name } /// - /// Gets Curve Parameter. + /// Gets curve name. /// - protected override X9ECParameters CurveParameter + protected override string CurveName { - get - { - return SecNamedCurves.GetByName("P-384"); - } + get { return "secp384r1"; } } /// diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH521.cs b/src/Renci.SshNet/Security/KeyExchangeECDH521.cs index ce5b35515..389dd2073 100644 --- a/src/Renci.SshNet/Security/KeyExchangeECDH521.cs +++ b/src/Renci.SshNet/Security/KeyExchangeECDH521.cs @@ -1,6 +1,4 @@ using Renci.SshNet.Abstractions; -using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec; -using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; namespace Renci.SshNet.Security { @@ -15,14 +13,11 @@ public override string Name } /// - /// Gets Curve Parameter. + /// Gets curve name. /// - protected override X9ECParameters CurveParameter + protected override string CurveName { - get - { - return SecNamedCurves.GetByName("P-521"); - } + get { return "secp521r1"; } } ///