github sshnet/SSH.NET 2024.2.0

2 days ago

New features

  • Add support for PKCS#8 private key files
  • Add additional async overloads on SftpClient
  • Add support for OpenSSH certificates
  • Add support for chacha20-poly1305@openssh.com cipher algorithm
  • Increase support for aes*-gcm@openssh.com and zlib@openssh.com on lower targets

This release takes a dependency on BouncyCastle in an effort to eliminate primitive crypto code from the library. It also takes a dependency on System.Formats.Asn1 on lower targets.

Breaking changes

  • A number of legacy algorithms were dropped in #1442
  • The implementation of DSA was changed from using handwritten code to using System.Cryptography in #1458. See the PR description for behaviour changes this could cause.
  • Renci.SshNet.Common.BigInteger was deleted and its uses replaced with System.Numerics.BigInteger in #1469
  • Renci.SshNet.Common.DerData and Renci.SshNet.Common.ObjectIdentifier were deleted in #1490 and uses replaced with System.Formats.Asn1
  • See the full API diff at the end

What's Changed

New Contributors

Full Changelog: 2024.1.0...2024.2.0

API diff
namespace Renci.SshNet
{
     public interface IBaseClient : System.IDisposable
     {
+        event System.EventHandler<Renci.SshNet.Common.SshIdentificationEventArgs>? ServerIdentificationReceived;
     }

     public interface ISftpClient : Renci.SshNet.IBaseClient, System.IDisposable
     {
+        System.Threading.Tasks.Task ChangeDirectoryAsync(string path, System.Threading.CancellationToken cancellationToken = default);
+        System.Threading.Tasks.Task CreateDirectoryAsync(string path, System.Threading.CancellationToken cancellationToken = default);
+        System.Threading.Tasks.Task DeleteAsync(string path, System.Threading.CancellationToken cancellationToken = default);
+        System.Threading.Tasks.Task DeleteDirectoryAsync(string path, System.Threading.CancellationToken cancellationToken = default);
     }

+    public interface ISshClient : Renci.SshNet.IBaseClient, System.IDisposable
+    {
+        System.Collections.Generic.IEnumerable<Renci.SshNet.ForwardedPort> ForwardedPorts { get; }
+        void AddForwardedPort(Renci.SshNet.ForwardedPort port);
+        Renci.SshNet.SshCommand CreateCommand(string commandText);
+        Renci.SshNet.SshCommand CreateCommand(string commandText, System.Text.Encoding encoding);
+        Renci.SshNet.Shell CreateShell(System.IO.Stream input, System.IO.Stream output, System.IO.Stream extendedOutput);
+        Renci.SshNet.Shell CreateShell(System.Text.Encoding encoding, string input, System.IO.Stream output, System.IO.Stream extendedOutput);
+        Renci.SshNet.Shell CreateShell(System.IO.Stream input, System.IO.Stream output, System.IO.Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, System.Collections.Generic.IDictionary<Renci.SshNet.Common.TerminalModes, uint> terminalModes);
+        Renci.SshNet.Shell CreateShell(System.IO.Stream input, System.IO.Stream output, System.IO.Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, System.Collections.Generic.IDictionary<Renci.SshNet.Common.TerminalModes, uint>? terminalModes, int bufferSize);
+        Renci.SshNet.Shell CreateShell(System.Text.Encoding encoding, string input, System.IO.Stream output, System.IO.Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, System.Collections.Generic.IDictionary<Renci.SshNet.Common.TerminalModes, uint> terminalModes);
+        Renci.SshNet.Shell CreateShell(System.Text.Encoding encoding, string input, System.IO.Stream output, System.IO.Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, System.Collections.Generic.IDictionary<Renci.SshNet.Common.TerminalModes, uint>? terminalModes, int bufferSize);
+        Renci.SshNet.Shell CreateShellNoTerminal(System.IO.Stream input, System.IO.Stream output, System.IO.Stream extendedOutput, int bufferSize = -1);
+        Renci.SshNet.ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize);
+        Renci.SshNet.ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, System.Collections.Generic.IDictionary<Renci.SshNet.Common.TerminalModes, uint>? terminalModeValues);
+        Renci.SshNet.ShellStream CreateShellStreamNoTerminal(int bufferSize = -1);
+        void RemoveForwardedPort(Renci.SshNet.ForwardedPort port);
+        Renci.SshNet.SshCommand RunCommand(string commandText);
+    }

     public class PrivateKeyConnectionInfo : Renci.SshNet.ConnectionInfo, System.IDisposable
     {
-        public PrivateKeyConnectionInfo(string host, string username, params Renci.SshNet.PrivateKeyFile[] keyFiles) { }
+        public PrivateKeyConnectionInfo(string host, string username, params Renci.SshNet.IPrivateKeySource[] keyFiles) { }
     }

     public class PrivateKeyFile : Renci.SshNet.IPrivateKeySource, System.IDisposable
     {
-        public PrivateKeyFile(System.IO.Stream privateKey, string passPhrase) { }
+        public PrivateKeyFile(System.IO.Stream privateKey, string? passPhrase) { }
-        public PrivateKeyFile(string fileName, string passPhrase) { }
+        public PrivateKeyFile(string fileName, string? passPhrase) { }
+        public PrivateKeyFile(System.IO.Stream privateKey, string? passPhrase, System.IO.Stream? certificate) { }
+        public PrivateKeyFile(string fileName, string? passPhrase, string? certificateFileName) { }
+        public Renci.SshNet.Security.Certificate? Certificate { get; }
     }

     public class SftpClient : Renci.SshNet.BaseClient, Renci.SshNet.IBaseClient, Renci.SshNet.ISftpClient, System.IDisposable
     {
+        public System.Threading.Tasks.Task ChangeDirectoryAsync(string path, System.Threading.CancellationToken cancellationToken = default) { }
+        public System.Threading.Tasks.Task CreateDirectoryAsync(string path, System.Threading.CancellationToken cancellationToken = default) { }
+        public System.Threading.Tasks.Task DeleteAsync(string path, System.Threading.CancellationToken cancellationToken = default) { }
+        public System.Threading.Tasks.Task DeleteDirectoryAsync(string path, System.Threading.CancellationToken cancellationToken = default) { }
+        public System.Threading.Tasks.Task<bool> ExistsAsync(string path, System.Threading.CancellationToken cancellationToken = default) { }
+        public System.Threading.Tasks.Task<Renci.SshNet.Sftp.ISftpFile> GetAsync(string path, System.Threading.CancellationToken cancellationToken) { }
     }

