Agent auth and Keygen (#794)

* Allow to set PrivateKeyFile Key directly
   So you can add your own Key-Classes to SSH.NET
* Add ED25519 ctor for just pub key part.
* Make ECDSA Key Bits accessible
   You cant export imported CngKeys. To be able to export them to agent or Key-Files make the private bits also accessible.
* Better NETFRAMEWORK vs NETSTANDARD handling
* Add Comment Property to Key
* Add IPrivateKeySource
  So Extension can add own PrivateKeyFiles, e.g. PuttyKeyFile.
This commit is contained in:
Stefan Rinkes
2022-03-05 17:05:02 +01:00
committed by GitHub
parent b30b11af4f
commit bc99ada7da
12 changed files with 156 additions and 108 deletions
+15
View File
@@ -0,0 +1,15 @@
using Renci.SshNet.Security;
namespace Renci.SshNet
{
/// <summary>
/// Represents private key source interface.
/// </summary>
public interface IPrivateKeySource
{
/// <summary>
/// Gets the host key.
/// </summary>
HostAlgorithm HostKey { get; }
}
}
+4 -4
View File
@@ -103,7 +103,7 @@ namespace Renci.SshNet
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public NetConfClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
public NetConfClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
: this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true)
{
}
@@ -116,7 +116,7 @@ namespace Renci.SshNet
/// <param name="keyFiles">Authentication private key file(s) .</param>
/// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
public NetConfClient(string host, string username, params PrivateKeyFile[] keyFiles)
public NetConfClient(string host, string username, params IPrivateKeySource[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
@@ -163,7 +163,7 @@ namespace Renci.SshNet
/// <value>
/// The NetConf server capabilities.
/// </value>
public XmlDocument ServerCapabilities
public XmlDocument ServerCapabilities
{
get { return _netConfSession.ServerCapabilities; }
}
@@ -277,4 +277,4 @@ namespace Renci.SshNet
}
}
}
}
}
@@ -28,7 +28,7 @@ namespace Renci.SshNet
/// <summary>
/// Gets the key files used for authentication.
/// </summary>
public ICollection<PrivateKeyFile> KeyFiles { get; private set; }
public ICollection<IPrivateKeySource> KeyFiles { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="PrivateKeyAuthenticationMethod"/> class.
@@ -36,13 +36,13 @@ namespace Renci.SshNet
/// <param name="username">The username.</param>
/// <param name="keyFiles">The key files.</param>
/// <exception cref="ArgumentException"><paramref name="username"/> is whitespace or <c>null</c>.</exception>
public PrivateKeyAuthenticationMethod(string username, params PrivateKeyFile[] keyFiles)
public PrivateKeyAuthenticationMethod(string username, params IPrivateKeySource[] keyFiles)
: base(username)
{
if (keyFiles == null)
throw new ArgumentNullException("keyFiles");
KeyFiles = new Collection<PrivateKeyFile>(keyFiles);
KeyFiles = new Collection<IPrivateKeySource>(keyFiles);
}
/// <summary>
@@ -250,4 +250,4 @@ namespace Renci.SshNet
}
}
}
}
}
+10 -10
View File
@@ -15,7 +15,7 @@ namespace Renci.SshNet
/// <summary>
/// Gets the key files used for authentication.
/// </summary>
public ICollection<PrivateKeyFile> KeyFiles { get; private set; }
public ICollection<IPrivateKeySource> KeyFiles { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="PrivateKeyConnectionInfo"/> class.
@@ -40,7 +40,7 @@ namespace Renci.SshNet
/// <param name="port">Connection port.</param>
/// <param name="username">Connection username.</param>
/// <param name="keyFiles">Connection key files.</param>
public PrivateKeyConnectionInfo(string host, int port, string username, params PrivateKeyFile[] keyFiles)
public PrivateKeyConnectionInfo(string host, int port, string username, params IPrivateKeySource[] keyFiles)
: this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles)
{
}
@@ -55,7 +55,7 @@ namespace Renci.SshNet
/// <param name="proxyHost">The proxy host.</param>
/// <param name="proxyPort">The proxy port.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles)
public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles)
: this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles)
{
}
@@ -71,7 +71,7 @@ namespace Renci.SshNet
/// <param name="proxyPort">The proxy port.</param>
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles)
public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles)
: this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles)
{
}
@@ -85,7 +85,7 @@ namespace Renci.SshNet
/// <param name="proxyHost">The proxy host.</param>
/// <param name="proxyPort">The proxy port.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles)
public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params IPrivateKeySource[] keyFiles)
: this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles)
{
}
@@ -100,7 +100,7 @@ namespace Renci.SshNet
/// <param name="proxyPort">The proxy port.</param>
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles)
public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params IPrivateKeySource[] keyFiles)
: this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles)
{
}
@@ -116,7 +116,7 @@ namespace Renci.SshNet
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="proxyPassword">The proxy password.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles)
public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles)
: this(host, DefaultPort, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, keyFiles)
{
}
@@ -133,10 +133,10 @@ namespace Renci.SshNet
/// <param name="proxyUsername">The proxy username.</param>
/// <param name="proxyPassword">The proxy password.</param>
/// <param name="keyFiles">The key files.</param>
public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles)
public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params IPrivateKeySource[] keyFiles)
: base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(username, keyFiles))
{
KeyFiles = new Collection<PrivateKeyFile>(keyFiles);
KeyFiles = new Collection<IPrivateKeySource>(keyFiles);
}
#region IDisposable Members
@@ -194,4 +194,4 @@ namespace Renci.SshNet
#endregion
}
}
}
+13 -5
View File
@@ -63,7 +63,7 @@ namespace Renci.SshNet
/// </list>
/// </para>
/// </remarks>
public class PrivateKeyFile : IDisposable
public class PrivateKeyFile : IPrivateKeySource, IDisposable
{
private static readonly Regex PrivateKeyRegex = new Regex(@"^-+ *BEGIN (?<keyName>\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k<keyName> PRIVATE KEY *-+",
#if FEATURE_REGEX_COMPILE
@@ -79,6 +79,15 @@ namespace Renci.SshNet
/// </summary>
public HostAlgorithm HostKey { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
/// </summary>
/// <param name="key">The key.</param>
public PrivateKeyFile(Key key)
{
HostKey = new KeyHostAlgorithm(key.ToString(), key);
}
/// <summary>
/// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
/// </summary>
@@ -262,7 +271,7 @@ namespace Renci.SshNet
if (decryptedLength > blobSize - 4)
throw new SshException("Invalid passphrase.");
if (keyType == "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}")
{
var exponent = reader.ReadBigIntWithBits();//e
@@ -515,8 +524,7 @@ namespace Renci.SshNet
throw new SshException("OpenSSH key type '" + keyType + "' is not supported.");
}
//comment, we don't need this but we could log it, not sure if necessary
var comment = privateKeyReader.ReadString(Encoding.UTF8);
parsedKey.Comment = privateKeyReader.ReadString(Encoding.UTF8);
//The list of privatekey/comment pairs is padded with the bytes 1, 2, 3, ...
//until the total length is a multiple of the cipher block size.
@@ -642,4 +650,4 @@ namespace Renci.SshNet
}
}
}
}
}
+3 -3
View File
@@ -142,7 +142,7 @@ namespace Renci.SshNet
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public ScpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
public ScpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
: this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true)
{
}
@@ -155,7 +155,7 @@ namespace Renci.SshNet
/// <param name="keyFiles">Authentication private key file(s) .</param>
/// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
public ScpClient(string host, string username, params PrivateKeyFile[] keyFiles)
public ScpClient(string host, string username, params IPrivateKeySource[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
@@ -466,4 +466,4 @@ namespace Renci.SshNet
throw new SshException("Secure copy execution request was rejected by the server. Please consult the server logs.");
}
}
}
}
@@ -99,6 +99,15 @@ namespace Renci.SshNet.Security
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ED25519Key"/> class.
/// </summary>
/// <param name="pk">pk data.</param>
public ED25519Key(byte[] pk)
{
publicKey = pk.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes);
}
/// <summary>
/// Initializes a new instance of the <see cref="ED25519Key"/> class.
/// </summary>
@@ -39,12 +39,12 @@ namespace Renci.SshNet.Security.Cryptography
// for 521 sig_size is 132
var sig_size = _key.KeyLength == 521 ? 132 : _key.KeyLength / 4;
var ssh_data = new SshDataSignature(signature, sig_size);
#if NETSTANDARD2_0
return _key.Ecdsa.VerifyData(input, ssh_data.Signature, _key.HashAlgorithm);
#else
#if NETFRAMEWORK
var ecdsa = (ECDsaCng)_key.Ecdsa;
ecdsa.HashAlgorithm = _key.HashAlgorithm;
return ecdsa.VerifyData(input, ssh_data.Signature);
#else
return _key.Ecdsa.VerifyData(input, ssh_data.Signature, _key.HashAlgorithm);
#endif
}
@@ -57,12 +57,12 @@ namespace Renci.SshNet.Security.Cryptography
/// </returns>
public override byte[] Sign(byte[] input)
{
#if NETSTANDARD2_0
var signed = _key.Ecdsa.SignData(input, _key.HashAlgorithm);
#else
#if NETFRAMEWORK
var ecdsa = (ECDsaCng)_key.Ecdsa;
ecdsa.HashAlgorithm = _key.HashAlgorithm;
var signed = ecdsa.SignData(input);
#else
var signed = _key.Ecdsa.SignData(input, _key.HashAlgorithm);
#endif
var ssh_data = new SshDataSignature(signed.Length);
ssh_data.Signature = signed;
@@ -18,7 +18,7 @@ namespace Renci.SshNet.Security
internal const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1
internal const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1
#if !NETSTANDARD2_0
#if NETFRAMEWORK
internal enum KeyBlobMagicNumber : int
{
BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345,
@@ -57,27 +57,7 @@ namespace Renci.SshNet.Security
return string.Format("ecdsa-sha2-nistp{0}", KeyLength);
}
#if NETSTANDARD2_0
/// <summary>
/// Gets the HashAlgorithm to use
/// </summary>
public HashAlgorithmName HashAlgorithm
{
get
{
switch (KeyLength)
{
case 256:
return HashAlgorithmName.SHA256;
case 384:
return HashAlgorithmName.SHA384;
case 521:
return HashAlgorithmName.SHA512;
}
return HashAlgorithmName.SHA256;
}
}
#else
#if NETFRAMEWORK
/// <summary>
/// Gets the HashAlgorithm to use
/// </summary>
@@ -98,6 +78,26 @@ namespace Renci.SshNet.Security
}
}
}
#else
/// <summary>
/// Gets the HashAlgorithm to use
/// </summary>
public HashAlgorithmName HashAlgorithm
{
get
{
switch (KeyLength)
{
case 256:
return HashAlgorithmName.SHA256;
case 384:
return HashAlgorithmName.SHA384;
case 521:
return HashAlgorithmName.SHA512;
}
return HashAlgorithmName.SHA256;
}
}
#endif
/// <summary>
@@ -144,28 +144,7 @@ namespace Renci.SshNet.Security
byte[] curve;
byte[] qx;
byte[] qy;
#if NETSTANDARD2_0
var parameter = Ecdsa.ExportParameters(false);
qx = parameter.Q.X;
qy = parameter.Q.Y;
switch (parameter.Curve.Oid.FriendlyName)
{
case "ECDSA_P256":
case "nistP256":
curve = Encoding.ASCII.GetBytes("nistp256");
break;
case "ECDSA_P384":
case "nistP384":
curve = Encoding.ASCII.GetBytes("nistp384");
break;
case "ECDSA_P521":
case "nistP521":
curve = Encoding.ASCII.GetBytes("nistp521");
break;
default:
throw new SshException("Unexpected Curve Name: " + parameter.Curve.Oid.FriendlyName);
}
#else
#if NETFRAMEWORK
var blob = key.Export(CngKeyBlobFormat.EccPublicBlob);
KeyBlobMagicNumber magic;
@@ -191,6 +170,27 @@ namespace Renci.SshNet.Security
default:
throw new SshException("Unexpected Curve Magic: " + magic);
}
#else
var parameter = Ecdsa.ExportParameters(false);
qx = parameter.Q.X;
qy = parameter.Q.Y;
switch (parameter.Curve.Oid.FriendlyName)
{
case "ECDSA_P256":
case "nistP256":
curve = Encoding.ASCII.GetBytes("nistp256");
break;
case "ECDSA_P384":
case "nistP384":
curve = Encoding.ASCII.GetBytes("nistp384");
break;
case "ECDSA_P521":
case "nistP521":
curve = Encoding.ASCII.GetBytes("nistp521");
break;
default:
throw new SshException("Unexpected Curve Name: " + parameter.Curve.Oid.FriendlyName);
}
#endif
// Make ECPoint from x and y
// Prepend 04 (uncompressed format) + qx-bytes + qy-bytes
@@ -212,6 +212,11 @@ namespace Renci.SshNet.Security
}
}
/// <summary>
/// Gets the PrivateKey Bytes
/// </summary>
public byte[] PrivateKey { get; private set; }
/// <summary>
/// Gets ECDsa Object
/// </summary>
@@ -278,29 +283,7 @@ namespace Renci.SshNet.Security
private void Import(string curve_oid, byte[] publickey, byte[] privatekey)
{
#if NETSTANDARD2_0
var curve = ECCurve.CreateFromValue(curve_oid);
var parameter = new ECParameters
{
Curve = curve
};
// ECPoint as BigInteger(2)
var cord_size = (publickey.Length - 1) / 2;
var qx = new byte[cord_size];
Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length);
var qy = new byte[cord_size];
Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length);
parameter.Q.X = qx;
parameter.Q.Y = qy;
if (privatekey != null)
parameter.D = privatekey.TrimLeadingZeros().Pad(cord_size);
Ecdsa = ECDsa.Create(parameter);
#else
#if NETFRAMEWORK
var curve_magic = KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC;
switch (GetCurveName(curve_oid))
{
@@ -335,7 +318,10 @@ namespace Renci.SshNet.Security
Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length);
if (privatekey != null)
{
privatekey = privatekey.Pad(cord_size);
PrivateKey = privatekey;
}
int headerSize = Marshal.SizeOf(typeof(BCRYPT_ECCKEY_BLOB));
int blobSize = headerSize + qx.Length + qy.Length;
@@ -355,6 +341,31 @@ namespace Renci.SshNet.Security
key = CngKey.Import(blob, privatekey == null ? CngKeyBlobFormat.EccPublicBlob : CngKeyBlobFormat.EccPrivateBlob);
Ecdsa = new ECDsaCng(key);
#else
var curve = ECCurve.CreateFromValue(curve_oid);
var parameter = new ECParameters
{
Curve = curve
};
// ECPoint as BigInteger(2)
var cord_size = (publickey.Length - 1) / 2;
var qx = new byte[cord_size];
Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length);
var qy = new byte[cord_size];
Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length);
parameter.Q.X = qx;
parameter.Q.Y = qy;
if (privatekey != null)
{
parameter.D = privatekey.TrimLeadingZeros().Pad(cord_size);
PrivateKey = parameter.D;
}
Ecdsa = ECDsa.Create(parameter);
#endif
}
@@ -36,6 +36,11 @@ namespace Renci.SshNet.Security
/// </value>
public abstract int KeyLength { get; }
/// <summary>
/// Gets the Key Comment
/// </summary>
public string Comment { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="Key"/> class.
/// </summary>
+6 -6
View File
@@ -208,7 +208,7 @@ namespace Renci.SshNet
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid. <para>-or-</para> <paramref name="username"/> is nu<b>null</b>ll or contains only whitespace characters.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
public SftpClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
: this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true)
{
}
@@ -221,7 +221,7 @@ namespace Renci.SshNet
/// <param name="keyFiles">Authentication private key file(s) .</param>
/// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <b>null</b>.</exception>
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid. <para>-or-</para> <paramref name="username"/> is <b>null</b> or contains only whitespace characters.</exception>
public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles)
public SftpClient(string host, string username, params IPrivateKeySource[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
@@ -712,13 +712,13 @@ namespace Renci.SshNet
// using SSH_FXP_REALPATH is not an alternative as the SFTP specification has not always
// been clear on how the server should respond when the specified path is not present on
// the server:
//
//
// SSH 1 to 4:
// No mention of how the server should respond if the path is not present on the server.
//
// SSH 5:
// The server SHOULD fail the request if the path is not present on the server.
//
//
// SSH 6:
// Draft 06: The server SHOULD fail the request if the path is not present on the server.
// Draft 07 to 13: The server MUST NOT fail the request if the path does not exist.
@@ -747,7 +747,7 @@ namespace Renci.SshNet
/// <exception cref="ArgumentException"><paramref name="path" /> is <b>null</b> or contains only whitespace characters.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>///
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>///
/// <exception cref="SshException">A SSH error where <see cref="Exception.Message" /> is the message from the remote host.</exception>
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
/// <remarks>
@@ -2400,4 +2400,4 @@ namespace Renci.SshNet
}
}
}
}
}
+3 -3
View File
@@ -104,7 +104,7 @@ namespace Renci.SshNet
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="IPEndPoint.MinPort"/> and <see cref="IPEndPoint.MaxPort"/>.</exception>
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
public SshClient(string host, int port, string username, params IPrivateKeySource[] keyFiles)
: this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true)
{
}
@@ -121,7 +121,7 @@ namespace Renci.SshNet
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is <c>null</c> or contains only whitespace characters.</exception>
public SshClient(string host, string username, params PrivateKeyFile[] keyFiles)
public SshClient(string host, string username, params IPrivateKeySource[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
@@ -507,4 +507,4 @@ namespace Renci.SshNet
throw new SshConnectionException("Client not connected.");
}
}
}
}