소스 검색

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.
Stefan Rinkes 3 년 전
부모
커밋
bc99ada7da

+ 15 - 0
src/Renci.SshNet/IPrivateKeySource.cs

@@ -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
src/Renci.SshNet/NetConfClient.cs

@@ -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
             }
         }
     }
-}
+}

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

@@ -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
src/Renci.SshNet/PrivateKeyConnectionInfo.cs

@@ -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
src/Renci.SshNet/PrivateKeyFile.cs

@@ -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
src/Renci.SshNet/ScpClient.cs

@@ -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.");
         }
     }
-}
+}

+ 9 - 0
src/Renci.SshNet/Security/Cryptography/ED25519Key.cs

@@ -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>

+ 6 - 6
src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs

@@ -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;

+ 71 - 60
src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs

@@ -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,45 +57,45 @@ namespace Renci.SshNet.Security
             return string.Format("ecdsa-sha2-nistp{0}", KeyLength);
         }
 
-#if NETSTANDARD2_0
+#if NETFRAMEWORK
         /// <summary>
         /// Gets the HashAlgorithm to use
         /// </summary>
-        public HashAlgorithmName HashAlgorithm
+        public CngAlgorithm HashAlgorithm
         {
             get
             {
-                switch (KeyLength)
+                switch (Ecdsa.KeySize)
                 {
                     case 256:
-                        return HashAlgorithmName.SHA256;
+                        return CngAlgorithm.Sha256;
                     case 384:
-                        return HashAlgorithmName.SHA384;
+                        return CngAlgorithm.Sha384;
                     case 521:
-                        return HashAlgorithmName.SHA512;
+                        return CngAlgorithm.Sha512;
+                    default:
+                        throw new SshException("Unknown KeySize: " + Ecdsa.KeySize);
                 }
-                return HashAlgorithmName.SHA256;
             }
         }
 #else
         /// <summary>
         /// Gets the HashAlgorithm to use
         /// </summary>
-        public CngAlgorithm HashAlgorithm
+        public HashAlgorithmName HashAlgorithm
         {
             get
             {
-                switch (Ecdsa.KeySize)
+                switch (KeyLength)
                 {
                     case 256:
-                        return CngAlgorithm.Sha256;
+                        return HashAlgorithmName.SHA256;
                     case 384:
-                        return CngAlgorithm.Sha384;
+                        return HashAlgorithmName.SHA384;
                     case 521:
-                        return CngAlgorithm.Sha512;
-                    default:
-                        throw new SshException("Unknown KeySize: " + Ecdsa.KeySize);
+                        return HashAlgorithmName.SHA512;
                 }
+                return HashAlgorithmName.SHA256;
             }
         }
 #endif
@@ -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
         }
 

+ 5 - 0
src/Renci.SshNet/Security/Cryptography/Key.cs

@@ -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
src/Renci.SshNet/SftpClient.cs

@@ -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
src/Renci.SshNet/SshClient.cs

@@ -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.");
         }
     }
-}
+}