-    public class SshClient : Renci.SshNet.BaseClient
+    public class SshClient : Renci.SshNet.BaseClient, Renci.SshNet.IBaseClient, Renci.SshNet.ISshClient, System.IDisposable
     {
     }

}
namespace Renci.SshNet.Common
{
-    public struct BigInteger : System.IComparable, System.IComparable<Renci.SshNet.Common.BigInteger>, System.IEquatable<Renci.SshNet.Common.BigInteger>, System.IFormattable
-    {
-    }

-    public class DerData
-    {
-        public DerData() { }
-        public DerData(byte[] data, bool construct = false) { }
-        public bool IsEndOfData { get; }
-        public byte[] Encode() { }
-        public Renci.SshNet.Common.BigInteger ReadBigInteger() { }
-        public byte[] ReadBitString() { }
-        public byte ReadByte() { }
-        public byte[] ReadBytes(int length) { }
-        public int ReadInteger() { }
-        public int ReadLength() { }
-        public byte[] ReadObject() { }
-        public byte[] ReadOctetString() { }
-        public void Write(Renci.SshNet.Common.BigInteger data) { }
-        public void Write(Renci.SshNet.Common.DerData data) { }
-        public void Write(Renci.SshNet.Common.ObjectIdentifier identifier) { }
-        public void Write(bool data) { }
-        public void Write(byte[] data) { }
-        public void Write(uint data) { }
-        public void WriteBitstring(byte[] data) { }
-        public void WriteBytes(System.Collections.Generic.IEnumerable<byte> data) { }
-        public void WriteNull() { }
-        public void WriteObjectIdentifier(byte[] bytes) { }
-    }

