Skip to content

Commit 901fd5a

Browse files
committed
Use BCL ECDiffieHellman for KeyExchange (.NET 8.0 onward only)
1 parent 9be67c0 commit 901fd5a

File tree

4 files changed

+78
-34
lines changed

4 files changed

+78
-34
lines changed

src/Renci.SshNet/Security/KeyExchangeECDH.cs

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using Renci.SshNet.Common;
33
using Renci.SshNet.Messages.Transport;
44

5-
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
5+
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec;
66
using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Agreement;
77
using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators;
88
using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters;
@@ -13,13 +13,17 @@ namespace Renci.SshNet.Security
1313
{
1414
internal abstract class KeyExchangeECDH : KeyExchangeEC
1515
{
16+
#if NET8_0_OR_GREATER
17+
private System.Security.Cryptography.ECDiffieHellman _clientECDH;
18+
#endif
19+
1620
/// <summary>
17-
/// Gets the parameter of the curve.
21+
/// Gets the name of the curve.
1822
/// </summary>
1923
/// <value>
20-
/// The parameter of the curve.
24+
/// The name of the curve.
2125
/// </value>
22-
protected abstract X9ECParameters CurveParameter { get; }
26+
protected abstract string CurveName { get; }
2327

2428
private ECDHCBasicAgreement _keyAgreement;
2529
private ECDomainParameters _domainParameters;
@@ -33,11 +37,30 @@ public override void Start(Session session, KeyExchangeInitMessage message, bool
3337

3438
Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived;
3539

36-
_domainParameters = new ECDomainParameters(CurveParameter.Curve,
37-
CurveParameter.G,
38-
CurveParameter.N,
39-
CurveParameter.H,
40-
CurveParameter.GetSeed());
40+
#if NET8_0_OR_GREATER
41+
if (IsNonWindowsOrWindowsVersionAtLeast(10))
42+
{
43+
_clientECDH = System.Security.Cryptography.ECDiffieHellman.Create();
44+
_clientECDH.GenerateKey(System.Security.Cryptography.ECCurve.CreateFromFriendlyName(CurveName));
45+
46+
var q = _clientECDH.PublicKey.ExportParameters().Q;
47+
48+
_clientExchangeValue = new byte[1 + q.X.Length + q.Y.Length];
49+
_clientExchangeValue[0] = 0x04;
50+
Buffer.BlockCopy(q.X, 0, _clientExchangeValue, 1, q.X.Length);
51+
Buffer.BlockCopy(q.Y, 0, _clientExchangeValue, q.X.Length + 1, q.Y.Length);
52+
53+
SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue));
54+
55+
return;
56+
}
57+
#endif
58+
var curveParameter = SecNamedCurves.GetByName(CurveName);
59+
_domainParameters = new ECDomainParameters(curveParameter.Curve,
60+
curveParameter.G,
61+
curveParameter.N,
62+
curveParameter.H,
63+
curveParameter.GetSeed());
4164

4265
var g = new ECKeyPairGenerator();
4366
g.Init(new ECKeyGenerationParameters(_domainParameters, new SecureRandom()));
@@ -46,7 +69,6 @@ public override void Start(Session session, KeyExchangeInitMessage message, bool
4669
_keyAgreement = new ECDHCBasicAgreement();
4770
_keyAgreement.Init(aKeyPair.Private);
4871
_clientExchangeValue = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded();
49-
5072
SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue));
5173
}
5274

@@ -91,12 +113,49 @@ private void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, b
91113
var y = new byte[cordSize];
92114
Buffer.BlockCopy(serverExchangeValue, cordSize + 1, y, 0, y.Length);
93115

116+
#if NET8_0_OR_GREATER
117+
if (IsNonWindowsOrWindowsVersionAtLeast(10))
118+
{
119+
using var serverECDH = System.Security.Cryptography.ECDiffieHellman.Create(new System.Security.Cryptography.ECParameters
120+
{
121+
Curve = System.Security.Cryptography.ECCurve.CreateFromFriendlyName(CurveName),
122+
Q =
123+
{
124+
X = x,
125+
Y = y,
126+
},
127+
});
128+
129+
var k = _clientECDH.DeriveRawSecretAgreement(serverECDH.PublicKey);
130+
SharedKey = k.ToBigInteger2().ToByteArray().Reverse();
131+
132+
return;
133+
}
134+
#endif
94135
var c = (FpCurve)_domainParameters.Curve;
95136
var q = c.CreatePoint(new Org.BouncyCastle.Math.BigInteger(1, x), new Org.BouncyCastle.Math.BigInteger(1, y));
96137
var publicKey = new ECPublicKeyParameters("ECDH", q, _domainParameters);
97138

98139
var k1 = _keyAgreement.CalculateAgreement(publicKey);
99140
SharedKey = k1.ToByteArray().ToBigInteger2().ToByteArray().Reverse();
100141
}
142+
143+
#if NET8_0_OR_GREATER
144+
145+
/// <inheritdoc/>
146+
protected override void Dispose(bool disposing)
147+
{
148+
base.Dispose(disposing);
149+
if (disposing)
150+
{
151+
_clientECDH?.Dispose();
152+
}
153+
}
154+
155+
private static bool IsNonWindowsOrWindowsVersionAtLeast(int major)
156+
{
157+
return Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major >= major;
158+
}
159+
#endif
101160
}
102161
}

src/Renci.SshNet/Security/KeyExchangeECDH256.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using Renci.SshNet.Abstractions;
2-
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec;
3-
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
42

53
namespace Renci.SshNet.Security
64
{
@@ -15,14 +13,11 @@ public override string Name
1513
}
1614

1715
/// <summary>
18-
/// Gets Curve Parameter.
16+
/// Gets curve name.
1917
/// </summary>
20-
protected override X9ECParameters CurveParameter
18+
protected override string CurveName
2119
{
22-
get
23-
{
24-
return SecNamedCurves.GetByName("P-256");
25-
}
20+
get { return "secp256r1"; }
2621
}
2722

2823
/// <summary>

src/Renci.SshNet/Security/KeyExchangeECDH384.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using Renci.SshNet.Abstractions;
2-
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec;
3-
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
42

53
namespace Renci.SshNet.Security
64
{
@@ -15,14 +13,11 @@ public override string Name
1513
}
1614

1715
/// <summary>
18-
/// Gets Curve Parameter.
16+
/// Gets curve name.
1917
/// </summary>
20-
protected override X9ECParameters CurveParameter
18+
protected override string CurveName
2119
{
22-
get
23-
{
24-
return SecNamedCurves.GetByName("P-384");
25-
}
20+
get { return "secp384r1"; }
2621
}
2722

2823
/// <summary>

src/Renci.SshNet/Security/KeyExchangeECDH521.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using Renci.SshNet.Abstractions;
2-
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec;
3-
using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
42

53
namespace Renci.SshNet.Security
64
{
@@ -15,14 +13,11 @@ public override string Name
1513
}
1614

1715
/// <summary>
18-
/// Gets Curve Parameter.
16+
/// Gets curve name.
1917
/// </summary>
20-
protected override X9ECParameters CurveParameter
18+
protected override string CurveName
2119
{
22-
get
23-
{
24-
return SecNamedCurves.GetByName("P-521");
25-
}
20+
get { return "secp521r1"; }
2621
}
2722

2823
/// <summary>

0 commit comments

Comments
 (0)