<PackageReference Include="SSH.NET" Version="2025.1.0" />

KeyExchangeECCurve25519

using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Agreement; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math.EC.Rfc8032; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Security.Cryptography; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Numerics; using Org.BouncyCastle.Math.EC.Rfc8032; using Renci.SshNet.Common; using Renci.SshNet.Security.Cryptography; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Numerics; using System; using System.Collections.Generic; using System.Collections.ObjectModel; namespace Renci.SshNet { public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable { private bool _isDisposed; public ICollection<IPrivateKeySource> KeyFiles { get; set; } public PrivateKeyConnectionInfo(string host, string username, params IPrivateKeySource[] keyFiles) : this(host, 22, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) { } public PrivateKeyConnectionInfo(string host, int port, string username, params IPrivateKeySource[] keyFiles) : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) { } public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles) : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) { } public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles) : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) { } public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles) : this(host, 22, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) { } public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles) : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) { } public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles) : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, keyFiles) { } public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles) : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(username, keyFiles)) { KeyFiles = new Collection<IPrivateKeySource>(keyFiles); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_isDisposed && disposing) { if (base.AuthenticationMethods != null) { foreach (AuthenticationMethod authenticationMethod in base.AuthenticationMethods) { authenticationMethod.Dispose(); } } _isDisposed = true; } } } } namespace Renci.SshNet.Security { public class ED25519Key : Key, IDisposable { private ED25519DigitalSignature _digitalSignature; private bool _isDisposed; public override BigInteger[] Public => new BigInteger[1] { Extensions.ToBigInteger2(PublicKey) }; public override int KeyLength => Ed25519.PublicKeySize * 8; protected internal override DigitalSignature DigitalSignature { get { if (_digitalSignature == null) _digitalSignature = new ED25519DigitalSignature(this); return _digitalSignature; } } public byte[] PublicKey { get; } public byte[] PrivateKey { get; } public override string ToString() { return "ssh-ed25519"; } public ED25519Key(SshKeyData publicKeyData) { ThrowHelper.ThrowIfNull(publicKeyData, "publicKeyData"); if (publicKeyData.Name != "ssh-ed25519" || publicKeyData.Keys.Length != 1) throw new ArgumentException($"""{publicKeyData.Name}""{publicKeyData.Keys.Length}""", "publicKeyData"); PublicKey = Extensions.Pad(Extensions.TrimLeadingZeros(Extensions.ToByteArray(publicKeyData.Keys[0], false, true)), Ed25519.PublicKeySize); PrivateKey = new byte[Ed25519.SecretKeySize]; } public ED25519Key(byte[] privateKeyData) { PrivateKey = new byte[Ed25519.SecretKeySize]; PublicKey = new byte[Ed25519.PublicKeySize]; Buffer.BlockCopy(privateKeyData, 0, PrivateKey, 0, Ed25519.SecretKeySize); Ed25519.GeneratePublicKey(privateKeyData, 0, PublicKey, 0); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_isDisposed && disposing) _isDisposed = true; } } } namespace Renci.SshNet.Security { internal class KeyExchangeECCurve25519 : KeyExchangeEC { protected sealed class BouncyCastleImpl : Impl { private X25519Agreement _keyAgreement; public override byte[] GenerateClientPublicKey() { X25519KeyPairGenerator val = new X25519KeyPairGenerator(); val.Init(new X25519KeyGenerationParameters(CryptoAbstraction.SecureRandom)); AsymmetricCipherKeyPair val2 = val.GenerateKeyPair(); _keyAgreement = new X25519Agreement(); _keyAgreement.Init(val2.get_Private()); return val2.get_Public().GetEncoded(); } public override byte[] CalculateAgreement(byte[] serverPublicKey) { X25519PublicKeyParameters val = new X25519PublicKeyParameters(serverPublicKey); byte[] array = new byte[_keyAgreement.get_AgreementSize()]; _keyAgreement.CalculateAgreement(val, array, 0); return array; } } protected BouncyCastleImpl _impl; public override string Name => "curve25519-sha256"; protected override int HashSize => 256; public override void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage) { base.Start(session, message, sendClientInitMessage); _impl = new BouncyCastleImpl(); StartImpl(); } protected virtual void StartImpl() { base.Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); base.Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; _clientExchangeValue = _impl.GenerateClientPublicKey(); SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); } public override void Finish() { base.Finish(); FinishImpl(); } protected virtual void FinishImpl() { base.Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived; } protected override byte[] Hash(byte[] hashData) { return CryptoAbstraction.HashSHA256(hashData); } private void Session_KeyExchangeEcdhReplyMessageReceived(object sender, MessageEventArgs<KeyExchangeEcdhReplyMessage> e) { KeyExchangeEcdhReplyMessage message = e.Message; base.Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); HandleServerEcdhReply(message.KS, message.QS, message.Signature); Finish(); } private void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature) { _serverExchangeValue = serverExchangeValue; _hostKey = hostKey; _signature = signature; byte[] data = _impl.CalculateAgreement(serverExchangeValue); base.SharedKey = Extensions.ToByteArray(Extensions.ToBigInteger2(data), false, true); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) _impl?.Dispose(); } } }