     public class HostKeyEventArgs : System.EventArgs
     {
+        public Renci.SshNet.Security.Certificate? Certificate { get; }
     }

-    public struct ObjectIdentifier
-    {
-        public ObjectIdentifier(params ulong[] identifiers) { }
-        public ulong[] Identifiers { get; }
-    }

     public abstract class SshData
     {
-        protected void Write(Renci.SshNet.Common.BigInteger data) { }
+        protected void Write(System.Numerics.BigInteger data) { }
     }

     public class SshDataStream : System.IO.MemoryStream
     {
-        public Renci.SshNet.Common.BigInteger ReadBigInt() { }
+        public System.Numerics.BigInteger ReadBigInt() { }
-        public void Write(Renci.SshNet.Common.BigInteger data) { }
+        public void Write(System.Numerics.BigInteger data) { }
     }

}
namespace Renci.SshNet.Compression
{
+    public class Zlib : Renci.SshNet.Compression.Compressor
+    {
+        public Zlib() { }
+        protected Zlib(bool delayedCompression) { }
+        public override string Name { get; }
+        protected override byte[] CompressCore(byte[] data, int offset, int length) { }
+        protected override byte[] DecompressCore(byte[] data, int offset, int length) { }
+        protected override void Dispose(bool disposing) { }
+    }

+    public class ZlibOpenSsh : Renci.SshNet.Compression.Zlib
+    {
+        public ZlibOpenSsh() { }
+        public override string Name { get; }
+    }

}
namespace Renci.SshNet.Messages.Transport
{
     public class KeyExchangeDhGroupExchangeGroup : Renci.SshNet.Messages.Message
     {
-        public Renci.SshNet.Common.BigInteger SafePrime { get; }
-        public Renci.SshNet.Common.BigInteger SubGroup { get; }
+        public System.Numerics.BigInteger SafePrime { get; }
+        public System.Numerics.BigInteger SubGroup { get; }
     }

}
namespace Renci.SshNet.Security
{
+    public class Certificate
+    {
+        public Certificate(byte[] data) { }
+        public byte[] CertificateAuthorityKey { get; }
+        public string CertificateAuthorityKeyFingerPrint { get; }
+        public System.Collections.Generic.IDictionary<string, string> CriticalOptions { get; }
+        public System.Collections.Generic.IDictionary<string, string> Extensions { get; }
+        public Renci.SshNet.Security.Key Key { get; }
+        public string KeyId { get; }
+        public string Name { get; }
+        public byte[] Nonce { get; }
+        public ulong Serial { get; }
+        public byte[] Signature { get; }
+        public Renci.SshNet.Security.Certificate.CertificateType Type { get; }
+        public System.DateTimeOffset ValidAfter { get; }
+        public ulong ValidAfterUnixSeconds { get; }
+        public System.DateTimeOffset ValidBefore { get; }
+        public ulong ValidBeforeUnixSeconds { get; }
+        public System.Collections.Generic.IList<string> ValidPrincipals { get; }
+        public enum CertificateType : uint
+        {
+            User = 1u,
+            Host = 2u,
+        }
+    }

-    public class CertificateHostAlgorithm : Renci.SshNet.Security.HostAlgorithm
+    public class CertificateHostAlgorithm : Renci.SshNet.Security.KeyHostAlgorithm
     {
-        public CertificateHostAlgorithm(string name) { }
+        public CertificateHostAlgorithm(string name, Renci.SshNet.Security.Certificate certificate, System.Collections.Generic.IReadOnlyDictionary<string, System.Func<byte[], Renci.SshNet.Security.KeyHostAlgorithm>> keyAlgorithms) { }
+        public CertificateHostAlgorithm(string name, Renci.SshNet.Security.Key privateKey, Renci.SshNet.Security.Certificate certificate) { }
+        public CertificateHostAlgorithm(string name, Renci.SshNet.Security.Certificate certificate, Renci.SshNet.Security.Cryptography.DigitalSignature digitalSignature, System.Collections.Generic.IReadOnlyDictionary<string, System.Func<byte[], Renci.SshNet.Security.KeyHostAlgorithm>> keyAlgorithms) { }
+        public CertificateHostAlgorithm(string name, Renci.SshNet.Security.Key privateKey, Renci.SshNet.Security.Certificate certificate, Renci.SshNet.Security.Cryptography.DigitalSignature digitalSignature) { }
+        public Renci.SshNet.Security.Certificate Certificate { get; }
     }

