Highlights
- Add support for post-quantum key exchange methods
sntrup761x25519-sha512
andmlkem768x25519-sha256
- Add support for PuTTY private key files
- Add logging capability via Microsoft.Extensions.Logging
Breaking changes
- Support for DSA was dropped in #1558
CipherPadding
was deleted in #1546 and uses replaced withOrg.BouncyCastle.Crypto.Paddings.IBlockCipherPadding
- See the full API diff at the end
What's Changed
- Bump version by @Rob-Hague in #1533
- fix Package Downgrade Warning with .NET 9 SDK by @mus65 in #1538
- Update PrivateKeyFile.cs by @scott-xu in #1541
- Handle lower-case hex in private key's salt field by @RiJo in #179
- Split PrivateKeyFile into different implementations. by @scott-xu in #1542
- Migrate from AppVeyor to GitHub Actions by @mus65 in #1539
- Add .NET 9 target by @mus65 in #1480
- Remove appveyor.yml by @mus65 in #1547
- Replace DiagnosticAbstration with Microsoft.Extensions.Logging.Abstractions by @mus65 in #1509
- [Private Key] Add support for PuTTY private key file format (V3 and V2) by @scott-xu in #1543
- fix newline characters in comment by @Varorbc in #1550
- Pin Alpine Linux image to 3.20 by @Rob-Hague in #1554
- Fix consumption of NBGV version properties in build by @Rob-Hague in #1544
- Drop net7.0 target by @Rob-Hague in #1468
- Add padding when encrypt and remove padding when decrypt by @scott-xu in #1545
- Use System.Security.Cryptography for TripleDesCipher by @scott-xu in #1546
- [PQC] Add support for sntrup761x25519-sha512 key exchange method by @scott-xu in #1562
- Drop DSA by @Rob-Hague in #1558
- Create dependabot.yml for docker image by @Rob-Hague in #1559
- Fix hang/unhandled exception in SshCommand upon disconnect by @Rob-Hague in #1565
- Bump alpine from 3.20 to 3.21 in /test/Renci.SshNet.IntegrationTests by @dependabot in #1567
- [PQC] Add support for mlkem768x25519-sha256 key exchange method by @scott-xu in #1563
- Move IDisposable implementation declaration from inheritees to parent by @bad-samaritan in #746
- Update bound port on ForwardedPortDynamic after connection (in case original was passed as zero) by @timyhac in #1577
- Configure Dependabot to also update NuGet and GitHub Actions by @mus65 in #1581
- Bump Microsoft.NET.Test.Sdk from 17.11.1 to 17.12.0 by @dependabot in #1585
- Bump actions/configure-pages from 4 to 5 by @dependabot in #1582
- fix: UnhandledException: System.ObjectDisposedException. by @SoftStoneDevelop in #1590
- Bump test dependencies by @dependabot in #1583
- Tweak logging.md by @Rob-Hague in #1601
- Reply to global requests when want_reply is true by @Rob-Hague in #1600
- Don't dispose channel when completing SshCommand by @Rob-Hague in #1596
- Drop net6.0 target by @mus65 in #1580
- remove Reverse extension to avoid source-breaking change with .NET 10 by @mus65 in #1606
- Bump the dependencies group with 7 updates by @dependabot in #1607
- CI: run .NET Framework Integration Tests on Windows by @mus65 in #1615
- Fix API break on KeyExchange by @Rob-Hague in #1609
- Add a Stream buffer validation helper by @Rob-Hague in #1605
- Add an OrderedDictionary implementation for algorithm priorities by @Rob-Hague in #1611
- lock SendData to fix random connection failures by @mus65 in #1623
- Only enable TreatWarningsAsErrors in Release by @mus65 in #1624
- Remove unused bcrypt password hashing code by @mus65 in #1626
- Bump the dependencies group with 5 updates by @dependabot in #1625
- Added ExistsAsync and GetAsync to ISftpClient by @Maarty in #1628
New Contributors
- @RiJo made their first contribution in #179
- @Varorbc made their first contribution in #1550
- @dependabot made their first contribution in #1567
- @bad-samaritan made their first contribution in #746
- @timyhac made their first contribution in #1577
- @SoftStoneDevelop made their first contribution in #1590
- @Maarty made their first contribution in #1628
Full Changelog: 2024.2.0...2025.0.0
API diff
namespace Renci.SshNet
{
public abstract class AuthenticationMethod
{
+ public void Dispose();
+ protected virtual void Dispose(bool disposing);
}
public class ConnectionInfo
{
- public System.Collections.Generic.IDictionary<string, System.Func<Renci.SshNet.Compression.Compressor>> CompressionAlgorithms { get; }
+ public Renci.SshNet.IOrderedDictionary<string, System.Func<Renci.SshNet.Compression.Compressor>> CompressionAlgorithms { get; }
- public System.Collections.Generic.IDictionary<string, Renci.SshNet.CipherInfo> Encryptions { get; }
+ public Renci.SshNet.IOrderedDictionary<string, Renci.SshNet.CipherInfo> Encryptions { get; }
- public System.Collections.Generic.IDictionary<string, Renci.SshNet.HashInfo> HmacAlgorithms { get; }
+ public Renci.SshNet.IOrderedDictionary<string, Renci.SshNet.HashInfo> HmacAlgorithms { get; }
- public System.Collections.Generic.IDictionary<string, System.Func<byte[], Renci.SshNet.Security.KeyHostAlgorithm>> HostKeyAlgorithms { get; }
+ public Renci.SshNet.IOrderedDictionary<string, System.Func<byte[], Renci.SshNet.Security.KeyHostAlgorithm>> HostKeyAlgorithms { get; }
- public System.Collections.Generic.IDictionary<string, System.Func<Renci.SshNet.Security.IKeyExchange>> KeyExchangeAlgorithms { get; }
+ public Renci.SshNet.IOrderedDictionary<string, System.Func<Renci.SshNet.Security.IKeyExchange>> KeyExchangeAlgorithms { get; }
}
public interface ISftpClient : Renci.SshNet.IBaseClient
{
+ System.Threading.Tasks.Task<bool> ExistsAsync(string path, System.Threading.CancellationToken cancellationToken = null);
+ System.Threading.Tasks.Task<Renci.SshNet.Sftp.ISftpFile> GetAsync(string path, System.Threading.CancellationToken cancellationToken);
}
public class KeyboardInteractiveAuthenticationMethod : Renci.SshNet.AuthenticationMethod
{
- public void Dispose();
- protected virtual void Dispose(bool disposing);
+ protected override void Dispose(bool disposing);
}
public class NoneAuthenticationMethod : Renci.SshNet.AuthenticationMethod
{
- public void Dispose();
- protected virtual void Dispose(bool disposing);
+ protected override void Dispose(bool disposing);
}
public class PasswordAuthenticationMethod : Renci.SshNet.AuthenticationMethod
{
- public void Dispose();
- protected virtual void Dispose(bool disposing);
+ protected override void Dispose(bool disposing);
}
public class PrivateKeyAuthenticationMethod : Renci.SshNet.AuthenticationMethod
{
- public void Dispose();
- protected virtual void Dispose(bool disposing);
+ protected override void Dispose(bool disposing);
}
+ public interface IOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>
+ {
+ bool ContainsKey(TKey key);
+ bool ContainsValue(TValue value);
+ System.Collections.Generic.KeyValuePair<TKey, TValue> GetAt(int index);
+ int IndexOf(TKey key);
+ void Insert(int index, TKey key, TValue value);
+ bool Remove(TKey key, out TValue value);
+ void RemoveAt(int index);
+ void SetAt(int index, TKey key, TValue value);
+ void SetAt(int index, TValue value);
+ void SetPosition(TKey key, int newIndex);
+ void SetPosition(int index, int newIndex);
+ bool TryAdd(TKey key, TValue value, out int index);
+ bool TryAdd(TKey key, TValue value);
+ bool TryGetValue(TKey key, out TValue value, out int index);
+ bool TryGetValue(TKey key, out TValue value);
+ int Count { get; }
+ TValue this[TKey key] { get; set; }
+ System.Collections.Generic.ICollection<TKey> Keys { get; }
+ System.Collections.Generic.ICollection<TValue> Values { get; }
+ }
+ public static class SshNetLoggingConfiguration
+ {
+ public static void InitializeLogging(Microsoft.Extensions.Logging.ILoggerFactory loggerFactory);
+ }
}
namespace Renci.SshNet.Messages.Transport
{
+ public class KeyExchangeHybridReplyMessage : Renci.SshNet.Messages.Message
+ {
+ public KeyExchangeHybridReplyMessage();
+ protected override void LoadData();
+ protected override void SaveData();
+ protected override int BufferCapacity { get; }
+ public byte[] KS { get; }
+ public override string MessageName { get; }
+ public override byte MessageNumber { get; }
+ public byte[] Signature { get; }
+ public byte[] SReply { get; }
+ }
}
namespace Renci.SshNet.Security
{
- public class DsaKey : Renci.SshNet.Security.Key
- {
- public DsaKey(Renci.SshNet.Security.SshKeyData publicKeyData);
- public DsaKey(byte[] privateKeyData);
- public DsaKey(System.Numerics.BigInteger p, System.Numerics.BigInteger q, System.Numerics.BigInteger g, System.Numerics.BigInteger y, System.Numerics.BigInteger x);
- public void Dispose();
- protected virtual void Dispose(bool disposing);
- protected internal override Renci.SshNet.Security.Cryptography.DigitalSignature DigitalSignature { get; }
- public System.Numerics.BigInteger G { get; }
- public override int KeyLength { get; }
- public System.Numerics.BigInteger P { get; }
- public override System.Numerics.BigInteger[] Public { get; }
- public System.Numerics.BigInteger Q { get; }
- public System.Numerics.BigInteger X { get; }
- public System.Numerics.BigInteger Y { get; }
- }
}
namespace Renci.SshNet.Security.Cryptography
{
public abstract class BlockCipher : Renci.SshNet.Security.Cryptography.SymmetricCipher
{
- protected BlockCipher(byte[] key, byte blockSize, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding padding)
+ protected BlockCipher(byte[] key, byte blockSize, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Org.BouncyCastle.Crypto.Paddings.IBlockCipherPadding padding)
}
- public class DsaDigitalSignature : Renci.SshNet.Security.Cryptography.DigitalSignature
- {
- public DsaDigitalSignature(Renci.SshNet.Security.DsaKey key);
- public void Dispose();
- protected virtual void Dispose(bool disposing);
- public override byte[] Sign(byte[] input);
- public override bool Verify(byte[] input, byte[] signature);
- }
}
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
public sealed class AesCipher : Renci.SshNet.Security.Cryptography.BlockCipher
{
- public void Dispose(bool disposing);
}
- public abstract class CipherPadding
- {
- protected CipherPadding();
- public abstract byte[] Pad(byte[] input, int offset, int length, int paddinglength);
- public byte[] Pad(byte[] input, int paddinglength);
- public abstract byte[] Pad(int blockSize, byte[] input, int offset, int length);
- public byte[] Pad(int blockSize, byte[] input);
- }
- public class DesCipher : Renci.SshNet.Security.Cryptography.BlockCipher
- {
- public DesCipher(byte[] key, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding padding) : base(default(byte[])!, default(byte)!, default(Renci.SshNet.Security.Cryptography.Ciphers.CipherMode)!, default(Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding)!);
- public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset);
- protected static void DesFunc(int[] wKey, byte[] input, int inOff, byte[] outBytes, int outOff);
- public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset);
- protected int[] GenerateWorkingKey(bool encrypting, byte[] key);
- protected virtual void ValidateKey();
- }
public sealed class TripleDesCipher : Renci.SshNet.Security.Cryptography.BlockCipher
{
- public TripleDesCipher(byte[] key, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding padding);
- protected override void ValidateKey();
+ public TripleDesCipher(byte[] key, byte[] iv, System.Security.Cryptography.CipherMode mode, bool pkcs7Padding);
+ public override byte[] Decrypt(byte[] input, int offset, int length);
+ public void Dispose();
+ public override byte[] Encrypt(byte[] input, int offset, int length);
}
}
- namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings
- {
- public class PKCS7Padding : Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding
- {
- public PKCS7Padding();
- public override byte[] Pad(byte[] input, int offset, int length, int paddinglength);
- public override byte[] Pad(int blockSize, byte[] input, int offset, int length);
- }
- }