Ver Fonte

Miscellaneous cleanup/throw helpers (#1491)

* PrivateKeyFile stuff

* Extract from net7 branch

* more ThrowHelper stuff
Rob Hague há 1 ano atrás
pai
commit
aac10fba3d
94 ficheiros alterados com 521 adições e 1443 exclusões
  1. 1 1
      Directory.Build.props
  2. 1 0
      Directory.Packages.props
  3. 3 0
      src/Renci.SshNet/.editorconfig
  4. 2 2
      src/Renci.SshNet/Abstractions/SocketAbstraction.cs
  5. 4 8
      src/Renci.SshNet/Abstractions/ThreadAbstraction.cs
  6. 3 4
      src/Renci.SshNet/AuthenticationMethod.cs
  7. 4 18
      src/Renci.SshNet/BaseClient.cs
  8. 2 9
      src/Renci.SshNet/ClientAuthentication.cs
  9. 1 4
      src/Renci.SshNet/Common/ChannelDataEventArgs.cs
  10. 2 13
      src/Renci.SshNet/Common/ChannelInputStream.cs
  11. 1 4
      src/Renci.SshNet/Common/ChannelRequestEventArgs.cs
  12. 8 23
      src/Renci.SshNet/Common/Extensions.cs
  13. 2 13
      src/Renci.SshNet/Common/HostKeyEventArgs.cs
  14. 1 4
      src/Renci.SshNet/Common/PacketDump.cs
  15. 1 13
      src/Renci.SshNet/Common/PipeStream.cs
  16. 2 6
      src/Renci.SshNet/Common/PortForwardEventArgs.cs
  17. 3 12
      src/Renci.SshNet/Common/PosixPath.cs
  18. 2 8
      src/Renci.SshNet/Common/SshData.cs
  19. 4 12
      src/Renci.SshNet/Common/SshDataStream.cs
  20. 83 0
      src/Renci.SshNet/Common/ThrowHelper.cs
  21. 9 14
      src/Renci.SshNet/Common/TimeSpanExtensions.cs
  22. 1 4
      src/Renci.SshNet/Connection/ConnectorBase.cs
  23. 4 9
      src/Renci.SshNet/Connection/SshIdentification.cs
  24. 7 30
      src/Renci.SshNet/ConnectionInfo.cs
  25. 6 18
      src/Renci.SshNet/ExpectAction.cs
  26. 1 1
      src/Renci.SshNet/ForwardedPort.cs
  27. 2 9
      src/Renci.SshNet/ForwardedPortDynamic.cs
  28. 6 20
      src/Renci.SshNet/ForwardedPortLocal.cs
  29. 6 20
      src/Renci.SshNet/ForwardedPortRemote.cs
  30. 3 4
      src/Renci.SshNet/MessageEventArgs`1.cs
  31. 3 9
      src/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs
  32. 3 4
      src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs
  33. 4 9
      src/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs
  34. 3 4
      src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs
  35. 2 4
      src/Renci.SshNet/NoneAuthenticationMethod.cs
  36. 2 8
      src/Renci.SshNet/PasswordAuthenticationMethod.cs
  37. 1 4
      src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
  38. 38 48
      src/Renci.SshNet/PrivateKeyFile.cs
  39. 3 4
      src/Renci.SshNet/RemotePathDoubleQuoteTransformation.cs
  40. 3 4
      src/Renci.SshNet/RemotePathNoneTransformation.cs
  41. 3 4
      src/Renci.SshNet/RemotePathShellQuoteTransformation.cs
  42. 3 0
      src/Renci.SshNet/Renci.SshNet.csproj
  43. 10 50
      src/Renci.SshNet/ScpClient.cs
  44. 1 4
      src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs
  45. 1 4
      src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs
  46. 2 8
      src/Renci.SshNet/Security/Cryptography/DsaKey.cs
  47. 1 4
      src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs
  48. 1 4
      src/Renci.SshNet/Security/Cryptography/ED25519Key.cs
  49. 1 4
      src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs
  50. 1 4
      src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs
  51. 2 8
      src/Renci.SshNet/Security/Cryptography/RsaKey.cs
  52. 3 4
      src/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs
  53. 4 18
      src/Renci.SshNet/ServiceFactory.cs
  54. 5 22
      src/Renci.SshNet/Session.cs
  55. 2 4
      src/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs
  56. 3 13
      src/Renci.SshNet/Sftp/SftpFile.cs
  57. 1 8
      src/Renci.SshNet/Sftp/SftpFileReader.cs
  58. 7 32
      src/Renci.SshNet/Sftp/SftpFileStream.cs
  59. 6 24
      src/Renci.SshNet/Sftp/SftpSession.cs
  60. 34 165
      src/Renci.SshNet/SftpClient.cs
  61. 2 14
      src/Renci.SshNet/ShellStream.cs
  62. 2 8
      src/Renci.SshNet/SshClient.cs
  63. 5 26
      src/Renci.SshNet/SshCommand.cs
  64. 2 8
      src/Renci.SshNet/SshMessageFactory.cs
  65. 5 24
      src/Renci.SshNet/SubsystemSession.cs
  66. 9 0
      test/Data/Key.OPENSSH.ECDSA.Encrypted.Aes.128.CTR.txt
  67. 1 0
      test/Data/Key.OPENSSH.ECDSA.Encrypted.Aes.128.CTR.txt.pub
  68. 11 0
      test/Data/Key.OPENSSH.ECDSA384.Encrypted.Aes.256.GCM.txt
  69. 1 0
      test/Data/Key.OPENSSH.ECDSA384.Encrypted.Aes.256.GCM.txt.pub
  70. 12 0
      test/Data/Key.OPENSSH.ECDSA521.Encrypted.Aes.192.CBC.txt
  71. 1 0
      test/Data/Key.OPENSSH.ECDSA521.Encrypted.Aes.192.CBC.txt.pub
  72. 0 1
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.128.CTR.pub
  73. 0 8
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.128.CTR.txt
  74. 0 1
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CBC.pub
  75. 0 8
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CBC.txt
  76. 0 1
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CTR.pub
  77. 0 8
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CTR.txt
  78. 0 1
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.256.GCM.pub
  79. 0 8
      test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.256.GCM.txt
  80. 39 0
      test/Data/Key.OPENSSH.RSA.Encrypted.Aes.192.CTR.txt
  81. 1 0
      test/Data/Key.OPENSSH.RSA.Encrypted.Aes.192.CTR.txt.pub
  82. 2 8
      test/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs
  83. 1 1
      test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs
  84. 1 1
      test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs
  85. 3 2
      test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs
  86. 12 14
      test/Renci.SshNet.Tests/Classes/Common/TimeSpanExtensionsTest.cs
  87. 1 7
      test/Renci.SshNet.Tests/Classes/KeyboardInteractiveAuthenticationMethodTest.cs
  88. 1 7
      test/Renci.SshNet.Tests/Classes/NoneAuthenticationMethodTest.cs
  89. 1 16
      test/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs
  90. 6 13
      test/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs
  91. 1 13
      test/Renci.SshNet.Tests/Classes/PrivateKeyAuthenticationMethodTest.cs
  92. 71 474
      test/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
  93. 1 3
      test/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFile.cs
  94. 1 3
      test/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs

+ 1 - 1
Directory.Build.props

@@ -32,7 +32,7 @@
         Disable nullable warnings on old frameworks because of missing annotations.
   -->
   <PropertyGroup Condition=" !$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0')) ">
-    <NoWarn>$(NoWarn);CS8602</NoWarn>
+    <NoWarn>$(NoWarn);CS8602;CS8604;CS8777</NoWarn>
   </PropertyGroup>
 
   <!--

+ 1 - 0
Directory.Packages.props

@@ -18,6 +18,7 @@
     <PackageVersion Include="MSTest.TestFramework" Version="3.6.0" />
     <PackageVersion Include="Moq" Version="4.20.72" />
     <PackageVersion Include="Nerdbank.GitVersioning" Version="3.7.70-alpha" />
+    <PackageVersion Include="PolySharp" Version="1.14.1" />
     <PackageVersion Include="SonarAnalyzer.CSharp" Version="9.19.0.84025" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Formats.Asn1" Version="8.0.1" />

+ 3 - 0
src/Renci.SshNet/.editorconfig

@@ -188,3 +188,6 @@ dotnet_diagnostic.MA0040.severity = none
 # MA0042: Do not use blocking calls in an async method
 # duplicate of CA1849
 dotnet_diagnostic.MA0042.severity = none
+
+# S3236: Caller information arguments should not be provided explicitly
+dotnet_diagnostic.S3236.severity = none

+ 2 - 2
src/Renci.SshNet/Abstractions/SocketAbstraction.cs

@@ -129,7 +129,7 @@ namespace Renci.SshNet.Abstractions
 
         public static int ReadPartial(Socket socket, byte[] buffer, int offset, int size, TimeSpan timeout)
         {
-            socket.ReceiveTimeout = timeout.AsTimeout(nameof(timeout));
+            socket.ReceiveTimeout = timeout.AsTimeout();
 
             try
             {
@@ -274,7 +274,7 @@ namespace Renci.SshNet.Abstractions
             var totalBytesRead = 0;
             var totalBytesToRead = size;
 
-            socket.ReceiveTimeout = readTimeout.AsTimeout(nameof(readTimeout));
+            socket.ReceiveTimeout = readTimeout.AsTimeout();
 
             do
             {

+ 4 - 8
src/Renci.SshNet/Abstractions/ThreadAbstraction.cs

@@ -2,6 +2,8 @@
 using System.Threading;
 using System.Threading.Tasks;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet.Abstractions
 {
     internal static class ThreadAbstraction
@@ -16,10 +18,7 @@ namespace Renci.SshNet.Abstractions
         /// </returns>
         public static Task ExecuteThreadLongRunning(Action action)
         {
-            if (action is null)
-            {
-                throw new ArgumentNullException(nameof(action));
-            }
+            ThrowHelper.ThrowIfNull(action);
 
             return Task.Factory.StartNew(action,
                                          CancellationToken.None,
@@ -33,10 +32,7 @@ namespace Renci.SshNet.Abstractions
         /// <param name="action">The action to execute.</param>
         public static void ExecuteThread(Action action)
         {
-            if (action is null)
-            {
-                throw new ArgumentNullException(nameof(action));
-            }
+            ThrowHelper.ThrowIfNull(action);
 
             _ = ThreadPool.QueueUserWorkItem(o => action());
         }

+ 3 - 4
src/Renci.SshNet/AuthenticationMethod.cs

@@ -1,5 +1,7 @@
 using System;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet
 {
     /// <summary>
@@ -34,10 +36,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentException"><paramref name="username"/> is whitespace or <see langword="null"/>.</exception>
         protected AuthenticationMethod(string username)
         {
-            if (string.IsNullOrWhiteSpace(username))
-            {
-                throw new ArgumentException("username");
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(username);
 
             Username = username;
         }

+ 4 - 18
src/Renci.SshNet/BaseClient.cs

@@ -184,15 +184,8 @@ namespace Renci.SshNet
         /// </remarks>
         private protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
         {
-            if (connectionInfo is null)
-            {
-                throw new ArgumentNullException(nameof(connectionInfo));
-            }
-
-            if (serviceFactory is null)
-            {
-                throw new ArgumentNullException(nameof(serviceFactory));
-            }
+            ThrowHelper.ThrowIfNull(connectionInfo);
+            ThrowHelper.ThrowIfNull(serviceFactory);
 
             _connectionInfo = connectionInfo;
             _ownsConnectionInfo = ownsConnectionInfo;
@@ -458,17 +451,10 @@ namespace Renci.SshNet
         /// <summary>
         /// Check if the current instance is disposed.
         /// </summary>
-        /// <exception cref="ObjectDisposedException">THe current instance is disposed.</exception>
+        /// <exception cref="ObjectDisposedException">The current instance is disposed.</exception>
         protected void CheckDisposed()
         {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_isDisposed, this);
-#else
-            if (_isDisposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
         }
 
         /// <summary>

+ 2 - 9
src/Renci.SshNet/ClientAuthentication.cs

@@ -52,15 +52,8 @@ namespace Renci.SshNet
         /// <exception cref="SshAuthenticationException">Failed to authenticate the client.</exception>
         public void Authenticate(IConnectionInfoInternal connectionInfo, ISession session)
         {
-            if (connectionInfo is null)
-            {
-                throw new ArgumentNullException(nameof(connectionInfo));
-            }
-
-            if (session is null)
-            {
-                throw new ArgumentNullException(nameof(session));
-            }
+            ThrowHelper.ThrowIfNull(connectionInfo);
+            ThrowHelper.ThrowIfNull(session);
 
             session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE");
             session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS");

+ 1 - 4
src/Renci.SshNet/Common/ChannelDataEventArgs.cs

@@ -16,10 +16,7 @@ namespace Renci.SshNet.Common
         public ChannelDataEventArgs(uint channelNumber, byte[] data)
             : base(channelNumber)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             Data = data;
         }

+ 2 - 13
src/Renci.SshNet/Common/ChannelInputStream.cs

@@ -101,10 +101,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentOutOfRangeException">offset or count is negative.</exception>
         public override void Write(byte[] buffer, int offset, int count)
         {
-            if (buffer == null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
+            ThrowHelper.ThrowIfNull(buffer);
 
             if (offset + count > buffer.Length)
             {
@@ -116,10 +113,7 @@ namespace Renci.SshNet.Common
                 throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative.");
             }
 
-            if (_isDisposed)
-            {
-                throw CreateObjectDisposedException();
-            }
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
 
             if (count == 0)
             {
@@ -208,10 +202,5 @@ namespace Renci.SshNet.Common
             get { return _totalPosition; }
             set { throw new NotSupportedException(); }
         }
-
-        private ObjectDisposedException CreateObjectDisposedException()
-        {
-            return new ObjectDisposedException(GetType().FullName);
-        }
     }
 }

+ 1 - 4
src/Renci.SshNet/Common/ChannelRequestEventArgs.cs

@@ -16,10 +16,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="info"/> is <see langword="null"/>.</exception>
         public ChannelRequestEventArgs(RequestInfo info)
         {
-            if (info is null)
-            {
-                throw new ArgumentNullException(nameof(info));
-            }
+            ThrowHelper.ThrowIfNull(info);
 
             Info = info;
         }

+ 8 - 23
src/Renci.SshNet/Common/Extensions.cs

@@ -5,6 +5,7 @@ using System.Globalization;
 using System.Net;
 using System.Net.Sockets;
 using System.Numerics;
+using System.Runtime.CompilerServices;
 using System.Text;
 
 using Renci.SshNet.Abstractions;
@@ -148,7 +149,7 @@ namespace Renci.SshNet.Common
             Debug.WriteLine(sb.ToString());
         }
 
-        internal static void ValidatePort(this uint value, string argument)
+        internal static void ValidatePort(this uint value, [CallerArgumentExpression(nameof(value))] string argument = null)
         {
             if (value > IPEndPoint.MaxPort)
             {
@@ -157,7 +158,7 @@ namespace Renci.SshNet.Common
             }
         }
 
-        internal static void ValidatePort(this int value, string argument)
+        internal static void ValidatePort(this int value, [CallerArgumentExpression(nameof(value))] string argument = null)
         {
             if (value < IPEndPoint.MinPort)
             {
@@ -187,10 +188,7 @@ namespace Renci.SshNet.Common
         /// </remarks>
         public static byte[] Take(this byte[] value, int offset, int count)
         {
-            if (value is null)
-            {
-                throw new ArgumentNullException(nameof(value));
-            }
+            ThrowHelper.ThrowIfNull(value);
 
             if (count == 0)
             {
@@ -222,10 +220,7 @@ namespace Renci.SshNet.Common
         /// </remarks>
         public static byte[] Take(this byte[] value, int count)
         {
-            if (value is null)
-            {
-                throw new ArgumentNullException(nameof(value));
-            }
+            ThrowHelper.ThrowIfNull(value);
 
             if (count == 0)
             {
@@ -244,15 +239,8 @@ namespace Renci.SshNet.Common
 
         public static bool IsEqualTo(this byte[] left, byte[] right)
         {
-            if (left is null)
-            {
-                throw new ArgumentNullException(nameof(left));
-            }
-
-            if (right is null)
-            {
-                throw new ArgumentNullException(nameof(right));
-            }
+            ThrowHelper.ThrowIfNull(left);
+            ThrowHelper.ThrowIfNull(right);
 
             return left.AsSpan().SequenceEqual(right);
         }
@@ -266,10 +254,7 @@ namespace Renci.SshNet.Common
         /// </returns>
         public static byte[] TrimLeadingZeros(this byte[] value)
         {
-            if (value is null)
-            {
-                throw new ArgumentNullException(nameof(value));
-            }
+            ThrowHelper.ThrowIfNull(value);
 
             for (var i = 0; i < value.Length; i++)
             {

+ 2 - 13
src/Renci.SshNet/Common/HostKeyEventArgs.cs

@@ -90,10 +90,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="host"/> is <see langword="null"/>.</exception>
         public HostKeyEventArgs(KeyHostAlgorithm host)
         {
-            if (host is null)
-            {
-                throw new ArgumentNullException(nameof(host));
-            }
+            ThrowHelper.ThrowIfNull(host);
 
             CanTrust = true;
             HostKey = host.Data;
@@ -102,15 +99,7 @@ namespace Renci.SshNet.Common
 
             _lazyFingerPrint = new Lazy<byte[]>(() => CryptoAbstraction.HashMD5(HostKey));
 
-            _lazyFingerPrintSHA256 = new Lazy<string>(() =>
-                {
-                    return Convert.ToBase64String(CryptoAbstraction.HashSHA256(HostKey))
-#if NET || NETSTANDARD2_1_OR_GREATER
-                                  .Replace("=", string.Empty, StringComparison.Ordinal);
-#else
-                                  .Replace("=", string.Empty);
-#endif // NET || NETSTANDARD2_1_OR_GREATER
-                });
+            _lazyFingerPrintSHA256 = new Lazy<string>(() => Convert.ToBase64String(CryptoAbstraction.HashSHA256(HostKey)).TrimEnd('='));
 
             _lazyFingerPrintMD5 = new Lazy<string>(() =>
                 {

+ 1 - 4
src/Renci.SshNet/Common/PacketDump.cs

@@ -14,10 +14,7 @@ namespace Renci.SshNet.Common
 
         public static string Create(byte[] data, int indentLevel)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             if (indentLevel < 0)
             {

+ 1 - 13
src/Renci.SshNet/Common/PipeStream.cs

@@ -90,7 +90,7 @@ namespace Renci.SshNet.Common
         {
             lock (_sync)
             {
-                ThrowIfDisposed();
+                ThrowHelper.ThrowObjectDisposedIf(_disposed, this);
 
                 AssertValid();
 
@@ -232,17 +232,5 @@ namespace Renci.SshNet.Common
             get { return 0; }
             set { throw new NotSupportedException(); }
         }
-
-        private void ThrowIfDisposed()
-        {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_disposed, this);
-#else
-            if (_disposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
-        }
     }
 }

+ 2 - 6
src/Renci.SshNet/Common/PortForwardEventArgs.cs

@@ -17,12 +17,8 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port" /> is not within <see cref="IPEndPoint.MinPort" /> and <see cref="IPEndPoint.MaxPort" />.</exception>
         internal PortForwardEventArgs(string host, uint port)
         {
-            if (host is null)
-            {
-                throw new ArgumentNullException(nameof(host));
-            }
-
-            port.ValidatePort("port");
+            ThrowHelper.ThrowIfNull(host);
+            port.ValidatePort();
 
             OriginatorHost = host;
             OriginatorPort = port;

+ 3 - 12
src/Renci.SshNet/Common/PosixPath.cs

@@ -38,10 +38,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentException"><paramref name="path"/> is empty ("").</exception>
         public static PosixPath CreateAbsoluteOrRelativeFilePath(string path)
         {
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             var posixPath = new PosixPath();
 
@@ -95,10 +92,7 @@ namespace Renci.SshNet.Common
         /// </remarks>
         public static string GetFileName(string path)
         {
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             var pathEnd = path.LastIndexOf('/');
             if (pathEnd == -1)
@@ -125,10 +119,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
         public static string GetDirectoryName(string path)
         {
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             var pathEnd = path.LastIndexOf('/');
             if (pathEnd == -1)

+ 2 - 8
src/Renci.SshNet/Common/SshData.cs

@@ -91,10 +91,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="data"/> is <see langword="null"/>.</exception>
         public void Load(byte[] data)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             LoadInternal(data, 0, data.Length);
         }
@@ -108,10 +105,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="data"/> is <see langword="null"/>.</exception>
         public void Load(byte[] data, int offset, int count)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             LoadInternal(data, offset, count);
         }

+ 4 - 12
src/Renci.SshNet/Common/SshDataStream.cs

@@ -123,10 +123,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="data"/> is <see langword="null"/>.</exception>
         public void Write(byte[] data)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             Write(data, 0, data.Length);
         }
@@ -140,10 +137,8 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="encoding"/> is <see langword="null"/>.</exception>
         public void Write(string s, Encoding encoding)
         {
-            if (encoding is null)
-            {
-                throw new ArgumentNullException(nameof(encoding));
-            }
+            ThrowHelper.ThrowIfNull(encoding);
+
 #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
             ReadOnlySpan<char> value = s;
             var count = encoding.GetByteCount(value);
@@ -182,10 +177,7 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
         public void WriteBinary(byte[] buffer)
         {
-            if (buffer is null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
+            ThrowHelper.ThrowIfNull(buffer);
 
             WriteBinary(buffer, 0, buffer.Length);
         }

+ 83 - 0
src/Renci.SshNet/Common/ThrowHelper.cs

@@ -0,0 +1,83 @@
+#nullable enable
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+namespace Renci.SshNet.Common
+{
+    internal static class ThrowHelper
+    {
+        public static void ThrowObjectDisposedIf(bool condition, object instance)
+        {
+#if NET7_0_OR_GREATER
+            ObjectDisposedException.ThrowIf(condition, instance);
+#else
+            if (condition)
+            {
+                Throw(instance);
+
+                static void Throw(object? instance)
+                {
+                    throw new ObjectDisposedException(instance?.GetType().FullName);
+                }
+            }
+#endif
+        }
+
+        public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
+        {
+#if NET6_0_OR_GREATER
+            ArgumentNullException.ThrowIfNull(argument, paramName);
+#else
+            if (argument is null)
+            {
+                Throw(paramName);
+
+                [DoesNotReturn]
+                static void Throw(string? paramName)
+                {
+                    throw new ArgumentNullException(paramName);
+                }
+            }
+#endif
+        }
+
+        public static void ThrowIfNullOrWhiteSpace([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
+        {
+#if NET8_0_OR_GREATER
+            ArgumentException.ThrowIfNullOrWhiteSpace(argument, paramName);
+#else
+            if (string.IsNullOrWhiteSpace(argument))
+            {
+                Throw(argument, paramName);
+
+                [DoesNotReturn]
+                static void Throw(string? argument, string? paramName)
+                {
+                    ThrowIfNull(argument, paramName);
+                    throw new ArgumentException("The value cannot be an empty string or composed entirely of whitespace.", paramName);
+                }
+            }
+#endif
+        }
+
+        public static void ThrowIfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
+        {
+#if NET7_0_OR_GREATER
+            ArgumentException.ThrowIfNullOrEmpty(argument, paramName);
+#else
+            if (string.IsNullOrEmpty(argument))
+            {
+                Throw(argument, paramName);
+
+                [DoesNotReturn]
+                static void Throw(string? argument, string? paramName)
+                {
+                    ThrowIfNull(argument, paramName);
+                    throw new ArgumentException("The value cannot be an empty string.", paramName);
+                }
+            }
+#endif
+        }
+    }
+}

+ 9 - 14
src/Renci.SshNet/Common/TimeSpanExtensions.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+using System;
+using System.Runtime.CompilerServices;
 
 namespace Renci.SshNet.Common
 {
@@ -7,22 +9,19 @@ namespace Renci.SshNet.Common
     /// </summary>
     internal static class TimeSpanExtensions
     {
-        private const string OutOfRangeTimeoutMessage =
-            $"The timeout must represent a value between -1 and Int32.MaxValue milliseconds, inclusive.";
-
         /// <summary>
         /// Returns the specified <paramref name="timeSpan"/> as a valid timeout in milliseconds.
         /// </summary>
         /// <param name="timeSpan">The <see cref="TimeSpan"/> to ensure validity.</param>
-        /// <param name="callerMemberName">The name of the calling member.</param>
+        /// <param name="paramName">The name of the calling member.</param>
         /// <exception cref="ArgumentOutOfRangeException">
         /// Thrown when <paramref name="timeSpan"/> does not represent a value between -1 and <see cref="int.MaxValue"/>, inclusive.
         /// </exception>
-        public static int AsTimeout(this TimeSpan timeSpan, string callerMemberName)
+        public static int AsTimeout(this TimeSpan timeSpan, [CallerArgumentExpression(nameof(timeSpan))] string? paramName = null)
         {
             var timeoutInMilliseconds = timeSpan.TotalMilliseconds;
             return timeoutInMilliseconds is < -1d or > int.MaxValue
-                       ? throw new ArgumentOutOfRangeException(callerMemberName, OutOfRangeTimeoutMessage)
+                       ? throw new ArgumentOutOfRangeException(paramName, "The timeout must represent a value between -1 and Int32.MaxValue milliseconds, inclusive.")
                        : (int)timeoutInMilliseconds;
         }
 
@@ -30,17 +29,13 @@ namespace Renci.SshNet.Common
         /// Ensures that the specified <paramref name="timeSpan"/> represents a valid timeout in milliseconds.
         /// </summary>
         /// <param name="timeSpan">The <see cref="TimeSpan"/> to ensure validity.</param>
-        /// <param name="callerMemberName">The name of the calling member.</param>
+        /// <param name="paramName">The name of the calling member.</param>
         /// <exception cref="ArgumentOutOfRangeException">
         /// Thrown when <paramref name="timeSpan"/> does not represent a value between -1 and <see cref="int.MaxValue"/>, inclusive.
         /// </exception>
-        public static void EnsureValidTimeout(this TimeSpan timeSpan, string callerMemberName)
+        public static void EnsureValidTimeout(this TimeSpan timeSpan, [CallerArgumentExpression(nameof(timeSpan))] string? paramName = null)
         {
-            var timeoutInMilliseconds = timeSpan.TotalMilliseconds;
-            if (timeoutInMilliseconds is < -1d or > int.MaxValue)
-            {
-                throw new ArgumentOutOfRangeException(callerMemberName, OutOfRangeTimeoutMessage);
-            }
+            _ = timeSpan.AsTimeout(paramName);
         }
     }
 }

+ 1 - 4
src/Renci.SshNet/Connection/ConnectorBase.cs

@@ -14,10 +14,7 @@ namespace Renci.SshNet.Connection
     {
         protected ConnectorBase(ISocketFactory socketFactory)
         {
-            if (socketFactory is null)
-            {
-                throw new ArgumentNullException(nameof(socketFactory));
-            }
+            ThrowHelper.ThrowIfNull(socketFactory);
 
             SocketFactory = socketFactory;
         }

+ 4 - 9
src/Renci.SshNet/Connection/SshIdentification.cs

@@ -1,5 +1,7 @@
 using System;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet.Connection
 {
     /// <summary>
@@ -31,15 +33,8 @@ namespace Renci.SshNet.Connection
         /// <exception cref="ArgumentNullException"><paramref name="softwareVersion"/> is <see langword="null"/>.</exception>
         public SshIdentification(string protocolVersion, string softwareVersion, string comments)
         {
-            if (protocolVersion is null)
-            {
-                throw new ArgumentNullException(nameof(protocolVersion));
-            }
-
-            if (softwareVersion is null)
-            {
-                throw new ArgumentNullException(nameof(softwareVersion));
-            }
+            ThrowHelper.ThrowIfNull(protocolVersion);
+            ThrowHelper.ThrowIfNull(softwareVersion);
 
             ProtocolVersion = protocolVersion;
             SoftwareVersion = softwareVersion;

+ 7 - 30
src/Renci.SshNet/ConnectionInfo.cs

@@ -322,37 +322,17 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentException">No <paramref name="authenticationMethods"/> specified.</exception>
         public ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params AuthenticationMethod[] authenticationMethods)
         {
-            if (host is null)
-            {
-                throw new ArgumentNullException(nameof(host));
-            }
-
-            port.ValidatePort("port");
-
-            if (username is null)
-            {
-                throw new ArgumentNullException(nameof(username));
-            }
-
-            if (username.All(char.IsWhiteSpace))
-            {
-                throw new ArgumentException("Cannot be empty or contain only whitespace.", nameof(username));
-            }
+            ThrowHelper.ThrowIfNull(host);
+            port.ValidatePort();
+            ThrowHelper.ThrowIfNullOrWhiteSpace(username);
 
             if (proxyType != ProxyTypes.None)
             {
-                if (proxyHost is null)
-                {
-                    throw new ArgumentNullException(nameof(proxyHost));
-                }
-
-                proxyPort.ValidatePort("proxyPort");
+                ThrowHelper.ThrowIfNull(proxyHost);
+                proxyPort.ValidatePort();
             }
 
-            if (authenticationMethods is null)
-            {
-                throw new ArgumentNullException(nameof(authenticationMethods));
-            }
+            ThrowHelper.ThrowIfNull(authenticationMethods);
 
             if (authenticationMethods.Length == 0)
             {
@@ -467,10 +447,7 @@ namespace Renci.SshNet
         /// <exception cref="SshAuthenticationException">No suitable authentication method found to complete authentication, or permission denied.</exception>
         internal void Authenticate(ISession session, IServiceFactory serviceFactory)
         {
-            if (serviceFactory is null)
-            {
-                throw new ArgumentNullException(nameof(serviceFactory));
-            }
+            ThrowHelper.ThrowIfNull(serviceFactory);
 
             IsAuthenticated = false;
             var clientAuthentication = serviceFactory.CreateClientAuthentication();

+ 6 - 18
src/Renci.SshNet/ExpectAction.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Text.RegularExpressions;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet
 {
     /// <summary>
@@ -26,15 +28,8 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="expect"/> or <paramref name="action"/> is <see langword="null"/>.</exception>
         public ExpectAction(Regex expect, Action<string> action)
         {
-            if (expect is null)
-            {
-                throw new ArgumentNullException(nameof(expect));
-            }
-
-            if (action is null)
-            {
-                throw new ArgumentNullException(nameof(action));
-            }
+            ThrowHelper.ThrowIfNull(expect);
+            ThrowHelper.ThrowIfNull(action);
 
             Expect = expect;
             Action = action;
@@ -48,15 +43,8 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="expect"/> or <paramref name="action"/> is <see langword="null"/>.</exception>
         public ExpectAction(string expect, Action<string> action)
         {
-            if (expect is null)
-            {
-                throw new ArgumentNullException(nameof(expect));
-            }
-
-            if (action is null)
-            {
-                throw new ArgumentNullException(nameof(action));
-            }
+            ThrowHelper.ThrowIfNull(expect);
+            ThrowHelper.ThrowIfNull(action);
 
             Expect = new Regex(Regex.Escape(expect));
             Action = action;

+ 1 - 1
src/Renci.SshNet/ForwardedPort.cs

@@ -100,7 +100,7 @@ namespace Renci.SshNet
         /// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
         protected virtual void StopPort(TimeSpan timeout)
         {
-            timeout.EnsureValidTimeout(nameof(timeout));
+            timeout.EnsureValidTimeout();
 
             RaiseClosing();
 

+ 2 - 9
src/Renci.SshNet/ForwardedPortDynamic.cs

@@ -102,7 +102,7 @@ namespace Renci.SshNet
         /// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
         protected override void StopPort(TimeSpan timeout)
         {
-            timeout.EnsureValidTimeout(nameof(timeout));
+            timeout.EnsureValidTimeout();
 
             if (!ForwardedPortStatus.ToStopping(ref _status))
             {
@@ -128,14 +128,7 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The current instance is disposed.</exception>
         protected override void CheckDisposed()
         {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_isDisposed, this);
-#else
-            if (_isDisposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
         }
 
         /// <summary>

+ 6 - 20
src/Renci.SshNet/ForwardedPortLocal.cs

@@ -90,18 +90,11 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port" /> is greater than <see cref="IPEndPoint.MaxPort" />.</exception>
         public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint port)
         {
-            if (boundHost is null)
-            {
-                throw new ArgumentNullException(nameof(boundHost));
-            }
-
-            if (host is null)
-            {
-                throw new ArgumentNullException(nameof(host));
-            }
+            ThrowHelper.ThrowIfNull(boundHost);
+            ThrowHelper.ThrowIfNull(host);
 
-            boundPort.ValidatePort("boundPort");
-            port.ValidatePort("port");
+            boundPort.ValidatePort();
+            port.ValidatePort();
 
             BoundHost = boundHost;
             BoundPort = boundPort;
@@ -138,7 +131,7 @@ namespace Renci.SshNet
         /// <param name="timeout">The maximum amount of time to wait for pending requests to finish processing.</param>
         protected override void StopPort(TimeSpan timeout)
         {
-            timeout.EnsureValidTimeout(nameof(timeout));
+            timeout.EnsureValidTimeout();
 
             if (!ForwardedPortStatus.ToStopping(ref _status))
             {
@@ -164,14 +157,7 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The current instance is disposed.</exception>
         protected override void CheckDisposed()
         {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_isDisposed, this);
-#else
-            if (_isDisposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
         }
 
         /// <summary>

+ 6 - 20
src/Renci.SshNet/ForwardedPortRemote.cs

@@ -86,18 +86,11 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentOutOfRangeException"><paramref name="port" /> is greater than <see cref="IPEndPoint.MaxPort" />.</exception>
         public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress hostAddress, uint port)
         {
-            if (boundHostAddress is null)
-            {
-                throw new ArgumentNullException(nameof(boundHostAddress));
-            }
-
-            if (hostAddress is null)
-            {
-                throw new ArgumentNullException(nameof(hostAddress));
-            }
+            ThrowHelper.ThrowIfNull(boundHostAddress);
+            ThrowHelper.ThrowIfNull(hostAddress);
 
-            boundPort.ValidatePort("boundPort");
-            port.ValidatePort("port");
+            boundPort.ValidatePort();
+            port.ValidatePort();
 
             BoundHostAddress = boundHostAddress;
             BoundPort = boundPort;
@@ -188,7 +181,7 @@ namespace Renci.SshNet
         /// <param name="timeout">The maximum amount of time to wait for the port to stop.</param>
         protected override void StopPort(TimeSpan timeout)
         {
-            timeout.EnsureValidTimeout(nameof(timeout));
+            timeout.EnsureValidTimeout();
 
             if (!ForwardedPortStatus.ToStopping(ref _status))
             {
@@ -228,14 +221,7 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The current instance is disposed.</exception>
         protected override void CheckDisposed()
         {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_isDisposed, this);
-#else
-            if (_isDisposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
         }
 
         private void Session_ChannelOpening(object sender, MessageEventArgs<ChannelOpenMessage> e)

+ 3 - 4
src/Renci.SshNet/MessageEventArgs`1.cs

@@ -1,5 +1,7 @@
 using System;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet
 {
     /// <summary>
@@ -20,10 +22,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="message"/> is <see langword="null"/>.</exception>
         public MessageEventArgs(T message)
         {
-            if (message is null)
-            {
-                throw new ArgumentNullException(nameof(message));
-            }
+            ThrowHelper.ThrowIfNull(message);
 
             Message = message;
         }

+ 3 - 9
src/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs

@@ -1,4 +1,4 @@
-using System;
+using Renci.SshNet.Common;
 
 namespace Renci.SshNet.Messages.Connection
 {
@@ -89,10 +89,7 @@ namespace Renci.SshNet.Messages.Connection
         public ChannelDataMessage(uint localChannelNumber, byte[] data)
             : base(localChannelNumber)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             Data = data;
             Offset = 0;
@@ -109,10 +106,7 @@ namespace Renci.SshNet.Messages.Connection
         public ChannelDataMessage(uint localChannelNumber, byte[] data, int offset, int size)
             : base(localChannelNumber)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             Data = data;
             Offset = offset;

+ 3 - 4
src/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Globalization;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet.Messages.Connection
 {
     /// <summary>
@@ -104,10 +106,7 @@ namespace Renci.SshNet.Messages.Connection
         /// <exception cref="ArgumentNullException"><paramref name="info"/> is <see langword="null"/>.</exception>
         public ChannelOpenMessage(uint channelNumber, uint initialWindowSize, uint maximumPacketSize, ChannelOpenInfo info)
         {
-            if (info == null)
-            {
-                throw new ArgumentNullException(nameof(info));
-            }
+            ThrowHelper.ThrowIfNull(info);
 
             ChannelType = Ascii.GetBytes(info.ChannelType);
             LocalChannelNumber = channelNumber;

+ 4 - 9
src/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Text;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet.Messages.Connection
 {
     /// <summary>
@@ -79,15 +81,8 @@ namespace Renci.SshNet.Messages.Connection
         public ExecRequestInfo(string command, Encoding encoding)
             : this()
         {
-            if (command is null)
-            {
-                throw new ArgumentNullException(nameof(command));
-            }
-
-            if (encoding is null)
-            {
-                throw new ArgumentNullException(nameof(encoding));
-            }
+            ThrowHelper.ThrowIfNull(command);
+            ThrowHelper.ThrowIfNull(encoding);
 
             _command = encoding.GetBytes(command);
             Encoding = encoding;

+ 3 - 4
src/Renci.SshNet/Messages/Transport/IgnoreMessage.cs

@@ -1,5 +1,7 @@
 using System;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet.Messages.Transport
 {
     /// <summary>
@@ -45,10 +47,7 @@ namespace Renci.SshNet.Messages.Transport
         /// <param name="data">The data.</param>
         public IgnoreMessage(byte[] data)
         {
-            if (data is null)
-            {
-                throw new ArgumentNullException(nameof(data));
-            }
+            ThrowHelper.ThrowIfNull(data);
 
             Data = data;
         }

+ 2 - 4
src/Renci.SshNet/NoneAuthenticationMethod.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Threading;
 
+using Renci.SshNet.Common;
 using Renci.SshNet.Messages;
 using Renci.SshNet.Messages.Authentication;
 
@@ -43,10 +44,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="session" /> is <see langword="null"/>.</exception>
         public override AuthenticationResult Authenticate(Session session)
         {
-            if (session is null)
-            {
-                throw new ArgumentNullException(nameof(session));
-            }
+            ThrowHelper.ThrowIfNull(session);
 
             session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived;
             session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived;

+ 2 - 8
src/Renci.SshNet/PasswordAuthenticationMethod.cs

@@ -69,10 +69,7 @@ namespace Renci.SshNet
         public PasswordAuthenticationMethod(string username, byte[] password)
             : base(username)
         {
-            if (password is null)
-            {
-                throw new ArgumentNullException(nameof(password));
-            }
+            ThrowHelper.ThrowIfNull(password);
 
             _password = password;
             _requestMessage = new RequestMessagePassword(ServiceName.Connection, Username, _password);
@@ -88,10 +85,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="session" /> is <see langword="null"/>.</exception>
         public override AuthenticationResult Authenticate(Session session)
         {
-            if (session is null)
-            {
-                throw new ArgumentNullException(nameof(session));
-            }
+            ThrowHelper.ThrowIfNull(session);
 
             _session = session;
 

+ 1 - 4
src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs

@@ -42,10 +42,7 @@ namespace Renci.SshNet
         public PrivateKeyAuthenticationMethod(string username, params IPrivateKeySource[] keyFiles)
             : base(username)
         {
-            if (keyFiles is null)
-            {
-                throw new ArgumentNullException(nameof(keyFiles));
-            }
+            ThrowHelper.ThrowIfNull(keyFiles);
 
             KeyFiles = new Collection<IPrivateKeySource>(keyFiles);
         }

+ 38 - 48
src/Renci.SshNet/PrivateKeyFile.cs

@@ -1,6 +1,8 @@
-using System;
+#nullable enable
+using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.IO;
 using System.Numerics;
@@ -141,6 +143,8 @@ namespace Renci.SshNet
         /// <param name="key">The key.</param>
         public PrivateKeyFile(Key key)
         {
+            ThrowHelper.ThrowIfNull(key);
+
             _key = key;
             _hostAlgorithms.Add(new KeyHostAlgorithm(key.ToString(), key));
         }
@@ -150,16 +154,15 @@ namespace Renci.SshNet
         /// </summary>
         /// <param name="privateKey">The private key.</param>
         public PrivateKeyFile(Stream privateKey)
+            : this(privateKey, passPhrase: null)
         {
-            Open(privateKey, passPhrase: null);
-            Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set.");
         }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
         /// </summary>
-        /// <param name="fileName">Name of the file.</param>
-        /// <exception cref="ArgumentNullException"><paramref name="fileName"/> is <see langword="null"/> or empty.</exception>
+        /// <param name="fileName">The path of the private key file.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="fileName"/> is <see langword="null"/>.</exception>
         /// <remarks>
         /// This method calls <see cref="File.Open(string, FileMode)"/> internally, this method does not catch exceptions from <see cref="File.Open(string, FileMode)"/>.
         /// </remarks>
@@ -171,25 +174,23 @@ namespace Renci.SshNet
         /// <summary>
         /// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
         /// </summary>
-        /// <param name="fileName">Name of the file.</param>
-        /// <param name="passPhrase">The pass phrase.</param>
-        /// <exception cref="ArgumentNullException"><paramref name="fileName"/> is <see langword="null"/> or empty, or <paramref name="passPhrase"/> is <see langword="null"/>.</exception>
+        /// <param name="fileName">The path of the private key file.</param>
+        /// <param name="passPhrase">The pass phrase for the private key.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="fileName"/> is <see langword="null"/>.</exception>
         /// <remarks>
         /// This method calls <see cref="File.Open(string, FileMode)"/> internally, this method does not catch exceptions from <see cref="File.Open(string, FileMode)"/>.
         /// </remarks>
-        public PrivateKeyFile(string fileName, string passPhrase)
+        public PrivateKeyFile(string fileName, string? passPhrase)
         {
-            if (string.IsNullOrEmpty(fileName))
-            {
-                throw new ArgumentNullException(nameof(fileName));
-            }
+            ThrowHelper.ThrowIfNull(fileName);
 
-            using (var keyFile = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
+            using (var keyFile = File.OpenRead(fileName))
             {
                 Open(keyFile, passPhrase);
             }
 
-            Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set.");
+            Debug.Assert(Key is not null, $"{nameof(Key)} is null.");
+            Debug.Assert(HostKeyAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set.");
         }
 
         /// <summary>
@@ -197,12 +198,15 @@ namespace Renci.SshNet
         /// </summary>
         /// <param name="privateKey">The private key.</param>
         /// <param name="passPhrase">The pass phrase.</param>
-        /// <exception cref="ArgumentNullException"><paramref name="privateKey"/> or <paramref name="passPhrase"/> is <see langword="null"/>.</exception>
-        public PrivateKeyFile(Stream privateKey, string passPhrase)
+        /// <exception cref="ArgumentNullException"><paramref name="privateKey"/> is <see langword="null"/>.</exception>
+        public PrivateKeyFile(Stream privateKey, string? passPhrase)
         {
+            ThrowHelper.ThrowIfNull(privateKey);
+
             Open(privateKey, passPhrase);
 
-            Debug.Assert(_hostAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set.");
+            Debug.Assert(Key is not null, $"{nameof(Key)} is null.");
+            Debug.Assert(HostKeyAlgorithms.Count > 0, $"{nameof(HostKeyAlgorithms)} is not set.");
         }
 
         /// <summary>
@@ -210,12 +214,10 @@ namespace Renci.SshNet
         /// </summary>
         /// <param name="privateKey">The private key.</param>
         /// <param name="passPhrase">The pass phrase.</param>
-        private void Open(Stream privateKey, string passPhrase)
+        [MemberNotNull(nameof(_key))]
+        private void Open(Stream privateKey, string? passPhrase)
         {
-            if (privateKey is null)
-            {
-                throw new ArgumentNullException(nameof(privateKey));
-            }
+            Debug.Assert(privateKey is not null, "Should have validated not-null in the constructor.");
 
             Match privateKeyMatch;
 
@@ -444,20 +446,9 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="cipherInfo" />, <paramref name="cipherData" />, <paramref name="passPhrase" /> or <paramref name="binarySalt" /> is <see langword="null"/>.</exception>
         private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string passPhrase, byte[] binarySalt)
         {
-            if (cipherInfo is null)
-            {
-                throw new ArgumentNullException(nameof(cipherInfo));
-            }
-
-            if (cipherData is null)
-            {
-                throw new ArgumentNullException(nameof(cipherData));
-            }
-
-            if (binarySalt is null)
-            {
-                throw new ArgumentNullException(nameof(binarySalt));
-            }
+            Debug.Assert(cipherInfo != null);
+            Debug.Assert(cipherData != null);
+            Debug.Assert(binarySalt != null);
 
             var cipherKey = new List<byte>();
 
@@ -505,14 +496,14 @@ namespace Renci.SshNet
         /// <returns>
         /// The OpenSSH V1 key.
         /// </returns>
-        private static Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase)
+        private static Key ParseOpenSshV1Key(byte[] keyFileData, string? passPhrase)
         {
             var keyReader = new SshDataReader(keyFileData);
 
             // check magic header
-            var authMagic = Encoding.UTF8.GetBytes("openssh-key-v1\0");
+            var authMagic = "openssh-key-v1\0"u8;
             var keyHeaderBytes = keyReader.ReadBytes(authMagic.Length);
-            if (!authMagic.IsEqualTo(keyHeaderBytes))
+            if (!authMagic.SequenceEqual(keyHeaderBytes))
             {
                 throw new SshException("This openssh key does not contain the 'openssh-key-v1' format magic header");
             }
@@ -525,7 +516,7 @@ namespace Renci.SshNet
 
             // kdf options length: 24 if passphrase, 0 if no passphrase
             var kdfOptionsLen = (int)keyReader.ReadUInt32();
-            byte[] salt = null;
+            byte[]? salt = null;
             var rounds = 0;
             if (kdfOptionsLen > 0)
             {
@@ -613,6 +604,10 @@ namespace Renci.SshNet
                 var iv = keyiv.Take(keyLength, ivLength);
 
                 var cipher = cipherInfo.Cipher(key, iv);
+
+                // The authentication tag data (if any) is concatenated to the end of the encrypted private key string.
+                // See https://github.com/openssh/openssh-portable/blob/509b757c052ea969b3a41fc36818b44801caf1cf/sshkey.c#L2951
+                // and https://github.com/openssh/openssh-portable/blob/509b757c052ea969b3a41fc36818b44801caf1cf/cipher.c#L340
                 var cipherData = keyReader.ReadBytes(privateKeyLength + cipher.TagSize);
 
                 try
@@ -737,14 +732,9 @@ namespace Renci.SshNet
                 return;
             }
 
-            if (disposing)
+            if (disposing && _key is IDisposable disposableKey)
             {
-                var key = _key;
-                if (key != null)
-                {
-                    ((IDisposable)key).Dispose();
-                    _key = null;
-                }
+                disposableKey.Dispose();
 
                 _isDisposed = true;
             }

+ 3 - 4
src/Renci.SshNet/RemotePathDoubleQuoteTransformation.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Text;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet
 {
     /// <summary>
@@ -50,10 +52,7 @@ namespace Renci.SshNet
         /// </example>
         public string Transform(string path)
         {
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             var transformed = new StringBuilder(path.Length);
 

+ 3 - 4
src/Renci.SshNet/RemotePathNoneTransformation.cs

@@ -1,5 +1,7 @@
 using System;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet
 {
     /// <summary>
@@ -21,10 +23,7 @@ namespace Renci.SshNet
         /// </remarks>
         public string Transform(string path)
         {
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             return path;
         }

+ 3 - 4
src/Renci.SshNet/RemotePathShellQuoteTransformation.cs

@@ -1,6 +1,8 @@
 using System;
 using System.Text;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet
 {
     /// <summary>
@@ -80,10 +82,7 @@ namespace Renci.SshNet
         /// </example>
         public string Transform(string path)
         {
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             // result is at least value and (likely) leading/trailing single-quotes
             var sb = new StringBuilder(path.Length + 2);

+ 3 - 0
src/Renci.SshNet/Renci.SshNet.csproj

@@ -37,6 +37,9 @@
   <ItemGroup>
     <PackageReference Include="BouncyCastle.Cryptography" />
     <PackageReference Include="Nerdbank.GitVersioning" PrivateAssets="all" />
+    <PackageReference Include="PolySharp" PrivateAssets="all">
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageReference>
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net462' or '$(TargetFramework)' == 'netstandard2.0' ">

+ 10 - 50
src/Renci.SshNet/ScpClient.cs

@@ -33,7 +33,6 @@ namespace Renci.SshNet
     /// </remarks>
     public partial class ScpClient : BaseClient
     {
-        private const string Message = "filename";
         private const string FileInfoPattern = @"C(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)";
         private const string DirectoryInfoPattern = @"D(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)";
         private const string TimestampPattern = @"T(?<mtime>\d+) 0 (?<atime>\d+) 0";
@@ -117,10 +116,7 @@ namespace Renci.SshNet
             }
             set
             {
-                if (value is null)
-                {
-                    throw new ArgumentNullException(nameof(value));
-                }
+                ThrowHelper.ThrowIfNull(value);
 
                 _remotePathTransformation = value;
             }
@@ -292,10 +288,7 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         public void Upload(FileInfo fileInfo, string path)
         {
-            if (fileInfo is null)
-            {
-                throw new ArgumentNullException(nameof(fileInfo));
-            }
+            ThrowHelper.ThrowIfNull(fileInfo);
 
             if (Session is null)
             {
@@ -342,20 +335,8 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         public void Upload(DirectoryInfo directoryInfo, string path)
         {
-            if (directoryInfo is null)
-            {
-                throw new ArgumentNullException(nameof(directoryInfo));
-            }
-
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
-
-            if (path.Length == 0)
-            {
-                throw new ArgumentException("The path cannot be a zero-length string.", nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(directoryInfo);
+            ThrowHelper.ThrowIfNullOrEmpty(path);
 
             if (Session is null)
             {
@@ -397,15 +378,8 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         public void Download(string filename, FileInfo fileInfo)
         {
-            if (string.IsNullOrEmpty(filename))
-            {
-                throw new ArgumentException("filename");
-            }
-
-            if (fileInfo is null)
-            {
-                throw new ArgumentNullException(nameof(fileInfo));
-            }
+            ThrowHelper.ThrowIfNullOrEmpty(filename);
+            ThrowHelper.ThrowIfNull(fileInfo);
 
             if (Session is null)
             {
@@ -444,15 +418,8 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         public void Download(string directoryName, DirectoryInfo directoryInfo)
         {
-            if (string.IsNullOrEmpty(directoryName))
-            {
-                throw new ArgumentException("directoryName");
-            }
-
-            if (directoryInfo is null)
-            {
-                throw new ArgumentNullException(nameof(directoryInfo));
-            }
+            ThrowHelper.ThrowIfNullOrEmpty(directoryName);
+            ThrowHelper.ThrowIfNull(directoryInfo);
 
             if (Session is null)
             {
@@ -491,15 +458,8 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         public void Download(string filename, Stream destination)
         {
-            if (string.IsNullOrWhiteSpace(filename))
-            {
-                throw new ArgumentException(Message);
-            }
-
-            if (destination is null)
-            {
-                throw new ArgumentNullException(nameof(destination));
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(filename);
+            ThrowHelper.ThrowIfNull(destination);
 
             if (Session is null)
             {

+ 1 - 4
src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs

@@ -25,10 +25,7 @@ namespace Renci.SshNet.Security.Cryptography.Ciphers
 
                 if (cipherMode != System.Security.Cryptography.CipherMode.ECB)
                 {
-                    if (iv is null)
-                    {
-                        throw new ArgumentNullException(nameof(iv));
-                    }
+                    ThrowHelper.ThrowIfNull(iv);
 
                     aes.IV = iv.Take(16);
                 }

+ 1 - 4
src/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs

@@ -20,10 +20,7 @@ namespace Renci.SshNet.Security.Cryptography
         /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
         public DsaDigitalSignature(DsaKey key)
         {
-            if (key is null)
-            {
-                throw new ArgumentNullException(nameof(key));
-            }
+            ThrowHelper.ThrowIfNull(key);
 
             _key = key;
         }

+ 2 - 8
src/Renci.SshNet/Security/Cryptography/DsaKey.cs

@@ -90,10 +90,7 @@ namespace Renci.SshNet.Security
         /// <param name="publicKeyData">The encoded public key data.</param>
         public DsaKey(SshKeyData publicKeyData)
         {
-            if (publicKeyData is null)
-            {
-                throw new ArgumentNullException(nameof(publicKeyData));
-            }
+            ThrowHelper.ThrowIfNull(publicKeyData);
 
             if (publicKeyData.Name != "ssh-dss" || publicKeyData.Keys.Length != 4)
             {
@@ -114,10 +111,7 @@ namespace Renci.SshNet.Security
         /// <param name="privateKeyData">DER encoded private key data.</param>
         public DsaKey(byte[] privateKeyData)
         {
-            if (privateKeyData is null)
-            {
-                throw new ArgumentNullException(nameof(privateKeyData));
-            }
+            ThrowHelper.ThrowIfNull(privateKeyData);
 
             var der = new AsnReader(privateKeyData, AsnEncodingRules.DER).ReadSequence();
             _ = der.ReadInteger(); // skip version

+ 1 - 4
src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs

@@ -21,10 +21,7 @@ namespace Renci.SshNet.Security.Cryptography
         /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
         public ED25519DigitalSignature(ED25519Key key)
         {
-            if (key is null)
-            {
-                throw new ArgumentNullException(nameof(key));
-            }
+            ThrowHelper.ThrowIfNull(key);
 
             _key = key;
         }

+ 1 - 4
src/Renci.SshNet/Security/Cryptography/ED25519Key.cs

@@ -78,10 +78,7 @@ namespace Renci.SshNet.Security
         /// <param name="publicKeyData">The encoded public key data.</param>
         public ED25519Key(SshKeyData publicKeyData)
         {
-            if (publicKeyData is null)
-            {
-                throw new ArgumentNullException(nameof(publicKeyData));
-            }
+            ThrowHelper.ThrowIfNull(publicKeyData);
 
             if (publicKeyData.Name != "ssh-ed25519" || publicKeyData.Keys.Length != 1)
             {

+ 1 - 4
src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs

@@ -18,10 +18,7 @@ namespace Renci.SshNet.Security.Cryptography
         /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
         public EcdsaDigitalSignature(EcdsaKey key)
         {
-            if (key is null)
-            {
-                throw new ArgumentNullException(nameof(key));
-            }
+            ThrowHelper.ThrowIfNull(key);
 
             _key = key;
         }

+ 1 - 4
src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs

@@ -187,10 +187,7 @@ namespace Renci.SshNet.Security
         /// <param name="publicKeyData">The encoded public key data.</param>
         public EcdsaKey(SshKeyData publicKeyData)
         {
-            if (publicKeyData is null)
-            {
-                throw new ArgumentNullException(nameof(publicKeyData));
-            }
+            ThrowHelper.ThrowIfNull(publicKeyData);
 
             if (!publicKeyData.Name.StartsWith("ecdsa-sha2-", StringComparison.Ordinal) || publicKeyData.Keys.Length != 2)
             {

+ 2 - 8
src/Renci.SshNet/Security/Cryptography/RsaKey.cs

@@ -139,10 +139,7 @@ namespace Renci.SshNet.Security
         /// <param name="publicKeyData">The encoded public key data.</param>
         public RsaKey(SshKeyData publicKeyData)
         {
-            if (publicKeyData is null)
-            {
-                throw new ArgumentNullException(nameof(publicKeyData));
-            }
+            ThrowHelper.ThrowIfNull(publicKeyData);
 
             if (publicKeyData.Name != "ssh-rsa" || publicKeyData.Keys.Length != 2)
             {
@@ -162,10 +159,7 @@ namespace Renci.SshNet.Security
         /// <param name="privateKeyData">DER encoded private key data.</param>
         public RsaKey(byte[] privateKeyData)
         {
-            if (privateKeyData is null)
-            {
-                throw new ArgumentNullException(nameof(privateKeyData));
-            }
+            ThrowHelper.ThrowIfNull(privateKeyData);
 
             var der = new AsnReader(privateKeyData, AsnEncodingRules.DER).ReadSequence();
             _ = der.ReadInteger(); // skip version

+ 3 - 4
src/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs

@@ -1,5 +1,7 @@
 using System;
 
+using Renci.SshNet.Common;
+
 namespace Renci.SshNet.Security.Cryptography
 {
     /// <summary>
@@ -19,10 +21,7 @@ namespace Renci.SshNet.Security.Cryptography
         /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
         protected SymmetricCipher(byte[] key)
         {
-            if (key is null)
-            {
-                throw new ArgumentNullException(nameof(key));
-            }
+            ThrowHelper.ThrowIfNull(key);
 
             Key = key;
         }

+ 4 - 18
src/Renci.SshNet/ServiceFactory.cs

@@ -82,15 +82,8 @@ namespace Renci.SshNet
         /// <inheritdoc/>
         public IKeyExchange CreateKeyExchange(IDictionary<string, Func<IKeyExchange>> clientAlgorithms, string[] serverAlgorithms)
         {
-            if (clientAlgorithms is null)
-            {
-                throw new ArgumentNullException(nameof(clientAlgorithms));
-            }
-
-            if (serverAlgorithms is null)
-            {
-                throw new ArgumentNullException(nameof(serverAlgorithms));
-            }
+            ThrowHelper.ThrowIfNull(clientAlgorithms);
+            ThrowHelper.ThrowIfNull(serverAlgorithms);
 
             // find an algorithm that is supported by both client and server
             var keyExchangeAlgorithmFactory = (from c in clientAlgorithms
@@ -241,15 +234,8 @@ namespace Renci.SshNet
         /// <exception cref="NotSupportedException">The <see cref="IConnectionInfo.ProxyType"/> value of <paramref name="connectionInfo"/> is not supported.</exception>
         public IConnector CreateConnector(IConnectionInfo connectionInfo, ISocketFactory socketFactory)
         {
-            if (connectionInfo is null)
-            {
-                throw new ArgumentNullException(nameof(connectionInfo));
-            }
-
-            if (socketFactory is null)
-            {
-                throw new ArgumentNullException(nameof(socketFactory));
-            }
+            ThrowHelper.ThrowIfNull(connectionInfo);
+            ThrowHelper.ThrowIfNull(socketFactory);
 
             switch (connectionInfo.ProxyType)
             {

+ 5 - 22
src/Renci.SshNet/Session.cs

@@ -528,20 +528,9 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="socketFactory"/> is <see langword="null"/>.</exception>
         internal Session(ConnectionInfo connectionInfo, IServiceFactory serviceFactory, ISocketFactory socketFactory)
         {
-            if (connectionInfo is null)
-            {
-                throw new ArgumentNullException(nameof(connectionInfo));
-            }
-
-            if (serviceFactory is null)
-            {
-                throw new ArgumentNullException(nameof(serviceFactory));
-            }
-
-            if (socketFactory is null)
-            {
-                throw new ArgumentNullException(nameof(socketFactory));
-            }
+            ThrowHelper.ThrowIfNull(connectionInfo);
+            ThrowHelper.ThrowIfNull(serviceFactory);
+            ThrowHelper.ThrowIfNull(socketFactory);
 
             ConnectionInfo = connectionInfo;
             _serviceFactory = serviceFactory;
@@ -917,10 +906,7 @@ namespace Renci.SshNet
         /// </returns>
         private WaitResult TryWait(WaitHandle waitHandle, TimeSpan timeout, out Exception exception)
         {
-            if (waitHandle is null)
-            {
-                throw new ArgumentNullException(nameof(waitHandle));
-            }
+            ThrowHelper.ThrowIfNull(waitHandle);
 
             var waitHandles = new[]
                 {
@@ -982,10 +968,7 @@ namespace Renci.SshNet
         /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exception>
         internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
         {
-            if (waitHandle is null)
-            {
-                throw new ArgumentNullException(nameof(waitHandle));
-            }
+            ThrowHelper.ThrowIfNull(waitHandle);
 
             var waitHandles = new[]
                 {

+ 2 - 4
src/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Text;
 
+using Renci.SshNet.Common;
 using Renci.SshNet.Sftp.Responses;
 
 namespace Renci.SshNet.Sftp.Requests
@@ -43,10 +44,7 @@ namespace Renci.SshNet.Sftp.Requests
         public SftpRealPathRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action<SftpNameResponse> nameAction, Action<SftpStatusResponse> statusAction)
             : base(protocolVersion, requestId, statusAction)
         {
-            if (nameAction is null)
-            {
-                throw new ArgumentNullException(nameof(nameAction));
-            }
+            ThrowHelper.ThrowIfNull(nameAction);
 
             Encoding = encoding;
             Path = path;

+ 3 - 13
src/Renci.SshNet/Sftp/SftpFile.cs

@@ -31,15 +31,8 @@ namespace Renci.SshNet.Sftp
                 throw new SshConnectionException("Client not connected.");
             }
 
-            if (attributes is null)
-            {
-                throw new ArgumentNullException(nameof(attributes));
-            }
-
-            if (fullName is null)
-            {
-                throw new ArgumentNullException(nameof(fullName));
-            }
+            ThrowHelper.ThrowIfNull(attributes);
+            ThrowHelper.ThrowIfNull(fullName);
 
             _sftpSession = sftpSession;
             Attributes = attributes;
@@ -482,10 +475,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ArgumentNullException"><paramref name="destFileName"/> is <see langword="null"/>.</exception>
         public void MoveTo(string destFileName)
         {
-            if (destFileName is null)
-            {
-                throw new ArgumentNullException(nameof(destFileName));
-            }
+            ThrowHelper.ThrowIfNull(destFileName);
 
             _sftpSession.RequestRename(FullName, destFileName);
 

+ 1 - 8
src/Renci.SshNet/Sftp/SftpFileReader.cs

@@ -74,14 +74,7 @@ namespace Renci.SshNet.Sftp
 
         public byte[] Read()
         {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_disposingOrDisposed, this);
-#else
-            if (_disposingOrDisposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
+            ThrowHelper.ThrowObjectDisposedIf(_disposingOrDisposed, this);
 
             if (_exception is not null)
             {

+ 7 - 32
src/Renci.SshNet/Sftp/SftpFileStream.cs

@@ -222,10 +222,7 @@ namespace Renci.SshNet.Sftp
                 throw new SshConnectionException("Client not connected.");
             }
 
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (bufferSize <= 0)
             {
@@ -338,10 +335,7 @@ namespace Renci.SshNet.Sftp
                 throw new SshConnectionException("Client not connected.");
             }
 
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (bufferSize <= 0)
             {
@@ -519,10 +513,7 @@ namespace Renci.SshNet.Sftp
         {
             var readLen = 0;
 
-            if (buffer is null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
+            ThrowHelper.ThrowIfNull(buffer);
 
 #if NET8_0_OR_GREATER
             ArgumentOutOfRangeException.ThrowIfNegative(offset);
@@ -664,10 +655,7 @@ namespace Renci.SshNet.Sftp
         {
             var readLen = 0;
 
-            if (buffer is null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
+            ThrowHelper.ThrowIfNull(buffer);
 
 #if NET8_0_OR_GREATER
             ArgumentOutOfRangeException.ThrowIfNegative(offset);
@@ -1017,10 +1005,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
         public override void Write(byte[] buffer, int offset, int count)
         {
-            if (buffer is null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
+            ThrowHelper.ThrowIfNull(buffer);
 
 #if NET8_0_OR_GREATER
             ArgumentOutOfRangeException.ThrowIfNegative(offset);
@@ -1120,10 +1105,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
         public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
         {
-            if (buffer is null)
-            {
-                throw new ArgumentNullException(nameof(buffer));
-            }
+            ThrowHelper.ThrowIfNull(buffer);
 
 #if NET8_0_OR_GREATER
             ArgumentOutOfRangeException.ThrowIfNegative(offset);
@@ -1356,14 +1338,7 @@ namespace Renci.SshNet.Sftp
 
         private void CheckSessionIsOpen()
         {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_session is null, this);
-#else
-            if (_session is null)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
+            ThrowHelper.ThrowObjectDisposedIf(_session is null, this);
 
             if (!_session.IsOpen)
             {

+ 6 - 24
src/Renci.SshNet/Sftp/SftpSession.cs

@@ -576,10 +576,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception>
         public byte[] EndOpen(SftpOpenAsyncResult asyncResult)
         {
-            if (asyncResult is null)
-            {
-                throw new ArgumentNullException(nameof(asyncResult));
-            }
+            ThrowHelper.ThrowIfNull(asyncResult);
 
             if (asyncResult.EndInvokeCalled)
             {
@@ -700,10 +697,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception>
         public void EndClose(SftpCloseAsyncResult asyncResult)
         {
-            if (asyncResult is null)
-            {
-                throw new ArgumentNullException(nameof(asyncResult));
-            }
+            ThrowHelper.ThrowIfNull(asyncResult);
 
             if (asyncResult.EndInvokeCalled)
             {
@@ -778,10 +772,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception>
         public byte[] EndRead(SftpReadAsyncResult asyncResult)
         {
-            if (asyncResult is null)
-            {
-                throw new ArgumentNullException(nameof(asyncResult));
-            }
+            ThrowHelper.ThrowIfNull(asyncResult);
 
             if (asyncResult.EndInvokeCalled)
             {
@@ -1080,10 +1071,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception>
         public SftpFileAttributes EndLStat(SFtpStatAsyncResult asyncResult)
         {
-            if (asyncResult is null)
-            {
-                throw new ArgumentNullException(nameof(asyncResult));
-            }
+            ThrowHelper.ThrowIfNull(asyncResult);
 
             if (asyncResult.EndInvokeCalled)
             {
@@ -1648,10 +1636,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception>
         public string EndRealPath(SftpRealPathAsyncResult asyncResult)
         {
-            if (asyncResult is null)
-            {
-                throw new ArgumentNullException(nameof(asyncResult));
-            }
+            ThrowHelper.ThrowIfNull(asyncResult);
 
             if (asyncResult.EndInvokeCalled)
             {
@@ -1748,10 +1733,7 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <see langword="null"/>.</exception>
         public SftpFileAttributes EndStat(SFtpStatAsyncResult asyncResult)
         {
-            if (asyncResult is null)
-            {
-                throw new ArgumentNullException(nameof(asyncResult));
-            }
+            ThrowHelper.ThrowIfNull(asyncResult);
 
             if (asyncResult.EndInvokeCalled)
             {

+ 34 - 165
src/Renci.SshNet/SftpClient.cs

@@ -295,11 +295,7 @@ namespace Renci.SshNet
         public void ChangeDirectory(string path)
         {
             CheckDisposed();
-
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (_sftpSession is null)
             {
@@ -338,11 +334,7 @@ namespace Renci.SshNet
         public void CreateDirectory(string path)
         {
             CheckDisposed();
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(path);
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             if (_sftpSession is null)
             {
@@ -367,11 +359,7 @@ namespace Renci.SshNet
         public void DeleteDirectory(string path)
         {
             CheckDisposed();
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             if (_sftpSession is null)
             {
@@ -396,11 +384,7 @@ namespace Renci.SshNet
         public void DeleteFile(string path)
         {
             CheckDisposed();
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             if (_sftpSession is null)
             {
@@ -427,11 +411,7 @@ namespace Renci.SshNet
         public async Task DeleteFileAsync(string path, CancellationToken cancellationToken)
         {
             CheckDisposed();
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             if (_sftpSession is null)
             {
@@ -473,16 +453,8 @@ namespace Renci.SshNet
         public void RenameFile(string oldPath, string newPath, bool isPosix)
         {
             CheckDisposed();
-
-            if (oldPath is null)
-            {
-                throw new ArgumentNullException(nameof(oldPath));
-            }
-
-            if (newPath is null)
-            {
-                throw new ArgumentNullException(nameof(newPath));
-            }
+            ThrowHelper.ThrowIfNull(oldPath);
+            ThrowHelper.ThrowIfNull(newPath);
 
             if (_sftpSession is null)
             {
@@ -518,16 +490,8 @@ namespace Renci.SshNet
         public async Task RenameFileAsync(string oldPath, string newPath, CancellationToken cancellationToken)
         {
             CheckDisposed();
-
-            if (oldPath is null)
-            {
-                throw new ArgumentNullException(nameof(oldPath));
-            }
-
-            if (newPath is null)
-            {
-                throw new ArgumentNullException(nameof(newPath));
-            }
+            ThrowHelper.ThrowIfNull(oldPath);
+            ThrowHelper.ThrowIfNull(newPath);
 
             if (_sftpSession is null)
             {
@@ -554,16 +518,8 @@ namespace Renci.SshNet
         public void SymbolicLink(string path, string linkPath)
         {
             CheckDisposed();
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
-
-            if (string.IsNullOrWhiteSpace(linkPath))
-            {
-                throw new ArgumentException(nameof(linkPath));
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
+            ThrowHelper.ThrowIfNullOrWhiteSpace(linkPath);
 
             if (_sftpSession is null)
             {
@@ -614,11 +570,7 @@ namespace Renci.SshNet
         public async IAsyncEnumerable<ISftpFile> ListDirectoryAsync(string path, [EnumeratorCancellation] CancellationToken cancellationToken)
         {
             CheckDisposed();
-
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (_sftpSession is null)
             {
@@ -723,11 +675,7 @@ namespace Renci.SshNet
         public ISftpFile Get(string path)
         {
             CheckDisposed();
-
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (_sftpSession is null)
             {
@@ -756,11 +704,7 @@ namespace Renci.SshNet
         public bool Exists(string path)
         {
             CheckDisposed();
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             if (_sftpSession is null)
             {
@@ -887,16 +831,8 @@ namespace Renci.SshNet
         public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback? asyncCallback, object? state, Action<ulong>? downloadCallback = null)
         {
             CheckDisposed();
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
-
-            if (output is null)
-            {
-                throw new ArgumentNullException(nameof(output));
-            }
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
+            ThrowHelper.ThrowIfNull(output);
 
             var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state);
 
@@ -1104,16 +1040,8 @@ namespace Renci.SshNet
         public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null)
         {
             CheckDisposed();
-
-            if (input is null)
-            {
-                throw new ArgumentNullException(nameof(input));
-            }
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(input);
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             var flags = Flags.Write | Flags.Truncate;
 
@@ -1178,11 +1106,7 @@ namespace Renci.SshNet
         public SftpFileSystemInformation GetStatus(string path)
         {
             CheckDisposed();
-
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (_sftpSession is null)
             {
@@ -1209,11 +1133,7 @@ namespace Renci.SshNet
         public async Task<SftpFileSystemInformation> GetStatusAsync(string path, CancellationToken cancellationToken)
         {
             CheckDisposed();
-
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (_sftpSession is null)
             {
@@ -1243,11 +1163,7 @@ namespace Renci.SshNet
         public void AppendAllLines(string path, IEnumerable<string> contents)
         {
             CheckDisposed();
-
-            if (contents is null)
-            {
-                throw new ArgumentNullException(nameof(contents));
-            }
+            ThrowHelper.ThrowIfNull(contents);
 
             using (var stream = AppendText(path))
             {
@@ -1271,11 +1187,7 @@ namespace Renci.SshNet
         public void AppendAllLines(string path, IEnumerable<string> contents, Encoding encoding)
         {
             CheckDisposed();
-
-            if (contents is null)
-            {
-                throw new ArgumentNullException(nameof(contents));
-            }
+            ThrowHelper.ThrowIfNull(contents);
 
             using (var stream = AppendText(path, encoding))
             {
@@ -1358,11 +1270,7 @@ namespace Renci.SshNet
         public StreamWriter AppendText(string path, Encoding encoding)
         {
             CheckDisposed();
-
-            if (encoding is null)
-            {
-                throw new ArgumentNullException(nameof(encoding));
-            }
+            ThrowHelper.ThrowIfNull(encoding);
 
             return new StreamWriter(new SftpFileStream(_sftpSession, path, FileMode.Append, FileAccess.Write, (int)_bufferSize), encoding);
         }
@@ -1596,11 +1504,7 @@ namespace Renci.SshNet
         public Task<SftpFileStream> OpenAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken)
         {
             CheckDisposed();
-
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (_sftpSession is null)
             {
@@ -2100,15 +2004,8 @@ namespace Renci.SshNet
         /// <exception cref="SshException">If a problem occurs while copying the file.</exception>
         public IEnumerable<FileInfo> SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern)
         {
-            if (sourcePath is null)
-            {
-                throw new ArgumentNullException(nameof(sourcePath));
-            }
-
-            if (string.IsNullOrWhiteSpace(destinationPath))
-            {
-                throw new ArgumentException(nameof(destinationPath));
-            }
+            ThrowHelper.ThrowIfNull(sourcePath);
+            ThrowHelper.ThrowIfNullOrWhiteSpace(destinationPath);
 
             return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asynchResult: null);
         }
@@ -2129,20 +2026,9 @@ namespace Renci.SshNet
         /// <exception cref="SshException">If a problem occurs while copying the file.</exception>
         public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback? asyncCallback, object? state)
         {
-            if (sourcePath is null)
-            {
-                throw new ArgumentNullException(nameof(sourcePath));
-            }
-
-            if (string.IsNullOrWhiteSpace(destinationPath))
-            {
-                throw new ArgumentException(nameof(destinationPath));
-            }
-
-            if (searchPattern is null)
-            {
-                throw new ArgumentNullException(nameof(searchPattern));
-            }
+            ThrowHelper.ThrowIfNull(sourcePath);
+            ThrowHelper.ThrowIfNullOrWhiteSpace(destinationPath);
+            ThrowHelper.ThrowIfNull(searchPattern);
 
             var asyncResult = new SftpSynchronizeDirectoriesAsyncResult(asyncCallback, state);
 
@@ -2280,10 +2166,7 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client not connected.</exception>
         private List<ISftpFile> InternalListDirectory(string path, SftpListDirectoryAsyncResult? asyncResult, Action<int>? listCallback)
         {
-            if (path is null)
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(path);
 
             if (_sftpSession is null)
             {
@@ -2347,15 +2230,8 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client not connected.</exception>
         private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult? asyncResult, Action<ulong>? downloadCallback)
         {
-            if (output is null)
-            {
-                throw new ArgumentNullException(nameof(output));
-            }
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(output);
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             if (_sftpSession is null)
             {
@@ -2413,15 +2289,8 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client not connected.</exception>
         private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult? asyncResult, Action<ulong>? uploadCallback)
         {
-            if (input is null)
-            {
-                throw new ArgumentNullException(nameof(input));
-            }
-
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentException(nameof(path));
-            }
+            ThrowHelper.ThrowIfNull(input);
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 
             if (_sftpSession is null)
             {

+ 2 - 14
src/Renci.SshNet/ShellStream.cs

@@ -231,7 +231,7 @@ namespace Renci.SshNet
         /// <inheritdoc/>
         public override void Flush()
         {
-            ThrowIfDisposed();
+            ThrowHelper.ThrowObjectDisposedIf(_disposed, this);
 
             Debug.Assert(_writeLength >= 0 && _writeLength <= _writeBuffer.Length);
 
@@ -766,18 +766,6 @@ namespace Renci.SshNet
             }
         }
 
-        private void ThrowIfDisposed()
-        {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_disposed, this);
-#else
-            if (_disposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
-        }
-
         /// <summary>
         /// Reads all of the text currently available in the shell.
         /// </summary>
@@ -847,7 +835,7 @@ namespace Renci.SshNet
         /// <inheritdoc/>
         public override void Write(byte[] buffer, int offset, int count)
         {
-            ThrowIfDisposed();
+            ThrowHelper.ThrowObjectDisposedIf(_disposed, this);
 
             while (count > 0)
             {

+ 2 - 8
src/Renci.SshNet/SshClient.cs

@@ -165,10 +165,7 @@ namespace Renci.SshNet
         /// <exception cref="SshConnectionException">Client is not connected.</exception>
         public void AddForwardedPort(ForwardedPort port)
         {
-            if (port is null)
-            {
-                throw new ArgumentNullException(nameof(port));
-            }
+            ThrowHelper.ThrowIfNull(port);
 
             EnsureSessionIsOpen();
 
@@ -183,10 +180,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="port"/> is <see langword="null"/>.</exception>
         public void RemoveForwardedPort(ForwardedPort port)
         {
-            if (port is null)
-            {
-                throw new ArgumentNullException(nameof(port));
-            }
+            ThrowHelper.ThrowIfNull(port);
 
             // Stop port forwarding before removing it
             port.Stop();

+ 5 - 26
src/Renci.SshNet/SshCommand.cs

@@ -214,20 +214,9 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException">Either <paramref name="session"/>, <paramref name="commandText"/> is <see langword="null"/>.</exception>
         internal SshCommand(ISession session, string commandText, Encoding encoding)
         {
-            if (session is null)
-            {
-                throw new ArgumentNullException(nameof(session));
-            }
-
-            if (commandText is null)
-            {
-                throw new ArgumentNullException(nameof(commandText));
-            }
-
-            if (encoding is null)
-            {
-                throw new ArgumentNullException(nameof(encoding));
-            }
+            ThrowHelper.ThrowIfNull(session);
+            ThrowHelper.ThrowIfNull(commandText);
+            ThrowHelper.ThrowIfNull(encoding);
 
             _session = session;
             CommandText = commandText;
@@ -254,14 +243,7 @@ namespace Renci.SshNet
 #pragma warning disable CA1849 // Call async methods when in an async method; PipeStream.DisposeAsync would complete synchronously anyway.
         public Task ExecuteAsync(CancellationToken cancellationToken = default)
         {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_isDisposed, this);
-#else
-            if (_isDisposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
 
             if (cancellationToken.IsCancellationRequested)
             {
@@ -389,10 +371,7 @@ namespace Renci.SshNet
         /// <exception cref="SshOperationTimeoutException">Operation has timed out.</exception>
         public IAsyncResult BeginExecute(string commandText, AsyncCallback? callback, object? state)
         {
-            if (commandText is null)
-            {
-                throw new ArgumentNullException(nameof(commandText));
-            }
+            ThrowHelper.ThrowIfNull(commandText);
 
             CommandText = commandText;
 

+ 2 - 8
src/Renci.SshNet/SshMessageFactory.cs

@@ -179,10 +179,7 @@ namespace Renci.SshNet
 
         public void EnableAndActivateMessage(string messageName)
         {
-            if (messageName is null)
-            {
-                throw new ArgumentNullException(nameof(messageName));
-            }
+            ThrowHelper.ThrowIfNull(messageName);
 
             lock (_lock)
             {
@@ -206,10 +203,7 @@ namespace Renci.SshNet
 
         public void DisableAndDeactivateMessage(string messageName)
         {
-            if (messageName is null)
-            {
-                throw new ArgumentNullException(nameof(messageName));
-            }
+            ThrowHelper.ThrowIfNull(messageName);
 
             lock (_lock)
             {

+ 5 - 24
src/Renci.SshNet/SubsystemSession.cs

@@ -57,7 +57,7 @@ namespace Renci.SshNet
         {
             get
             {
-                EnsureNotDisposed();
+                ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
 
                 return _channel;
             }
@@ -83,15 +83,8 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="session" /> or <paramref name="subsystemName" /> is <see langword="null"/>.</exception>
         protected SubsystemSession(ISession session, string subsystemName, int operationTimeout)
         {
-            if (session is null)
-            {
-                throw new ArgumentNullException(nameof(session));
-            }
-
-            if (subsystemName is null)
-            {
-                throw new ArgumentNullException(nameof(subsystemName));
-            }
+            ThrowHelper.ThrowIfNull(session);
+            ThrowHelper.ThrowIfNull(subsystemName);
 
             _session = session;
             _subsystemName = subsystemName;
@@ -106,7 +99,7 @@ namespace Renci.SshNet
         /// <exception cref="SshException">The channel session could not be opened, or the subsystem could not be executed.</exception>
         public void Connect()
         {
-            EnsureNotDisposed();
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
 
             if (IsOpen)
             {
@@ -166,7 +159,7 @@ namespace Renci.SshNet
         /// <param name="data">The data to be sent.</param>
         public void SendData(byte[] data)
         {
-            EnsureNotDisposed();
+            ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
             EnsureSessionIsOpen();
 
             _channel.SendData(data);
@@ -536,17 +529,5 @@ namespace Renci.SshNet
                 _isDisposed = true;
             }
         }
-
-        private void EnsureNotDisposed()
-        {
-#if NET7_0_OR_GREATER
-            ObjectDisposedException.ThrowIf(_isDisposed, this);
-#else
-            if (_isDisposed)
-            {
-                throw new ObjectDisposedException(GetType().FullName);
-            }
-#endif // NET7_0_OR_GREATER
-        }
     }
 }

+ 9 - 0
test/Data/Key.OPENSSH.ECDSA.Encrypted.Aes.128.CTR.txt

@@ -0,0 +1,9 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczEyOC1jdHIAAAAGYmNyeXB0AAAAGAAAABB3bTQKrK
+H0DYVSdPpOe3RdAAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz
+dHAyNTYAAABBBP05AsaI5tCqEDeC6upJ6C8mzlEg79coH3HAj+yMkCbSOyarh323fQLri3
+W7i5wkBzoCehiLQcTqm25dCfkDbz0AAACwtbNeGU2YTSAl71OVKnekpohLV+jesgwSueEG
+fpj6R2YjysLkjuZhr7+hlVXrR+yKO3Ooqq1nku5LI/BRsWkogZYgL4C/0zyq4ZaYJA1/A8
+6ywlek12aWLhiLf7AfWGffjeU7qOqQRY6gxyhFhx9uI1YmrlXC3hbIcJJJFjSLLxboaJoP
+4PlYls4h/UCvjRzmGzQ19tB/mWExfbdvZyX9SXYQTHFHLYfXeZqzLcyWKqY=
+-----END OPENSSH PRIVATE KEY-----

+ 1 - 0
test/Data/Key.OPENSSH.ECDSA.Encrypted.Aes.128.CTR.txt.pub

@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBP05AsaI5tCqEDeC6upJ6C8mzlEg79coH3HAj+yMkCbSOyarh323fQLri3W7i5wkBzoCehiLQcTqm25dCfkDbz0= robert@VMWKS-015

+ 11 - 0
test/Data/Key.OPENSSH.ECDSA384.Encrypted.Aes.256.GCM.txt

@@ -0,0 +1,11 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAAFmFlczI1Ni1nY21Ab3BlbnNzaC5jb20AAAAGYmNyeXB0AA
+AAGAAAABB/rJasJWK8MoKQHtCZgIuVAAAAEAAAAAEAAACIAAAAE2VjZHNhLXNoYTItbmlz
+dHAzODQAAAAIbmlzdHAzODQAAABhBIoFo5PTu8NHANpVbFmZLpH3wvvegkt14r6cJF3WeU
+YAQLuhfxfujxli6+nHdgkuEZXVt3EVDZaosap9K8ZdiPxdA7fHj41Dc5CyyMIeNCdCTkO0
++BBDCygLHCw6k0a7wgAAANBizahkc7s9D0BkPZ1J4XY+qzWPLF5E1aR4xuXVh8GwYFdiE/
+Ko2o27CfH5TAd9fBXngoONDAAiVtkqBZ4zLoki/qb9EqIrtEOPeZvMQZC3icCM/PN/E0R0
+JzWS3dBYg6REMiBqtlU0/bfLTmAuG3CZkMw5uqbBlZw8yMpvLXkU3FJhAhc2B2vqTAABk3
+3F80tjoR0BYAg3UYnVT2CAffSjsXQIzV0NOWGChifgJsa+h1z6gH1Syhxkz0y8Pw74LWaf
+E5Qg1cTYtg16n2rYGq9y+RBSm3d7N0l5DU/pBTNA1A==
+-----END OPENSSH PRIVATE KEY-----

+ 1 - 0
test/Data/Key.OPENSSH.ECDSA384.Encrypted.Aes.256.GCM.txt.pub

@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBIoFo5PTu8NHANpVbFmZLpH3wvvegkt14r6cJF3WeUYAQLuhfxfujxli6+nHdgkuEZXVt3EVDZaosap9K8ZdiPxdA7fHj41Dc5CyyMIeNCdCTkO0+BBDCygLHCw6k0a7wg== SSH.NET

+ 12 - 0
test/Data/Key.OPENSSH.ECDSA521.Encrypted.Aes.192.CBC.txt

@@ -0,0 +1,12 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczE5Mi1jYmMAAAAGYmNyeXB0AAAAGAAAABCRq0zYrf
+9w0di4+awS6To0AAAAEAAAAAEAAACsAAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlz
+dHA1MjEAAACFBAGdkuygP8JJOCTbo3G6pxSBXIODWBNkYaidj4Z47o+r4OiAiAcEgpZSsQ
+n1qhXMOtzeqBMvRe7IphOWFXBae2viVABR5JNwUB08HtxRG6zgr6jv5vrDqggHGNAcShOc
+luRrWu87nIQgIw2N5unSX6HhdOQ3VYRTUI3kAAfx/8WI6GSfvQAAARAJ/zj9zqb38KT4dT
+Jk3YAiayhDyDlMakMcLVPqysoHHrePmD5UapDs6hxtb2AfgHy4P0dpsLprP3wjL1Eq9C3z
+Acm9S9P+B5dVAAWGojYRRuctHDUW6ytQ5nI+t85jjdkKO58gsEZuQizNdln1ShHkV9spdr
+OVBG49QO93v9L09C7LJEQtlsyibRayoc/AT/nOl9hDAZiV4uQ/tkOjyY+YPcexk0v49QNs
+yjNyayEsZLmDA5xKSLqKezjFUcuNT8/LQwH34zslyCrlRUO/VNSStqsM3dDjhxaqBWiFRZ
+wNWiMu2b2allf1+Ev2NGtuVFzx78XVTxNok2/1xgVGpffgkJD4U4ghr4vqHIdnCW6E7Q==
+-----END OPENSSH PRIVATE KEY-----

+ 1 - 0
test/Data/Key.OPENSSH.ECDSA521.Encrypted.Aes.192.CBC.txt.pub

@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGdkuygP8JJOCTbo3G6pxSBXIODWBNkYaidj4Z47o+r4OiAiAcEgpZSsQn1qhXMOtzeqBMvRe7IphOWFXBae2viVABR5JNwUB08HtxRG6zgr6jv5vrDqggHGNAcShOcluRrWu87nIQgIw2N5unSX6HhdOQ3VYRTUI3kAAfx/8WI6GSfvQ== SSH.NET

+ 0 - 1
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.128.CTR.pub

@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP44SYIMQiq6RfzllvHztr7ATNkXbAFqFZtukvHa0VaU SSH.NET

+ 0 - 8
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.128.CTR.txt

@@ -1,8 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAACmFlczEyOC1jdHIAAAAGYmNyeXB0AAAAGAAAABAGS7ibrm
-I3evObqzYMOyFsAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIP44SYIMQiq6Rfzl
-lvHztr7ATNkXbAFqFZtukvHa0VaUAAAAkHezd7R3B/U5jmRG9ayxcB5v9eIjjM2eZ/7O6z
-7waNtAIO36Ve+BY5qIduP4t8qdZ1JDFHaPxx/WLqezfV0hqRLzR4Pm/bcAHN60610x0BSF
-xHDC7nFej/X4Sr0SEplqsCfqfk5B4wsmdLUGwvpxIqoUCsCLx4YVaAlG/OezZUJ6b6lViZ
-zcaaG2MGRlm28f9Q==
------END OPENSSH PRIVATE KEY-----

+ 0 - 1
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CBC.pub

@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPjSxcsIj5ycJxIhhcMQuJAYTgANIXUa4Y1WEiOrblXA SSH.NET

+ 0 - 8
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CBC.txt

@@ -1,8 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAACmFlczE5Mi1jYmMAAAAGYmNyeXB0AAAAGAAAABCO+DfG2m
-YhNIGKmiDRH4cTAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIPjSxcsIj5ycJxIh
-hcMQuJAYTgANIXUa4Y1WEiOrblXAAAAAkC9fsItDX1HaNpw9PBxBX/Eedlu72MGGO7osjJ
-QtuvNb5VhcR07iYhxyw97F7MeraRYNvLrWQyxURB1BkZaSCemBrQJ/ljgEOhU5IgqmlooI
-T4x5BuChnet8HfPJ5Ws9fd5WMOtfMpdO8ZHkJM5VPiSUUhfgWSV0YGKY4Q7luDcpLHzBZk
-ZpossNHwNKsBMC9g==
------END OPENSSH PRIVATE KEY-----

+ 0 - 1
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CTR.pub

@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIERKx068MaW5RqzljZBz1nZw6OIzp4zyUjTKlJQ45wAg SSH.NET

+ 0 - 8
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.192.CTR.txt

@@ -1,8 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAACmFlczE5Mi1jdHIAAAAGYmNyeXB0AAAAGAAAABCV/tolDk
-gwraQcGNlFOu+GAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIERKx068MaW5Rqzl
-jZBz1nZw6OIzp4zyUjTKlJQ45wAgAAAAkGnQfFCyY4tOQO+dNs+1Zzt6l5UbRHMjECnvuF
-M3P5oFi0FXRSNSODXvZzZWCn9EVtaICV0bP+UKx9SfVAAkS64ZHl8n0IlRI9PmmthP6yZr
-N7RzXaejppY/ZqQm+yH7S1cNb9KsAEIGMUlws34KlPCitc1HKJu8r9UmQGXaXXur/l47f5
-AVP+RmKjdDZy7FvA==
------END OPENSSH PRIVATE KEY-----

+ 0 - 1
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.256.GCM.pub

@@ -1 +0,0 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKs32WTgxZkPlc1Xg4q6m9H2MRsMYDOuRXXXKNa8sBIM scott@SCOTTLAPTOP

+ 0 - 8
test/Data/Key.OPENSSH.ED25519.Encrypted.Aes.256.GCM.txt

@@ -1,8 +0,0 @@
------BEGIN OPENSSH PRIVATE KEY-----
-b3BlbnNzaC1rZXktdjEAAAAAFmFlczI1Ni1nY21Ab3BlbnNzaC5jb20AAAAGYmNyeXB0AA
-AAGAAAABDlF34NcdHSHJFFHrK8PK/FAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAA
-IGgS0NrTKMQcchUDkewJOtOC5J+nmrdny2rxCN0koPHdAAAAkHCy69z0kHw/kCs4a+LCBn
-2R37rprUEqHSiykVEPKxRDqpjqitaVNGzvPo6uhUclW9xxsAufMYv+Mn/Rz5ZLqHSV1Jio
-zdBZrAkM13DJpW6xKVjbjGTr7zXpnjr1dgMs1tmq/T9F503Dky84u9qA+jUVczFitWuwvn
-JMHIJ7zgAI2fp5z+aq51lH9rLAp6Vmjoal5MRsZhIHBrrwheBEWPE=
------END OPENSSH PRIVATE KEY-----

+ 39 - 0
test/Data/Key.OPENSSH.RSA.Encrypted.Aes.192.CTR.txt

@@ -0,0 +1,39 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczE5Mi1jdHIAAAAGYmNyeXB0AAAAGAAAABBYqTkTPZ
+iGh7R1+oK5fanAAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQC2ZDTbfK8N
+hmdhDTXIP9lfwjTJQoIIrpWIBFyBi5Lca+sZx9qaxB+6vJZvXXE3SEqAspfqbqXxKXEhyb
+T/yOj3n05W9/lp3XSw7YMNaV7Od5fj9n3PQRo9bjVlqcT2vKUfqpsk4d8c3+WH8e5W4ZYd
+v50gtj5AIk7cDMOZovAmXifS3O3JTJ7oFqhXMXwjbtf12Fsnx4MMfJrMlqpuuB49M+8zXI
+qSFGHx1T/uNuA/8YVZd51qiLciFDVUHefV/BoOp2caWx5ILLYcsnKIYyJLNVW+BxD9s1Yr
+jVrHbxcv+xtydCv7PDFEuK1Kj4XsYE6TYrdta0QWsajTK+CK62+DTs/pjj7aK8/vZdFKHE
+cOQw0EQOgIwBjrF9DqKgBB8xkkd9//Ankyt9Kg2YdX2oKkoaVxsz9VJFAKpOiDmASAQ5d5
+b9P/7KoOaIxdyyslOij7tO5DsZbZHoqtqxvb7Dy+l9GEkblOWbx8/6TBsVmCbQypt6N7wo
+bzSgNIKpHupoMAAAWAXrOffnhD8vHzk6DrT9GwF65tKQS9591sZOl0iijyQWSbh5EJazTq
+4KrkQp4PUUF/xiewpiLVm/BRvrLs3Q2nMzBMVav4AqSWQN3pDS7oHm4UP1WIxv7vJu2kRD
+v/N9tZOkc/O2tnkq8M1UyUrpc6y+Of6HhI3XiEnLMd4iDWt3+zHR4MBoe/8OrSPzG/aR0i
+ISHJGJ86dp5uZeltspTx4Vixbft8//4WUKIhXERubSAILCe4fijVXpFlx7JDrs8cVSJRNT
+abTUmxKTLUqJxfGQG0UcwYJKJxtSGsnPtQohL7A+U10ybKo7f0sFTUmQ3zypr0czUgR9uQ
+UA5Q4UukV+SXzOCU9GNpBJ0TTWgUp6pJ6n4RPxN+bZxkNvY+f4wKVHXmDojqjyNN+mWsi9
+ddvq5QTGXiZSAphhYhl0t01i2qjiYyidmpAfCQDsc1SEkFv55JlJFRkl6ahbiSQAGH0MoV
+t2QgC2FFbIKjJ+cl9m4S+/1GSbfzf+h8KJYT8d9uqmruhEbOetg4H6b3dFV8ZAEwUz/2Pm
+GnCSyOai/nJo0YvVdB6kBX+jhyJxD7/CvbHFEbMjhrLFc4R0UI+hjCMVo/CvrO5QPql9ho
+coOehdI4Q8OtJAyheq7WqQmFulzsGLleZFO9Ti0G2lerf3uimw4fCLf7nMI8CXkYP9urRn
+1VUx7IkLfG85qde1aSgIAODg4F+EfA3WpbBY3KeH4wtPounkhky3nJ+sDQqaLvEaB2bWE5
+kwTw+90y//2U8EiLtY2eF/YKV0LLo1e+Ynwj+V1jLErQr2tKbTtyhul4v2H5vr37pVMxwe
+JJZKibRWVoYvp0AH+xC0ibnLM+k/YzZasyVJA/ehuqu1dYDvEgVvYqo9izTS4zwl2sZ8+z
+x2XIAS64dERvOLFW4NUTm7k9/QusCqsqyrULZVvs3JQzFfs/2gydeWStZZ/sx1EiOq4Zi5
+JV7vtD4I1sFqYitZDpgqwwr1rc15Ewa0jyPmgyTEg6yngKSUoTZ+M2sqdjjWT3Xqje3kLp
+vcbDuoQ1X2dbZFqdVvcl/Cwa1KH4nBt3CuSM/fg+mzoKMuUlW5EhRjumaawDNsxawjUk24
+TrhCdU5nUrREWxLmODgaqgY7OwTWq20MXlpoWkMqyPHZbe8FxiIHtID9Afx7LWVh3WI2Dq
+v8ZT33f0+ORLGSqJPSZqEe+Ty2jSbIJvMBJ2NLq+FQF7GNV3t45HbvKpDluRMJgctVgY7r
+nFvmalsRnavet0mS9LpEP0kVMtFOU/LKC7jUZSX5zh4vaOCaMB29Q1vE4Gm9+HiE5Cq57B
+oXg1qV2KOQZgK+LUD9FrBQFXWWyUFQZCLepck/JgVKZDic7C+/I/53gPAtxsdTP+4/T9zP
+YRO5algWLBiAIZzAx4s93TzUc6Iy93lqOixtk02N3m2BQYAboShKPcxmSfcvFwk6mUOyeb
+Ma+58ExXt0htH8p8A73P4wNIHhYuDMF3/qH1NHXLiY7pNSOjMGdpQDZ/MWhqq3oniGzWPb
+oFxgwKctq8aYwR1ggbcD02Yt1XPCZdKVPmVEUuxZ53y1Fhx5UaYUy3re2M34csvrvlXLfz
+v0jL2K3j1vlBR372Niew81NoV79ZGsEfZvHYDZPuZgrPEM8KTNL+ZsfoaCTiX2q9lHwFO9
+FRFF6cWVfDXsONLhZ/mg2vo8voRAsXcQDWaUZzt+K9nNQB3zL+N8WFpyBx13I/FsBh5oTf
+FQiPckepWNk1pm/Dh5JKuE3mR5OFRfdX7C6jHBvSUxaZ3ospRtkC3SfXKTXF2OW7VxuouJ
+s6n3ymsQuttFm7olJ/h1bLVmXGTBZF9HOXLiT5+yM7E1s0mutnahL7zFIcFBUDO9WIVdaA
+CZWdHA==
+-----END OPENSSH PRIVATE KEY-----

+ 1 - 0
test/Data/Key.OPENSSH.RSA.Encrypted.Aes.192.CTR.txt.pub

@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2ZDTbfK8NhmdhDTXIP9lfwjTJQoIIrpWIBFyBi5Lca+sZx9qaxB+6vJZvXXE3SEqAspfqbqXxKXEhybT/yOj3n05W9/lp3XSw7YMNaV7Od5fj9n3PQRo9bjVlqcT2vKUfqpsk4d8c3+WH8e5W4ZYdv50gtj5AIk7cDMOZovAmXifS3O3JTJ7oFqhXMXwjbtf12Fsnx4MMfJrMlqpuuB49M+8zXIqSFGHx1T/uNuA/8YVZd51qiLciFDVUHefV/BoOp2caWx5ILLYcsnKIYyJLNVW+BxD9s1YrjVrHbxcv+xtydCv7PDFEuK1Kj4XsYE6TYrdta0QWsajTK+CK62+DTs/pjj7aK8/vZdFKHEcOQw0EQOgIwBjrF9DqKgBB8xkkd9//Ankyt9Kg2YdX2oKkoaVxsz9VJFAKpOiDmASAQ5d5b9P/7KoOaIxdyyslOij7tO5DsZbZHoqtqxvb7Dy+l9GEkblOWbx8/6TBsVmCbQypt6N7wobzSgNIKpHupoM= SSH.NET

+ 2 - 8
test/Renci.SshNet.IntegrationTests/Common/Socks5Handler.cs

@@ -22,10 +22,7 @@ namespace Renci.SshNet.IntegrationTests.Common
 
         public Socket Connect(IPEndPoint endPoint)
         {
-            if (endPoint == null)
-            {
-                throw new ArgumentNullException("endPoint");
-            }
+            ThrowHelper.ThrowIfNull(endPoint);
 
             var addressBytes = GetAddressBytes(endPoint);
             return Connect(addressBytes, endPoint.Port);
@@ -33,10 +30,7 @@ namespace Renci.SshNet.IntegrationTests.Common
 
         public Socket Connect(string host, int port)
         {
-            if (host == null)
-            {
-                throw new ArgumentNullException(nameof(host));
-            }
+            ThrowHelper.ThrowIfNull(host);
 
             if (host.Length > byte.MaxValue)
             {

+ 1 - 1
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.DeleteDirectory.cs

@@ -55,7 +55,7 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
         [TestMethod]
         [TestCategory("Sftp")]
         [Description("Test passing null to DeleteDirectory.")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void Test_Sftp_DeleteDirectory_Null()
         {
             using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))

+ 1 - 1
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Download.cs

@@ -77,7 +77,7 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
         [TestMethod]
         [TestCategory("Sftp")]
         [Description("Test passing null to BeginDownloadFile")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void Test_Sftp_BeginDownloadFile_FileNameIsNull()
         {
             using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))

+ 3 - 2
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs

@@ -353,7 +353,7 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
         [TestMethod]
         [TestCategory("Sftp")]
         [Description("Test passing null to BeginUploadFile")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void Test_Sftp_BeginUploadFile_FileNameIsNull()
         {
             using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
@@ -375,7 +375,8 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
                 var async1 = sftp.BeginListDirectory("/", null, null);
                 var filename = Path.GetTempFileName();
                 CreateTestFile(filename, 100);
-                var async2 = sftp.BeginUploadFile(File.OpenRead(filename), "test", null, null);
+                using var fileStream = File.OpenRead(filename);
+                var async2 = sftp.BeginUploadFile(fileStream, "test", null, null);
                 sftp.EndUploadFile(async1);
             }
         }

+ 12 - 14
test/Renci.SshNet.Tests/Classes/Common/TimeSpanExtensionsTest.cs

@@ -15,7 +15,7 @@ namespace Renci.SshNet.Tests.Classes.Common
         {
             var timeSpan = TimeSpan.FromSeconds(10);
 
-            var timeout = timeSpan.AsTimeout("TestMethodName");
+            var timeout = timeSpan.AsTimeout();
 
             Assert.AreEqual(10000, timeout);
         }
@@ -26,7 +26,7 @@ namespace Renci.SshNet.Tests.Classes.Common
         {
             var timeSpan = TimeSpan.FromSeconds(-1);
 
-            timeSpan.AsTimeout("TestMethodName");
+            timeSpan.AsTimeout();
         }
 
         [TestMethod]
@@ -35,24 +35,23 @@ namespace Renci.SshNet.Tests.Classes.Common
         {
             var timeSpan = TimeSpan.FromMilliseconds((double)int.MaxValue + 1);
 
-            timeSpan.AsTimeout("TestMethodName");
+            timeSpan.AsTimeout();
         }
 
         [TestMethod]
         public void AsTimeout_ArgumentOutOfRangeException_HasCorrectInformation()
         {
-
+            var timeSpan = TimeSpan.FromMilliseconds((double)int.MaxValue + 1);
             try
             {
-                var timeSpan = TimeSpan.FromMilliseconds((double)int.MaxValue + 1);
 
-                timeSpan.AsTimeout("TestMethodName");
+                timeSpan.AsTimeout();
             }
             catch (ArgumentOutOfRangeException ex)
             {
                 Assert.IsNull(ex.InnerException);
                 ArgumentExceptionAssert.MessageEquals("The timeout must represent a value between -1 and Int32.MaxValue milliseconds, inclusive.", ex);
-                Assert.AreEqual("TestMethodName", ex.ParamName);
+                Assert.AreEqual(nameof(timeSpan), ex.ParamName);
             }
         }
 
@@ -61,7 +60,7 @@ namespace Renci.SshNet.Tests.Classes.Common
         {
             var timeSpan = TimeSpan.FromSeconds(5);
 
-            timeSpan.EnsureValidTimeout("TestMethodName");
+            timeSpan.EnsureValidTimeout();
         }
 
         [TestMethod]
@@ -70,7 +69,7 @@ namespace Renci.SshNet.Tests.Classes.Common
         {
             var timeSpan = TimeSpan.FromSeconds(-1);
 
-            timeSpan.EnsureValidTimeout("TestMethodName");
+            timeSpan.EnsureValidTimeout();
         }
 
         [TestMethod]
@@ -79,24 +78,23 @@ namespace Renci.SshNet.Tests.Classes.Common
         {
             var timeSpan = TimeSpan.FromMilliseconds((double)int.MaxValue + 1);
 
-            timeSpan.EnsureValidTimeout("TestMethodName");
+            timeSpan.EnsureValidTimeout();
         }
 
         [TestMethod]
         public void EnsureValidTimeout_ArgumentOutOfRangeException_HasCorrectInformation()
         {
-
+            var timeSpan = TimeSpan.FromMilliseconds((double)int.MaxValue + 1);
             try
             {
-                var timeSpan = TimeSpan.FromMilliseconds((double)int.MaxValue + 1);
 
-                timeSpan.EnsureValidTimeout("TestMethodName");
+                timeSpan.EnsureValidTimeout();
             }
             catch (ArgumentOutOfRangeException ex)
             {
                 Assert.IsNull(ex.InnerException);
                 ArgumentExceptionAssert.MessageEquals("The timeout must represent a value between -1 and Int32.MaxValue milliseconds, inclusive.", ex);
-                Assert.AreEqual("TestMethodName", ex.ParamName);
+                Assert.AreEqual(nameof(timeSpan), ex.ParamName);
             }
         }
     }

+ 1 - 7
test/Renci.SshNet.Tests/Classes/KeyboardInteractiveAuthenticationMethodTest.cs

@@ -13,19 +13,13 @@ namespace Renci.SshNet.Tests.Classes
     public partial class KeyboardInteractiveAuthenticationMethodTest : TestBase
     {
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("KeyboardInteractiveAuthenticationMethod: Pass null as username.")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void Keyboard_Test_Pass_Null()
         {
             new KeyboardInteractiveAuthenticationMethod(null);
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("KeyboardInteractiveAuthenticationMethod: Pass String.Empty as username.")]
         [ExpectedException(typeof(ArgumentException))]
         public void Keyboard_Test_Pass_Whitespace()
         {

+ 1 - 7
test/Renci.SshNet.Tests/Classes/NoneAuthenticationMethodTest.cs

@@ -14,19 +14,13 @@ namespace Renci.SshNet.Tests.Classes
     public class NoneAuthenticationMethodTest : TestBase
     {
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("NoneAuthenticationMethod: Pass null as username.")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void None_Test_Pass_Null()
         {
             new NoneAuthenticationMethod(null);
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("NoneAuthenticationMethod: Pass String.Empty as username.")]
         [ExpectedException(typeof(ArgumentException))]
         public void None_Test_Pass_Whitespace()
         {

+ 1 - 16
test/Renci.SshNet.Tests/Classes/PasswordAuthenticationMethodTest.cs

@@ -13,19 +13,13 @@ namespace Renci.SshNet.Tests.Classes
     public partial class PasswordAuthenticationMethodTest : TestBase
     {
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("PasswordAuthenticationMethod: Pass null as username, \"valid\" as password.")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void Password_Test_Pass_Null_Username()
         {
             new PasswordAuthenticationMethod(null, "valid");
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("PasswordAuthenticationMethod: Pass \"valid\" as username, null as password.")]
         [ExpectedException(typeof(ArgumentNullException))]
         public void Password_Test_Pass_Null_Password()
         {
@@ -33,18 +27,12 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("PasswordAuthenticationMethod: Pass \"valid\" as username, \"valid\" as password.")]
         public void Password_Test_Pass_Valid_Username_And_Password()
         {
             new PasswordAuthenticationMethod("valid", "valid");
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("PasswordAuthenticationMethod: Pass String.Empty as username, \"valid\" as password.")]
         [ExpectedException(typeof(ArgumentException))]
         public void Password_Test_Pass_Whitespace()
         {
@@ -52,9 +40,6 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("PasswordAuthenticationMethod: Pass \"valid\" as username, String.Empty as password.")]
         public void Password_Test_Pass_Valid()
         {
             new PasswordAuthenticationMethod("valid", string.Empty);

+ 6 - 13
test/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs

@@ -14,8 +14,7 @@ namespace Renci.SshNet.Tests.Classes
     [TestClass]
     public class PasswordConnectionInfoTest : TestBase
     {
-        [WorkItem(703), TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
+        [TestMethod]
         public void Test_ConnectionInfo_Host_Is_Null()
         {
             try
@@ -31,16 +30,14 @@ namespace Renci.SshNet.Tests.Classes
 
         }
 
-        [WorkItem(703), TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
-        [ExpectedException(typeof(ArgumentException))]
+        [TestMethod]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void Test_ConnectionInfo_Username_Is_Null()
         {
             _ = new PasswordConnectionInfo(Resources.HOST, null, Resources.PASSWORD);
         }
 
-        [WorkItem(703), TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
+        [TestMethod]
         [ExpectedException(typeof(ArgumentNullException))]
         public void Test_ConnectionInfo_Password_Is_Null()
         {
@@ -48,24 +45,20 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
-        [Description("Test passing whitespace to username parameter.")]
         [ExpectedException(typeof(ArgumentException))]
         public void Test_ConnectionInfo_Username_Is_Whitespace()
         {
             _ = new PasswordConnectionInfo(Resources.HOST, " ", Resources.PASSWORD);
         }
 
-        [WorkItem(703), TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
+        [TestMethod]
         [ExpectedException(typeof(ArgumentOutOfRangeException))]
         public void Test_ConnectionInfo_SmallPortNumber()
         {
             _ = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MinPort - 1, Resources.USERNAME, Resources.PASSWORD);
         }
 
-        [WorkItem(703), TestMethod]
-        [TestCategory("PasswordConnectionInfo")]
+        [TestMethod]
         [ExpectedException(typeof(ArgumentOutOfRangeException))]
         public void Test_ConnectionInfo_BigPortNumber()
         {

+ 1 - 13
test/Renci.SshNet.Tests/Classes/PrivateKeyAuthenticationMethodTest.cs

@@ -13,21 +13,13 @@ namespace Renci.SshNet.Tests.Classes
     public class PrivateKeyAuthenticationMethodTest : TestBase
     {
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [TestCategory("PrivateKeyAuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("PrivateKeyAuthenticationMethod: Pass null as username, null as password.")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void PrivateKey_Test_Pass_Null()
         {
             new PrivateKeyAuthenticationMethod(null, null);
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [TestCategory("PrivateKeyAuthenticationMethod")]
-        [Owner("olegkap")]
-        [Description("PrivateKeyAuthenticationMethod: Pass valid username, null as password.")]
         [ExpectedException(typeof(ArgumentNullException))]
         public void PrivateKey_Test_Pass_PrivateKey_Null()
         {
@@ -35,10 +27,6 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [TestCategory("AuthenticationMethod")]
-        [TestCategory("PrivateKeyAuthenticationMethod")]
-        [Owner("Kenneth_aa")]
-        [Description("PrivateKeyAuthenticationMethod: Pass String.Empty as username, null as password.")]
         [ExpectedException(typeof(ArgumentException))]
         public void PrivateKey_Test_Pass_Whitespace()
         {

+ 71 - 474
test/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs

@@ -5,6 +5,7 @@ using System.Linq;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
 using Renci.SshNet.Common;
+using Renci.SshNet.Security;
 using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes
@@ -35,29 +36,10 @@ namespace Renci.SshNet.Tests.Classes
         /// <summary>
         /// A test for <see cref="PrivateKeyFile(string)"/> ctor.
         ///</summary>
-        [WorkItem(703), TestMethod]
-        public void ConstructorWithFileNameShouldThrowArgumentNullExceptionWhenFileNameIsEmpty()
-        {
-            var fileName = string.Empty;
-            try
-            {
-                _ = new PrivateKeyFile(fileName);
-                Assert.Fail();
-            }
-            catch (ArgumentNullException ex)
-            {
-                Assert.IsNull(ex.InnerException);
-                Assert.AreEqual("fileName", ex.ParamName);
-            }
-        }
-
-        /// <summary>
-        /// A test for <see cref="PrivateKeyFile(string)"/> ctor.
-        ///</summary>
-        [WorkItem(703), TestMethod]
+        [TestMethod]
         public void ConstructorWithFileNameShouldThrowArgumentNullExceptionWhenFileNameIsNull()
         {
-            var fileName = string.Empty;
+            string fileName = null;
             try
             {
                 _ = new PrivateKeyFile(fileName);
@@ -73,29 +55,10 @@ namespace Renci.SshNet.Tests.Classes
         /// <summary>
         /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
         ///</summary>
-        [WorkItem(703), TestMethod]
-        public void ConstructorWithFileNameAndPassphraseShouldThrowArgumentNullExceptionWhenFileNameIsEmpty()
-        {
-            var fileName = string.Empty;
-            try
-            {
-                _ = new PrivateKeyFile(fileName, "12345");
-                Assert.Fail();
-            }
-            catch (ArgumentNullException ex)
-            {
-                Assert.IsNull(ex.InnerException);
-                Assert.AreEqual("fileName", ex.ParamName);
-            }
-        }
-
-        /// <summary>
-        /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
-        ///</summary>
-        [WorkItem(703), TestMethod]
+        [TestMethod]
         public void ConstructorWithFileNameAndPassphraseShouldThrowArgumentNullExceptionWhenFileNameIsNull()
         {
-            var fileName = string.Empty;
+            string fileName = null;
             try
             {
                 _ = new PrivateKeyFile(fileName, "12345");
@@ -108,7 +71,7 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
 
-        [WorkItem(703), TestMethod]
+        [TestMethod]
         public void ConstructorWithPrivateKeyShouldThrowArgumentNullExceptionWhenPrivateKeyIsNull()
         {
             Stream privateKey = null;
@@ -124,7 +87,7 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
 
-        [WorkItem(703), TestMethod]
+        [TestMethod]
         public void ConstructorWithPrivateKeyAndPassphraseShouldThrowArgumentNullExceptionWhenPrivateKeyIsNull()
         {
             Stream privateKey = null;
@@ -141,63 +104,22 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [Owner("olegkap")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_RSA()
-        {
-            using (var stream = GetData("Key.RSA.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream));
-            }
-        }
-
-        [TestMethod]
-        [Owner("drieseng")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_SSH2_DSA()
-        {
-            using (var stream = GetData("Key.SSH2.DSA.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod]
-        [Owner("drieseng")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_SSH2_RSA()
+        public void ConstructorWithKeyShouldThrowArgumentNullExceptionWhenKeyIsNull()
         {
-            using (var stream = GetData("Key.SSH2.RSA.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream));
-            }
-        }
-
-        [TestMethod]
-        [Owner("drieseng")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_SSH2_Encrypted_DSA_DES_CBC()
-        {
-            using (var stream = GetData("Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt"))
+            Key key = null;
+            try
             {
-                _ = new PrivateKeyFile(stream, "12345");
+                _ = new PrivateKeyFile(key);
+                Assert.Fail();
             }
-        }
-
-        [TestMethod]
-        [Owner("drieseng")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_SSH2_Encrypted_RSA_DES_CBC()
-        {
-            using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
+            catch (ArgumentNullException ex)
             {
-                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
+                Assert.IsNull(ex.InnerException);
+                Assert.AreEqual("key", ex.ParamName);
             }
         }
 
         [TestMethod]
-        [Owner("drieseng")]
-        [TestCategory("PrivateKey")]
         public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshExceptionWhenPassphraseIsWrong()
         {
             using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
@@ -217,8 +139,6 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [Owner("drieseng")]
-        [TestCategory("PrivateKey")]
         public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPassphraseIsNull()
         {
             using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
@@ -238,8 +158,6 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [Owner("drieseng")]
-        [TestCategory("PrivateKey")]
         public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPassphraseIsEmpty()
         {
             using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
@@ -258,142 +176,10 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
 
-        [TestMethod]
-        [Owner("olegkap")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_RSA_DES_CBC()
-        {
-            using (var stream = GetData("Key.RSA.Encrypted.Des.CBC.12345.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
-            }
-        }
-
-        [TestMethod]
-        [Owner("olegkap")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_RSA_DES_EDE3_CBC()
-        {
-            using (var stream = GetData("Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod]
-        [Owner("olegkap")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_RSA_AES_128_CBC()
-        {
-            using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
-            }
-        }
-
-        [TestMethod]
-        [Owner("olegkap")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_RSA_AES_192_CBC()
-        {
-            using (var stream = GetData("Key.RSA.Encrypted.Aes.192.CBC.12345.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
-            }
-        }
-
-        [TestMethod]
-        [Owner("olegkap")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_RSA_AES_256_CBC()
-        {
-            using (var stream = GetData("Key.RSA.Encrypted.Aes.256.CBC.12345.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
-            }
-        }
-
-        [TestMethod]
-        [Owner("olegkap")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_RSA_DES_EDE3_CFB()
-        {
-            using (var stream = GetData("Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream, "1234567890"));
-            }
-        }
-
-        [TestMethod]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_ECDSA()
-        {
-            using (var stream = GetData("Key.ECDSA.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_ECDSA384()
-        {
-            using (var stream = GetData("Key.ECDSA384.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_ECDSA521()
-        {
-            using (var stream = GetData("Key.ECDSA521.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_ECDSA_Encrypted()
-        {
-            using (var stream = GetData("Key.ECDSA.Encrypted.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_ECDSA384_Encrypted()
-        {
-            using (var stream = GetData("Key.ECDSA384.Encrypted.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_ECDSA521_Encrypted()
-        {
-            using (var stream = GetData("Key.ECDSA521.Encrypted.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
         /// <summary>
         ///A test for Dispose
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
         public void DisposeTest()
         {
             using (var privateKeyStream = GetData("Key.RSA.txt"))
@@ -403,23 +189,10 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
 
-        /// <summary>
-        /// A test for <see cref="PrivateKeyFile(Stream, string)"/> ctor.
-        ///</summary>
-        [TestMethod()]
-        public void ConstructorWithStreamAndPassphrase()
-        {
-            using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
-            {
-                var privateKeyFile = new PrivateKeyFile(stream, "12345");
-                TestRsaKeyFile(privateKeyFile);
-            }
-        }
-
         /// <summary>
         /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
         public void ConstructorWithFileNameAndPassphrase()
         {
             using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
@@ -431,15 +204,13 @@ namespace Renci.SshNet.Tests.Classes
             {
                 var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
                 TestRsaKeyFile(privateKeyFile);
-
-                fs.Close();
             }
         }
 
         /// <summary>
         /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
         public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenNeededPassphraseIsEmpty()
         {
             var passphrase = string.Empty;
@@ -464,7 +235,7 @@ namespace Renci.SshNet.Tests.Classes
         /// <summary>
         /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
         public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenNeededPassphraseIsNull()
         {
             string passphrase = null;
@@ -489,7 +260,7 @@ namespace Renci.SshNet.Tests.Classes
         /// <summary>
         /// A test for <see cref="PrivateKeyFile(string)"/> ctor.
         ///</summary>
-        [TestMethod()]
+        [TestMethod]
         public void ConstructorWithFileName()
         {
             using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
@@ -501,21 +272,7 @@ namespace Renci.SshNet.Tests.Classes
             TestRsaKeyFile(privateKeyFile);
         }
 
-        /// <summary>
-        /// A test for <see cref="PrivateKeyFile(Stream)"/> ctor.
-        ///</summary>
-        [TestMethod()]
-        public void ConstructorWithStream()
-        {
-            using (var stream = GetData("Key.RSA.txt"))
-            {
-                var privateKeyFile = new PrivateKeyFile(stream);
-                TestRsaKeyFile(privateKeyFile);
-            }
-        }
-
         [TestMethod]
-        [TestCategory("PrivateKey")]
         public void ConstructorWithFileNameShouldBeAbleToReadFileThatIsSharedForReadAccess()
         {
             using (var stream = GetData("Key.RSA.txt"))
@@ -527,13 +284,10 @@ namespace Renci.SshNet.Tests.Classes
             {
                 var privateKeyFile = new PrivateKeyFile(_temporaryFile);
                 TestRsaKeyFile(privateKeyFile);
-
-                fs.Close();
             }
         }
 
         [TestMethod]
-        [TestCategory("PrivateKey")]
         public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsSharedForReadAccess()
         {
             using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
@@ -545,217 +299,60 @@ namespace Renci.SshNet.Tests.Classes
             {
                 var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
                 TestRsaKeyFile(privateKeyFile);
-
-                fs.Close();
-            }
-        }
-
-        [TestMethod()]
-        [Owner("bhalbright")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_3DES_CBC()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.3Des.CBC.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_128_CBC()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.128.CBC.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
             }
         }
 
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_192_CBC()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.192.CBC.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_256_CBC()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.256.CBC.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_128_CTR()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.128.CTR.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_192_CTR()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.192.CTR.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_256_CTR()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.256.CTR.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_128_GCM()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.128.GCM.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_AES_256_GCM()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.Aes.256.GCM.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("scott-xu")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED_ChaCha20_Poly1305()
-        {
-            using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.ChaCha20.Poly1305.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_RSA()
-        {
-            using (var stream = GetData("Key.OPENSSH.RSA.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream));
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_RSA_ENCRYPTED()
-        {
-            using (var stream = GetData("Key.OPENSSH.RSA.Encrypted.txt"))
-            {
-                TestRsaKeyFile(new PrivateKeyFile(stream, "12345"));
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ECDSA()
-        {
-            using (var stream = GetData("Key.OPENSSH.ECDSA.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ECDSA_ENCRYPTED()
-        {
-            using (var stream = GetData("Key.OPENSSH.ECDSA.Encrypted.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ECDSA384()
-        {
-            using (var stream = GetData("Key.OPENSSH.ECDSA384.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ECDSA384_ENCRYPTED()
-        {
-            using (var stream = GetData("Key.OPENSSH.ECDSA384.Encrypted.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ECDSA521()
-        {
-            using (var stream = GetData("Key.OPENSSH.ECDSA521.txt"))
-            {
-                _ = new PrivateKeyFile(stream);
-            }
-        }
-
-        [TestMethod()]
-        [Owner("darinkes")]
-        [TestCategory("PrivateKey")]
-        public void Test_PrivateKey_OPENSSH_ECDSA521_ENCRYPTED()
-        {
-            using (var stream = GetData("Key.OPENSSH.ECDSA521.Encrypted.txt"))
-            {
-                _ = new PrivateKeyFile(stream, "12345");
+        [TestMethod]
+        [DataRow("Key.DSA.txt", null, typeof(DsaKey))]
+        [DataRow("Key.ECDSA.Encrypted.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.ECDSA.txt", null, typeof(EcdsaKey))]
+        [DataRow("Key.ECDSA384.Encrypted.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.ECDSA384.txt", null, typeof(EcdsaKey))]
+        [DataRow("Key.ECDSA521.Encrypted.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.ECDSA521.txt", null, typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA.Encrypted.Aes.128.CTR.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA.Encrypted.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA.txt", null, typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA384.Encrypted.Aes.256.GCM.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA384.Encrypted.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA384.txt", null, typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA521.Encrypted.Aes.192.CBC.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA521.Encrypted.txt", "12345", typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ECDSA521.txt", null, typeof(EcdsaKey))]
+        [DataRow("Key.OPENSSH.ED25519.Encrypted.3Des.CBC.txt", "12345", typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.128.CBC.txt", "12345", typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.128.GCM.txt", "12345", typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.256.CBC.txt", "12345", typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.256.CTR.txt", "12345", typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.ED25519.Encrypted.ChaCha20.Poly1305.txt", "12345", typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.ED25519.Encrypted.txt", "12345", typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.ED25519.txt", null, typeof(ED25519Key))]
+        [DataRow("Key.OPENSSH.RSA.Encrypted.Aes.192.CTR.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.OPENSSH.RSA.Encrypted.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.OPENSSH.RSA.txt", null, typeof(RsaKey))]
+        [DataRow("Key.RSA.Encrypted.Aes.128.CBC.12345.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.RSA.Encrypted.Aes.192.CBC.12345.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.RSA.Encrypted.Aes.256.CBC.12345.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.RSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt", "1234567890", typeof(RsaKey))]
+        [DataRow("Key.RSA.txt", null, typeof(RsaKey))]
+        [DataRow("Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(DsaKey))]
+        [DataRow("Key.SSH2.DSA.txt", null, typeof(DsaKey))]
+        [DataRow("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(RsaKey))]
+        [DataRow("Key.SSH2.RSA.txt", null, typeof(RsaKey))]
+        public void Test_PrivateKey(string name, string passPhrase, Type expectedKeyType)
+        {
+            using (var stream = GetData(name))
+            {
+                var pkFile = new PrivateKeyFile(stream, passPhrase);
+
+                Assert.IsInstanceOfType(pkFile.Key, expectedKeyType);
+
+                if (expectedKeyType == typeof(RsaKey))
+                {
+                    TestRsaKeyFile(pkFile);
+                }
             }
         }
 

+ 1 - 3
test/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFile.cs

@@ -12,9 +12,7 @@ namespace Renci.SshNet.Tests.Classes
     public partial class SftpClientTest
     {
         [TestMethod]
-        [TestCategory("Sftp")]
-        [Description("Test passing null to DeleteFile.")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public void Test_Sftp_DeleteFile_Null()
         {
             using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))

+ 1 - 3
test/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs

@@ -13,9 +13,7 @@ namespace Renci.SshNet.Tests.Classes
     public partial class SftpClientTest
     {
         [TestMethod]
-        [TestCategory("Sftp")]
-        [Description("Test passing null to DeleteFile.")]
-        [ExpectedException(typeof(ArgumentException))]
+        [ExpectedException(typeof(ArgumentNullException))]
         public async Task Test_Sftp_DeleteFileAsync_Null()
         {
             using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))