     public class DsaKey : Renci.SshNet.Security.Key, System.IDisposable
     {
-        public DsaKey(Renci.SshNet.Common.BigInteger p, Renci.SshNet.Common.BigInteger q, Renci.SshNet.Common.BigInteger g, Renci.SshNet.Common.BigInteger y, Renci.SshNet.Common.BigInteger x) { }
+        public DsaKey(System.Numerics.BigInteger p, System.Numerics.BigInteger q, System.Numerics.BigInteger g, System.Numerics.BigInteger y, System.Numerics.BigInteger x) { }
-        public Renci.SshNet.Common.BigInteger G { get; }
+        public System.Numerics.BigInteger G { get; }
-        public Renci.SshNet.Common.BigInteger P { get; }
-        public override Renci.SshNet.Common.BigInteger[] Public { get; }
-        public Renci.SshNet.Common.BigInteger Q { get; }
-        public Renci.SshNet.Common.BigInteger X { get; }
-        public Renci.SshNet.Common.BigInteger Y { 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; }
     }

     public class ED25519Key : Renci.SshNet.Security.Key, System.IDisposable
     {
-        public override Renci.SshNet.Common.BigInteger[] Public { get; }
+        public override System.Numerics.BigInteger[] Public { get; }
     }

     public class EcdsaKey : Renci.SshNet.Security.Key, System.IDisposable
     {
-        public byte[] PrivateKey { get; }
-        public override Renci.SshNet.Common.BigInteger[] Public { get; }
+        public byte[]? PrivateKey { get; }
+        public override System.Numerics.BigInteger[] Public { get; }
     }

     public abstract class Key
     {
-        public abstract Renci.SshNet.Common.BigInteger[] Public { get; }
+        public abstract System.Numerics.BigInteger[] Public { get; }
     }

     public class RsaKey : Renci.SshNet.Security.Key, System.IDisposable
     {
-        public RsaKey(Renci.SshNet.Common.BigInteger modulus, Renci.SshNet.Common.BigInteger exponent, Renci.SshNet.Common.BigInteger d, Renci.SshNet.Common.BigInteger p, Renci.SshNet.Common.BigInteger q, Renci.SshNet.Common.BigInteger inverseQ) { }
-        public Renci.SshNet.Common.BigInteger D { get; }
-        public Renci.SshNet.Common.BigInteger DP { get; }
-        public Renci.SshNet.Common.BigInteger DQ { get; }
+        public RsaKey(System.Numerics.BigInteger modulus, System.Numerics.BigInteger exponent, System.Numerics.BigInteger d, System.Numerics.BigInteger p, System.Numerics.BigInteger q, System.Numerics.BigInteger inverseQ) { }
+        public System.Numerics.BigInteger D { get; }
+        public System.Numerics.BigInteger DP { get; }
+        public System.Numerics.BigInteger DQ { get; }
-        public Renci.SshNet.Common.BigInteger Exponent { get; }
-        public Renci.SshNet.Common.BigInteger InverseQ { get; }
+        public System.Numerics.BigInteger Exponent { get; }
+        public System.Numerics.BigInteger InverseQ { get; }
-        public Renci.SshNet.Common.BigInteger Modulus { get; }
-        public Renci.SshNet.Common.BigInteger P { get; }
-        public override Renci.SshNet.Common.BigInteger[] Public { get; }
-        public Renci.SshNet.Common.BigInteger Q { get; }
+        public System.Numerics.BigInteger Modulus { get; }
+        public System.Numerics.BigInteger P { get; }
+        public override System.Numerics.BigInteger[] Public { get; }
+        public System.Numerics.BigInteger Q { get; }
     }

