diff --git a/src/Renci.SshNet.IntegrationTests/.editorconfig b/src/Renci.SshNet.IntegrationTests/.editorconfig
new file mode 100644
index 000000000..b94e29112
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/.editorconfig
@@ -0,0 +1,32 @@
+[*.cs]
+
+#### SYSLIB diagnostics ####
+
+# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time
+#
+# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented.
+dotnet_diagnostic.SYSLIB1045.severity = none
+
+### StyleCop Analyzers rules ###
+
+#### .NET Compiler Platform analysers rules ####
+
+# IDE0007: Use var instead of explicit type
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007
+dotnet_diagnostic.IDE0007.severity = suggestion
+
+# IDE0028: Use collection initializers
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028
+dotnet_diagnostic.IDE0028.severity = suggestion
+
+# IDE0058: Remove unnecessary expression value
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0058
+dotnet_diagnostic.IDE0058.severity = suggestion
+
+# IDE0059: Remove unnecessary value assignment
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0059
+dotnet_diagnostic.IDE0059.severity = suggestion
+
+# IDE0230: Use UTF-8 string literal
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0230
+dotnet_diagnostic.IDE0230.severity = suggestion
diff --git a/src/Renci.SshNet.IntegrationTests/Dockerfile b/src/Renci.SshNet.IntegrationTests/Dockerfile
new file mode 100644
index 000000000..811d51543
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/Dockerfile
@@ -0,0 +1,49 @@
+FROM alpine:latest
+
+COPY --chown=root:root server/ssh /etc/ssh/
+COPY --chown=root:root server/script /opt/sshnet
+COPY user/sshnet /home/sshnet/.ssh
+
+RUN apk update && apk upgrade --no-cache && \
+ apk add --no-cache syslog-ng && \
+ # install and configure sshd
+ apk add --no-cache openssh && \
+ # install openssh-server-pam to allow for keyboard-interactive authentication
+ apk add --no-cache openssh-server-pam && \
+ dos2unix /etc/ssh/* && \
+ chmod 400 /etc/ssh/ssh*key && \
+ sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \
+ sed -i 's/#LogLevel\s*INFO/LogLevel DEBUG3/' /etc/ssh/sshd_config && \
+ echo 'PubkeyAcceptedAlgorithms ssh-rsa' >> /etc/ssh/sshd_config && \
+ chmod 646 /etc/ssh/sshd_config && \
+ # install and configure sudo
+ apk add --no-cache sudo && \
+ addgroup sudo && \
+ # allow root to run any command
+ echo 'root ALL=(ALL) ALL' > /etc/sudoers && \
+ # allow everyone in the 'sudo' group to run any command without a password
+ echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
+ # add user to run most of the integration tests
+ adduser -D sshnet && \
+ passwd -u sshnet && \
+ echo 'sshnet:ssh4ever' | chpasswd && \
+ dos2unix /home/sshnet/.ssh/* && \
+ chown -R sshnet:sshnet /home/sshnet && \
+ chmod -R 700 /home/sshnet/.ssh && \
+ chmod -R 644 /home/sshnet/.ssh/authorized_keys && \
+ # add user to administer container (update configs, restart sshd)
+ adduser -D sshnetadm && \
+ passwd -u sshnetadm && \
+ echo 'sshnetadm:ssh4ever' | chpasswd && \
+ addgroup sshnetadm sudo && \
+ dos2unix /opt/sshnet/* && \
+ # install shadow package; we use chage command in this package to expire/unexpire password of the sshnet user
+ apk add --no-cache shadow && \
+ # allow us to use telnet command; we use this in the remote port forwarding tests
+ apk --no-cache add busybox-extras && \
+ # install full-fledged ps command
+ apk add --no-cache procps
+
+EXPOSE 22 22
+
+ENTRYPOINT ["/opt/sshnet/start.sh"]
\ No newline at end of file
diff --git a/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj b/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj
new file mode 100644
index 000000000..244ee6f3a
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/IntegrationTests.csproj
@@ -0,0 +1,47 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+ false
+ true
+ true
+
+ $(NoWarn);CS1591
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+ LocalSshNet
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs
new file mode 100644
index 000000000..a03b04a3a
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/ScpClientTests.cs
@@ -0,0 +1,41 @@
+namespace IntegrationTests
+{
+ ///
+ /// The SCP client integration tests
+ ///
+ [TestClass]
+ public class ScpClientTests : IntegrationTestBase, IDisposable
+ {
+ private readonly ScpClient _scpClient;
+
+ public ScpClientTests()
+ {
+ _scpClient = new ScpClient(SshServerHostName, SshServerPort, User.UserName, User.Password);
+ _scpClient.Connect();
+ }
+
+ [TestMethod]
+
+ public void Upload_And_Download_FileStream()
+ {
+ var file = $"/tmp/{Guid.NewGuid()}.txt";
+ var fileContent = "File content !@#$%^&*()_+{}:,./<>[];'\\|";
+
+ using var uploadStream = new MemoryStream(Encoding.UTF8.GetBytes(fileContent));
+ _scpClient.Upload(uploadStream, file);
+
+ using var downloadStream = new MemoryStream();
+ _scpClient.Download(file, downloadStream);
+
+ var result = Encoding.UTF8.GetString(downloadStream.ToArray());
+
+ Assert.AreEqual(fileContent, result);
+ }
+
+ public void Dispose()
+ {
+ _scpClient.Disconnect();
+ _scpClient.Dispose();
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs
new file mode 100644
index 000000000..dfb406475
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs
@@ -0,0 +1,100 @@
+namespace IntegrationTests
+{
+ ///
+ /// The SFTP client integration tests
+ ///
+ [TestClass]
+ public class SftpClientTests : IntegrationTestBase, IDisposable
+ {
+ private readonly SftpClient _sftpClient;
+
+ public SftpClientTests()
+ {
+ _sftpClient = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password);
+ _sftpClient.Connect();
+ }
+
+ [TestMethod]
+ public void Create_directory_with_contents_and_list_it()
+ {
+ var testDirectory = "/home/sshnet/sshnet-test";
+ var testFileName = "test-file.txt";
+ var testFilePath = $"{testDirectory}/{testFileName}";
+ var testContent = "file content";
+
+ // Create new directory and check if it exists
+ _sftpClient.CreateDirectory(testDirectory);
+ Assert.IsTrue(_sftpClient.Exists(testDirectory));
+
+ // Upload file and check if it exists
+ using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
+ _sftpClient.UploadFile(fileStream, testFilePath);
+ Assert.IsTrue(_sftpClient.Exists(testFilePath));
+
+ // Check if ListDirectory works
+ var files = _sftpClient.ListDirectory(testDirectory);
+
+ _sftpClient.DeleteFile(testFilePath);
+ _sftpClient.DeleteDirectory(testDirectory);
+
+ var builder = new StringBuilder();
+ foreach (var file in files)
+ {
+ builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}");
+ }
+
+ Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True
+/home/sshnet/sshnet-test/.. False True
+/home/sshnet/sshnet-test/test-file.txt True False
+", builder.ToString());
+ }
+
+ [TestMethod]
+ public async Task Create_directory_with_contents_and_list_it_async()
+ {
+ var testDirectory = "/home/sshnet/sshnet-test";
+ var testFileName = "test-file.txt";
+ var testFilePath = $"{testDirectory}/{testFileName}";
+ var testContent = "file content";
+
+ // Create new directory and check if it exists
+ _sftpClient.CreateDirectory(testDirectory);
+ Assert.IsTrue(_sftpClient.Exists(testDirectory));
+
+ // Upload file and check if it exists
+ using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
+ _sftpClient.UploadFile(fileStream, testFilePath);
+ Assert.IsTrue(_sftpClient.Exists(testFilePath));
+
+ // Check if ListDirectory works
+ var files = await _sftpClient.ListDirectoryAsync(testDirectory, CancellationToken.None);
+
+ _sftpClient.DeleteFile(testFilePath);
+ _sftpClient.DeleteDirectory(testDirectory);
+
+ var builder = new StringBuilder();
+ foreach (var file in files)
+ {
+ builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}");
+ }
+
+ Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True
+/home/sshnet/sshnet-test/.. False True
+/home/sshnet/sshnet-test/test-file.txt True False
+", builder.ToString());
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(SftpPermissionDeniedException), "Permission denied")]
+ public void Test_Sftp_ListDirectory_Permission_Denied()
+ {
+ _sftpClient.ListDirectory("/root");
+ }
+
+ public void Dispose()
+ {
+ _sftpClient.Disconnect();
+ _sftpClient.Dispose();
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/SshClientTests.cs b/src/Renci.SshNet.IntegrationTests/SshClientTests.cs
new file mode 100644
index 000000000..33a90d13e
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/SshClientTests.cs
@@ -0,0 +1,32 @@
+namespace IntegrationTests
+{
+ ///
+ /// The SSH client integration tests
+ ///
+ [TestClass]
+ public class SshClientTests : IntegrationTestBase, IDisposable
+ {
+ private readonly SshClient _sshClient;
+
+ public SshClientTests()
+ {
+ _sshClient = new SshClient(SshServerHostName, SshServerPort, User.UserName, User.Password);
+ _sshClient.Connect();
+ }
+
+ [TestMethod]
+ public void Echo_Command_with_all_characters()
+ {
+ var builder = new StringBuilder();
+ var response = _sshClient.RunCommand("echo $'test !@#$%^&*()_+{}:,./<>[];\\|'");
+
+ Assert.AreEqual("test !@#$%^&*()_+{}:,./<>[];\\|\n", response.Result);
+ }
+
+ public void Dispose()
+ {
+ _sshClient.Disconnect();
+ _sshClient.Dispose();
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/TestInitializer.cs b/src/Renci.SshNet.IntegrationTests/TestInitializer.cs
new file mode 100644
index 000000000..16b0a3eca
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/TestInitializer.cs
@@ -0,0 +1,19 @@
+namespace IntegrationTests
+{
+ [TestClass]
+ public class TestInitializer
+ {
+ [AssemblyInitialize]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "MSTests requires context parameter")]
+ public static async Task Initialize(TestContext context)
+ {
+ await InfrastructureFixture.Instance.InitializeAsync();
+ }
+
+ [AssemblyCleanup]
+ public static async Task Cleanup()
+ {
+ await InfrastructureFixture.Instance.DisposeAsync();
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs
new file mode 100644
index 000000000..63e6c70ce
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs
@@ -0,0 +1,76 @@
+using DotNet.Testcontainers.Images;
+using DotNet.Testcontainers.Builders;
+using DotNet.Testcontainers.Containers;
+
+namespace IntegrationTests.TestsFixtures
+{
+ public sealed class InfrastructureFixture : IDisposable
+ {
+ private InfrastructureFixture()
+ {
+ }
+
+ private static readonly Lazy InstanceLazy = new Lazy(() => new InfrastructureFixture());
+
+ public static InfrastructureFixture Instance
+ {
+ get
+ {
+ return InstanceLazy.Value;
+ }
+ }
+
+ private IContainer? _sshServer;
+
+ private IFutureDockerImage? _sshServerImage;
+
+ public string? SshServerHostName { get; set; }
+
+ public ushort SshServerPort { get; set; }
+
+ public SshUser AdminUser = new SshUser("sshnetadm", "ssh4ever");
+
+ public SshUser User = new SshUser("sshnet", "ssh4ever");
+
+ public async Task InitializeAsync()
+ {
+ _sshServerImage = new ImageFromDockerfileBuilder()
+ .WithName("renci-ssh-tests-server-image")
+ .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), "Renci.SshNet.IntegrationTests")
+ .WithDockerfile("Dockerfile")
+ .WithDeleteIfExists(true)
+
+ .Build();
+
+ await _sshServerImage.CreateAsync();
+
+ _sshServer = new ContainerBuilder()
+ .WithHostname("renci-ssh-tests-server")
+ .WithImage(_sshServerImage)
+ .WithPortBinding(22, true)
+ .Build();
+
+ await _sshServer.StartAsync();
+
+ SshServerPort = _sshServer.GetMappedPublicPort(22);
+ SshServerHostName = _sshServer.Hostname;
+ }
+
+ public async Task DisposeAsync()
+ {
+ if (_sshServer != null)
+ {
+ await _sshServer.DisposeAsync();
+ }
+
+ if (_sshServerImage != null)
+ {
+ await _sshServerImage.DisposeAsync();
+ }
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs
new file mode 100644
index 000000000..521f947cc
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs
@@ -0,0 +1,66 @@
+namespace IntegrationTests.TestsFixtures
+{
+ ///
+ /// The base class for integration tests
+ ///
+ public abstract class IntegrationTestBase
+ {
+ private readonly InfrastructureFixture _infrastructureFixture;
+
+ ///
+ /// The SSH Server host name.
+ ///
+ public string? SshServerHostName
+ {
+ get
+ {
+ return _infrastructureFixture.SshServerHostName;
+ }
+ }
+
+ ///
+ /// The SSH Server host name
+ ///
+ public ushort SshServerPort
+ {
+ get
+ {
+ return _infrastructureFixture.SshServerPort;
+ }
+ }
+
+ ///
+ /// The admin user that can use SSH Server.
+ ///
+ public SshUser AdminUser
+ {
+ get
+ {
+ return _infrastructureFixture.AdminUser;
+ }
+ }
+
+ ///
+ /// The normal user that can use SSH Server.
+ ///
+ public SshUser User
+ {
+ get
+ {
+ return _infrastructureFixture.User;
+ }
+ }
+
+ protected IntegrationTestBase()
+ {
+ _infrastructureFixture = InfrastructureFixture.Instance;
+ ShowInfrastructureInformation();
+ }
+
+ private void ShowInfrastructureInformation()
+ {
+ Console.WriteLine($"SSH Server host name: {_infrastructureFixture.SshServerHostName}");
+ Console.WriteLine($"SSH Server port: {_infrastructureFixture.SshServerPort}");
+ }
+ }
+}
diff --git a/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs b/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs
new file mode 100644
index 000000000..5f2ee4d56
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/TestsFixtures/SshUser.cs
@@ -0,0 +1,16 @@
+namespace IntegrationTests.TestsFixtures
+{
+ public class SshUser
+ {
+ public string UserName { get; }
+
+ public string Password { get; }
+
+ public SshUser(string userName, string password)
+ {
+ UserName = userName;
+ Password = password;
+ }
+ }
+}
+
diff --git a/src/Renci.SshNet.IntegrationTests/Usings.cs b/src/Renci.SshNet.IntegrationTests/Usings.cs
new file mode 100644
index 000000000..bc21218d4
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/Usings.cs
@@ -0,0 +1,36 @@
+#pragma warning disable IDE0005
+
+extern alias LocalSshNet;
+
+global using System.Text;
+
+global using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+global using IntegrationTests.TestsFixtures;
+
+// The testcontainers library uses SSH.NET, so we have two versions of SSH.NET in the project.
+// We need to explicitly choose which version we want to test.
+// To avoid problems, we import all namespaces.
+global using LocalSshNet::Renci.SshNet;
+global using LocalSshNet::Renci.SshNet.Abstractions;
+global using LocalSshNet::Renci.SshNet.Channels;
+global using LocalSshNet::Renci.SshNet.Common;
+global using LocalSshNet::Renci.SshNet.Compression;
+global using LocalSshNet::Renci.SshNet.Connection;
+global using LocalSshNet::Renci.SshNet.Messages;
+global using LocalSshNet::Renci.SshNet.Messages.Authentication;
+global using LocalSshNet::Renci.SshNet.Messages.Connection;
+global using LocalSshNet::Renci.SshNet.Messages.Transport;
+global using LocalSshNet::Renci.SshNet.NetConf;
+global using LocalSshNet::Renci.SshNet.Security;
+global using LocalSshNet::Renci.SshNet.Security.Chaos;
+global using LocalSshNet::Renci.SshNet.Security.Chaos.NaCl;
+global using LocalSshNet::Renci.SshNet.Security.Chaos.NaCl.Internal;
+global using LocalSshNet::Renci.SshNet.Security.Cryptography;
+global using LocalSshNet::Renci.SshNet.Security.Cryptography.Ciphers;
+global using LocalSshNet::Renci.SshNet.Security.Org;
+global using LocalSshNet::Renci.SshNet.Security.Org.BouncyCastle;
+
+global using LocalSshNet::Renci.SshNet.Sftp;
+
+
diff --git a/src/Renci.SshNet.IntegrationTests/app.config b/src/Renci.SshNet.IntegrationTests/app.config
new file mode 100644
index 000000000..3571058ce
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Renci.SshNet.IntegrationTests/server/script/start.sh b/src/Renci.SshNet.IntegrationTests/server/script/start.sh
new file mode 100644
index 000000000..e7e9f758c
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/server/script/start.sh
@@ -0,0 +1,10 @@
+#!/bin/ash
+/usr/sbin/syslog-ng
+
+# allow us to make changes to /etc/hosts; we need this for the port forwarding tests
+chmod 777 /etc/hosts
+
+# start PAM-enabled ssh daemon as we also want keyboard-interactive authentication to work
+/usr/sbin/sshd.pam
+
+tail -f < /var/log/auth.log
diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key
new file mode 100644
index 000000000..5739844cf
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ecdsa_key
@@ -0,0 +1,9 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
+1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTtDoci0CaEgyR+p+ersiYltKUSqZx/
+MffWpnEPfGgnFI81huQw0D9e/SqABbeHtrzcSWskSZc0f2jjFxyqVkliAAAAsDrEln06xJ
+Z9AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBO0OhyLQJoSDJH6n
+56uyJiW0pRKpnH8x99amcQ98aCcUjzWG5DDQP179KoAFt4e2vNxJayRJlzR/aOMXHKpWSW
+IAAAAgYdRMomjDSquRMSYTvEIzX7cReJ2grVIWsxIOLyhJnw0AAAAWcm9vdEBVYnVudHUx
+OTEwRGVza3RvcAEC
+-----END OPENSSH PRIVATE KEY-----
diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key
new file mode 100644
index 000000000..8e45178e6
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_ed25519_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCCMW3VnuKfj6AxBQ7gJ4qAfEgw/YJl9q3AXyelCw+OdwAAAKDKcLC0ynCw
+tAAAAAtzc2gtZWQyNTUxOQAAACCCMW3VnuKfj6AxBQ7gJ4qAfEgw/YJl9q3AXyelCw+Odw
+AAAED9TRnDkG0tzdZv5oPJCXwzqrkxut7y33A8Wi8AzusJL4IxbdWe4p+PoDEFDuAnioB8
+SDD9gmX2rcBfJ6ULD453AAAAFnJvb3RAVWJ1bnR1MTkxMERlc2t0b3ABAgMEBQYH
+-----END OPENSSH PRIVATE KEY-----
diff --git a/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key
new file mode 100644
index 000000000..edb94f341
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/server/ssh/ssh_host_rsa_key
@@ -0,0 +1,38 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+-----END OPENSSH PRIVATE KEY-----
diff --git a/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys b/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys
new file mode 100644
index 000000000..484a0bdcc
--- /dev/null
+++ b/src/Renci.SshNet.IntegrationTests/user/sshnet/authorized_keys
@@ -0,0 +1,4 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4N0T6VopnwPSYQUw0waQNhBjz0DYFQwvkv4OwWYSf//fJF3M6bH42Tn2J+IlQ+4/fCFnE3m99seV5T1myEj7fsupNteY2sKFGXENLGtAD/76FM0iBmXx76xlSTyZSSmNDIRU4xUR23cfc+al84F5mO2lEk+5Zr3Qn5JUpucBfis4vtu0sMDgZ4w1d0tcuXkT/MEJn2iX2cnxbSy5qNAPHu7b+LEfXBv2OrMDqPrx/X6QREgi3w5RxL5kz7bvitWsIwIvb3ST2ARAArBwb8pEyp2A/w5p22rkQtL+3ibZ8fkmpgn33f31AZPgtM++iJPBmPKFjArcWEJ9fIVB/6DAj
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPzzrPpItEjNG7tU0DpJJ4pkI01E9d6K61OKTVPdFQSyGCdMj9XdP93lC6sJA+9/ahvf5F3gWEKxUJL2CKUiFWw=
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ==
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ==
\ No newline at end of file
diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
index 88693e5aa..94a4e8015 100644
--- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
+++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
@@ -64,9 +64,9 @@
-
-
-
+
+
+
diff --git a/src/Renci.SshNet.sln b/src/Renci.SshNet.sln
index 09953551d..be2686bee 100644
--- a/src/Renci.SshNet.sln
+++ b/src/Renci.SshNet.sln
@@ -42,6 +42,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D21A4D03-0
..\test\Directory.Build.props = ..\test\Directory.Build.props
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "Renci.SshNet.IntegrationTests\IntegrationTests.csproj", "{EEF98046-729C-419E-932D-4E569073C8CC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -84,6 +86,26 @@ Global
{C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x64.ActiveCfg = Release|Any CPU
{C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x86.ActiveCfg = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|ARM.Build.0 = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x64.Build.0 = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|x86.Build.0 = Debug|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|ARM.ActiveCfg = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|ARM.Build.0 = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x64.ActiveCfg = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x64.Build.0 = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x86.ActiveCfg = Release|Any CPU
+ {EEF98046-729C-419E-932D-4E569073C8CC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs
index 0425d1fba..d99338feb 100644
--- a/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs
+++ b/src/Renci.SshNet/Properties/CommonAssemblyInfo.cs
@@ -5,13 +5,13 @@
[assembly: AssemblyDescription("SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.")]
[assembly: AssemblyCompany("Renci")]
[assembly: AssemblyProduct("SSH.NET")]
-[assembly: AssemblyCopyright("Copyright � Renci 2010-2017")]
+[assembly: AssemblyCopyright("Copyright � Renci 2010-2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("2017.0.0")]
-[assembly: AssemblyFileVersion("2017.0.0")]
-[assembly: AssemblyInformationalVersion("2017.0.0-beta1")]
+[assembly: AssemblyVersion("2023.0.0")]
+[assembly: AssemblyFileVersion("2023.0.0")]
+[assembly: AssemblyInformationalVersion("2023.0.0")]
[assembly: CLSCompliant(false)]
// Setting ComVisible to false makes the types in this assembly not visible
@@ -24,4 +24,4 @@
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
-#endif
\ No newline at end of file
+#endif