     public sealed class SshKeyData : Renci.SshNet.Common.SshData
     {
-        public SshKeyData(string name, Renci.SshNet.Common.BigInteger[] keys) { }
+        public SshKeyData(string name, System.Numerics.BigInteger[] keys) { }
-        public Renci.SshNet.Common.BigInteger[] Keys { get; }
+        public System.Numerics.BigInteger[] Keys { get; }
     }

}
namespace Renci.SshNet.Security.Cryptography
{
     public abstract class Cipher
     {
-        public byte[] Decrypt(byte[] input) { }
+        public virtual byte[] Decrypt(byte[] input) { }
     }

-    public class HMACMD5 : System.Security.Cryptography.HMACMD5
-    {
-        public HMACMD5(byte[] key) { }
-        public HMACMD5(byte[] key, int hashSize) { }
-        public override int HashSize { get; }
-        protected override byte[] HashFinal() { }
-    }

-    public class HMACSHA1 : System.Security.Cryptography.HMACSHA1
-    {
-        public HMACSHA1(byte[] key) { }
-        public HMACSHA1(byte[] key, int hashSize) { }
-        public override int HashSize { get; }
-        protected override byte[] HashFinal() { }
-    }

-    public class HMACSHA256 : System.Security.Cryptography.HMACSHA256
-    {
-        public HMACSHA256(byte[] key) { }
-        public HMACSHA256(byte[] key, int hashSize) { }
-        public override int HashSize { get; }
-        protected override byte[] HashFinal() { }
-    }

-    public class HMACSHA384 : System.Security.Cryptography.HMACSHA384
-    {
-        public HMACSHA384(byte[] key) { }
-        public HMACSHA384(byte[] key, int hashSize) { }
-        public override int HashSize { get; }
-        protected override byte[] HashFinal() { }
-    }

-    public class HMACSHA512 : System.Security.Cryptography.HMACSHA512
-    {
-        public HMACSHA512(byte[] key) { }
-        public HMACSHA512(byte[] key, int hashSize) { }
-        public override int HashSize { get; }
-        protected override byte[] HashFinal() { }
-    }

-    public abstract class StreamCipher : Renci.SshNet.Security.Cryptography.SymmetricCipher
-    {
-        protected StreamCipher(byte[] key) { }
-    }

}
namespace Renci.SshNet.Security.Cryptography.Ciphers
{
-    public sealed class Arc4Cipher : Renci.SshNet.Security.Cryptography.StreamCipher
-    {
-        public Arc4Cipher(byte[] key, bool dischargeFirstBytes) { }
-        public override byte MinimumSize { get; }
-        public override byte[] Decrypt(byte[] input, int offset, int length) { }
-        public override byte[] Encrypt(byte[] input, int offset, int length) { }
-    }

-    public sealed class BlowfishCipher : Renci.SshNet.Security.Cryptography.BlockCipher
-    {
-        public BlowfishCipher(byte[] key, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding padding) { }
-        public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-        public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-    }

-    public sealed class CastCipher : Renci.SshNet.Security.Cryptography.BlockCipher
-    {
-        public CastCipher(byte[] key, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding padding) { }
-        public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-        public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-    }

-    public sealed class SerpentCipher : Renci.SshNet.Security.Cryptography.BlockCipher
-    {
-        public SerpentCipher(byte[] key, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding padding) { }
-        public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-        public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-    }

-    public sealed class TwofishCipher : Renci.SshNet.Security.Cryptography.BlockCipher
-    {
-        public TwofishCipher(byte[] key, Renci.SshNet.Security.Cryptography.Ciphers.CipherMode mode, Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding padding) { }
-        public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-        public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { }
-    }

}
namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings
{
-    public class PKCS5Padding : Renci.SshNet.Security.Cryptography.Ciphers.CipherPadding
-        public PKCS5Padding() { }
-        public override byte[] Pad(byte[] input, int offset, int length, int paddinglength) { }
-        public override byte[] Pad(int blockSize, byte[] input, int offset, int length) { }
-    }

}
namespace Renci.SshNet.Sftp
{
     public interface ISftpFile
     {
+        System.Threading.Tasks.Task DeleteAsync(System.Threading.CancellationToken cancellationToken = default);
     }

     public sealed class SftpFile : Renci.SshNet.Sftp.ISftpFile
     {
+        public System.Threading.Tasks.Task DeleteAsync(System.Threading.CancellationToken cancellationToken = default) { }
     }
}

Don't miss a new SSH.NET release

NewReleases is sending notifications on new releases.