浏览代码

Added support for ssh.com private keys.
Fixes issue #1987.

Gert Driesen 11 年之前
父节点
当前提交
43836a5b50

+ 1 - 0
Renci.SshClient/Build/nuget/SSH.NET.nuspec

@@ -16,6 +16,7 @@
 New Features:
 
     * Improved accuracy of IsConnected on .NET
+    * Added support for ssh.com (SSH-2) private keys (issue #1987)
 
 Fixes:
 

+ 15 - 1
Renci.SshClient/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj

@@ -775,10 +775,24 @@
       <Link>Renci.SshNet.snk</Link>
     </None>
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt">
+      <Link>Data\Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt</Link>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.SSH2.DSA.txt">
+      <Link>Data\Key.SSH2.DSA.txt</Link>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt">
+      <Link>Data\Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt</Link>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Renci.SshNet.Tests\Data\Key.SSH2.RSA.txt">
+      <Link>Data\Key.SSH2.RSA.txt</Link>
+    </EmbeddedResource>
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
     <VisualStudio>
-      <UserProperties ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
+      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 107 - 0
Renci.SshClient/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs

@@ -146,6 +146,113 @@ namespace Renci.SshNet.Tests.Classes
             }
         }
 
+        [TestMethod]
+        [Owner("drieseng")]
+        [TestCategory("PrivateKey")]
+        public void Test_PrivateKey_SSH2_DSA()
+        {
+            using (var stream = this.GetData("Key.SSH2.DSA.txt"))
+            {
+                new PrivateKeyFile(stream);
+            }
+        }
+
+        [TestMethod]
+        [Owner("drieseng")]
+        [TestCategory("PrivateKey")]
+        public void Test_PrivateKey_SSH2_RSA()
+        {
+            using (var stream = this.GetData("Key.SSH2.RSA.txt"))
+            {
+                new PrivateKeyFile(stream);
+            }
+        }
+
+        [TestMethod]
+        [Owner("drieseng")]
+        [TestCategory("PrivateKey")]
+        public void Test_PrivateKey_SSH2_Encrypted_DSA_DES_CBC()
+        {
+            using (var stream = this.GetData("Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt"))
+            {
+                new PrivateKeyFile(stream, "12345");
+            }
+        }
+
+        [TestMethod]
+        [Owner("drieseng")]
+        [TestCategory("PrivateKey")]
+        public void Test_PrivateKey_SSH2_Encrypted_RSA_DES_CBC()
+        {
+            using (var stream = this.GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
+            {
+                new PrivateKeyFile(stream, "12345");
+            }
+        }
+
+        [TestMethod]
+        [Owner("drieseng")]
+        [TestCategory("PrivateKey")]
+        public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshExceptionWhenPassphraseIsWrong()
+        {
+            using (var stream = this.GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
+            {
+                try
+                {
+                    new PrivateKeyFile(stream, "34567");
+                    Assert.Fail();
+                }
+                catch (SshException ex)
+                {
+                    Assert.IsInstanceOfType(ex, typeof(SshException));
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Invalid passphrase.", ex.Message);
+                }
+            }
+        }
+
+        [TestMethod]
+        [Owner("drieseng")]
+        [TestCategory("PrivateKey")]
+        public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPassphraseIsNull()
+        {
+            using (var stream = this.GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
+            {
+                try
+                {
+                    new PrivateKeyFile(stream, null);
+                    Assert.Fail();
+                }
+                catch (SshPassPhraseNullOrEmptyException ex)
+                {
+                    Assert.IsInstanceOfType(ex, typeof(SshPassPhraseNullOrEmptyException));
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Private key is encrypted but passphrase is empty.", ex.Message);
+                }
+            }
+        }
+
+        [TestMethod]
+        [Owner("drieseng")]
+        [TestCategory("PrivateKey")]
+        public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPassphraseIsEmpty()
+        {
+            using (var stream = this.GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
+            {
+                try
+                {
+                    new PrivateKeyFile(stream, string.Empty);
+                    Assert.Fail();
+                }
+                catch (SshPassPhraseNullOrEmptyException ex)
+                {
+                    Assert.IsInstanceOfType(ex, typeof(SshPassPhraseNullOrEmptyException));
+                    Assert.IsNull(ex.InnerException);
+                    Assert.AreEqual("Private key is encrypted but passphrase is empty.", ex.Message);
+                }
+            }
+        }
+
         [TestMethod]
         [Owner("olegkap")]
         [TestCategory("PrivateKey")]

+ 12 - 0
Renci.SshClient/Renci.SshNet.Tests/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt

@@ -0,0 +1,12 @@
+---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----
+P2/56wAAAgoAAAAmZGwtbW9kcHtzaWdue2RzYS1uaXN0LXNoYTF9LGRoe3BsYWlufX0AAA
+AIM2Rlcy1jYmMAAAHIxrKV3QJkISlRHox4JYLlWJG0ccdEfMmDF3IXW+up6nUQZeWyOS9/
+csXyclUtJ/adA0qkpH5hcaIIOr1jQgvPxjMy7o3I5WJ/MvKB7omSDYG82wKZAaZQeSCaqY
+Cn1DYHz6Xou5BIle37f0y71tq3d7YFCh33BjbCM8KHsar3TgRf3oegaawZyZuLnnZRy0L+
+xdsdqgntc4fyhpmCmfwIYhWwD2R9P56FXTw59VuE0r/LxxLJW8Cq2R8i7bN0lb2ezb3qnU
+SykSJ5PYvoW6e7HIZ3GEr9d+wDhqp0SKWHl+shed3e5xi1NF7fRR23bZ1xlAk3FdrBrH1b
+aWYW4JFzdVc33Pg5oHLe+q5NQ3vQpMxXlN2IG9cdAigWKNJMdvLuxiYi+6lzNIGAH+yb01
+56ksXYgUs65Gqnj0GMYVmf//8NN0gRKXl5dT57LyuU42jRePwANMyO/n9QOi9OKeea8Tku
+f6ZMRiHQLDiZx2ShEXEhHRwmv42jt2xjuHNMbY0dZTKZEkFMUGVFVbkl5MEmN8Fj59rHJn
+0adWRPLH5smwM8WXbaYt+E0r628UNZbedXCcjZ5c5egpxCBxRrSJqnK1f0l899fulB
+---- END SSH2 ENCRYPTED PRIVATE KEY ----

+ 12 - 0
Renci.SshClient/Renci.SshNet.Tests/Data/Key.SSH2.DSA.txt

@@ -0,0 +1,12 @@
+---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----
+P2/56wAAAgIAAAAmZGwtbW9kcHtzaWdue2RzYS1uaXN0LXNoYTF9LGRoe3BsYWlufX0AAA
+AEbm9uZQAAAcQAAAHAAAAAAAAABACPIMhxXIZFYZD4sJIZIxQ6jesNpxVk6lcvbC4xbN/K
+BFizKQMPfnNd9nq1KbQITUOiWVAFWRpZb/91eYZLEg8goXvVmOSH7DpYel9HXGQsASsrBM
+0JT8jBagLW3Pbr+6FFjcwX7xG7Veljf1ZnJl3+3cqNxAVV2wRXT5dTSgu1vQAAA/sEtj09
+ugx/Tdl6bo7X6mX17hcgVgIxcYj5VNONg2k6IHmRFriLviYaS68mIB4SG3jmvvxbXAGqR1
+bWBUrv90n0wpxxcuuNoCFylJQyuqUkzSsUHb0WMcncZ/tBQt+NJnRB1Zp9sw8n20ocpg3W
+VPdaXTtc4pk83NYB6ywG6UFPvgAAAKDY1+bTt7s2iNmYoBE4C9hdWRCyeQAAA/0X+De5dw
+o33LMl9W8IvA4dY8Q1wshdycAGJzhy+qYF9dCcwD1Pg+4EbPjYPmzJopsVrK97v9QhxyYc
+XMr/iHhngGwd9nYNzzSKx665vkSjzyeJWpeQ+fvNV3CLItP01ypbUreM+s+Vz1wor5joLK
+cDS4X0oQ0RIVZNEHnekuLuFgAAAJ4j+lpXSvEZexhbiACKenUniZ/Qkg==
+---- END SSH2 ENCRYPTED PRIVATE KEY ----

+ 23 - 0
Renci.SshClient/Renci.SshNet.Tests/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt

@@ -0,0 +1,23 @@
+---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----
+Comment: "imported-openssh-key"
+P2/56wAAA/MAAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS
+1wa2NzMXYyLW9hZXB9fQAAAAgzZGVzLWNiYwAAA6A6Wt6IR5cz0PCsSJDOjcs3MdQscfdN
+p54qdp1DPUltmJJCwxU31izkP+mdRoSBgTwAd2jIvq+xdxhe8ZYqFtf6QvbIsZMWC/giRU
+mDcHLnGxWgWDY9adh5AscC7FOTEXhME/uNkGdYp3x5lmmC8uHC6cKt4RIqq7elBnOlnNvQ
+Mmr0mJluLsqsu4pr57bM0TJ37s7ov33zLuINO40kZ1Yc9A0NebWoGZPB8Wq2UCVskAXM6v
+/cJQRNu73Hn77vbLhzVEKnOokRdzDncKRMkItwJV0iz4/kuAcf6OdgwmPQ2e/lz7NR+5yI
+a37bKeKKnOR4JGAIjSoGu/Skc8GKfcEPAnnZ2xYJ15fNPy5fBbga8SAQGJ3UCx1cPsaaTA
+pt0fJ5si9X26V8XrK+T0mRBZPjj0s/eb2jOAzgWPCur4N3hqijyQO7RoTjh1jcL7ONT9zv
+HJjqqaTXMqQGPeHEpSamyTyXMDwMZGRAaGQDM1awWX3jq1uNgGW/3/0TV/rq69nVso3dVv
+DiSPGdK+GgZzVWsfZv/5X17YrQLEkr0s6Mpsm5L3j4NYtcppMR2bV3vsA9eCWb99lnvhIt
+bR12MY21S/o1CwAtSYZojylcxZcWVaoZ20OTzrzTjsCOLxyIiQAzdKFG49F+YCUIJYM8ne
+NA+jW34x7u1L+NKVHMPUx7gcYEE6RapEOQRx1p+9HV7efGNIOmpUCZJZ4itHFUSOxi6eao
+FT8J2si0edrDMnWDdRBhOMQNkSt9zViWpIvLLx4g9jih6XEgAOsRTyD9GnpM6HBspd+Jsy
+XfSgbfBsJpnHOpjx/mTiQ0AkfkBeOvgfGRWyjjSfJaxojnE59JHo1HR6+9XFuWGXjYbIYo
+gpcLnJ1JY/to21ABQgsNCxH1VocPNGTKodcDpf91bYKqS4peri9CKSXsRP3Y4K7cffwXAH
+kWFvvfw65pZHx9xTJfgv986jfn1JCDrO/6UOwnyrL2X8g9wrCEQ1R0QNQ82mx+cXu4atVO
+oA2Hjj5OegwHScGTZ9LaC+CRyW847QrJF7sPDcYxbEUTvgSSGZ+dymAjFFNMlBdMAEmtNB
+P0dBUFObJsP0vTyZrxnueal6zleQY1ox5csO3XF2nKpO+KjfuVd6qM6/7sBpqpA1bycM2O
+8k6Wq2hSEhPVovarOb878P1Uiuzr4eb/eIt7tC1SHrcfAoBIYnUYVzUB5g4WIIy3qxEaGm
+36ETbq3U+vVk9BatC4
+---- END SSH2 ENCRYPTED PRIVATE KEY ----

+ 23 - 0
Renci.SshClient/Renci.SshNet.Tests/Data/Key.SSH2.RSA.txt

@@ -0,0 +1,23 @@
+---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----
+Comment: "imported-openssh-key"
+P2/56wAAA+4AAAA3aWYtbW9kbntzaWdue3JzYS1wa2NzMS1zaGExfSxlbmNyeXB0e3JzYS
+1wa2NzMXYyLW9hZXB9fQAAAARub25lAAADnwAAA5sAAAARAQABAAAIAJoF62h2fdR02ncN
+StzmpuKmvnAdQdbkAlzek1LBuXrJX2wsF6XcqsLSAvAHhvy71q6TK9GBQOgpGH3vn+Tjo2
+POKZL/2u9sN52yanbQw0oFCDOHBy2xCRw/+NoOofcVtR59eeSKMy79gmkGQnsCliqdznXX
+GUuZSxHIQQZhfcCbYuGHCjA6B8dsFeVzrzwju1lr3eEtuChCpgnWHevbyasOYWTj3/Mt/W
+x0XEAQQB9jutL4VpzNzsE1D47dLfVWAflYppCcxFH8LS7Sq5NEUFs0Vje1LJK6XhrfieH8
+YjQfpsikbpcTIebIHP74W3RjBhcKt7eBoHhcSj8vU5pPj3EAAAgA1cGcbXOCOZ54AvKgLu
+7ibJfchKnHAOTG1BxMkxy/0pCoTPSDAXiHyNxjN4qjk8nqg1dfsPdjXCRpW+eBsQMhJZ1P
+qIh4WB7fkK7+Wm4twhqQhLQ7uPwjnNflNYqRizeYJ5wUofuK2I30DgiNQjw2hV6Tos8ikv
+VA7GCedjnScRYbUauV8rrXC6+Xny6mD2F3BGE/CQdYTZXNs7mKIkCpb9ziJXMM7juCfBSO
+qQSwZy7XeEKuMqzP9diHCByqqCcippHc0RZTLONCTWa7ndoqAdhZpkk/pDqmFTEWEJuUPN
+8jWCqukS4RzxXEE3lbzZbInYmsldlXIeboQoFJM+4WXwAABAC84bFrW0L1E2GtgVp2uZoi
+Ve1ynoXmr7nHfCmbVJS5nEZBYTKT+62/VdlDiLm7QlnQqpkxyYw+d3pM3rvHLAb/6ntoIs
+l5HJG4WWDl0vQVwjQR5CQpRrtLQiDIhMAxBdKGS5Ql9RpdQKvhFI+7STzttqcDCerOEVQG
+SlT3vL52UAAABADgL9iwue1u7D6IrL26PwudbJSZ3lPt3YkZl25LDIVrGU5U0xPH3o+FQP
+GTkQv5qrc43I96Jc+P9d7A2EyE6j+ArtSWKq5OrVUAhg16O49rc2ve5v4w53fOH0P20BHv
+k1QxIEg5vHAJwgfZMJ68ub6+73gtkSHJ6W78T2Qpo+dHtwAABAD0Fti2uUlPoiDmhOIZUF
+7qs3pxkC+OjZcWBgi7IsEjiBxZVmYH3iD722gmbA4om6ulAxs7F2q10l0YXY1ds250FOxQ
+swT2var18A0MjOWa7RgHChBqiHxi+krLW6F9UvllWYmb4bz4E1T3rfE4OB0h5UW2oHD4Hn
+smPA/t9MmWmQ==
+---- END SSH2 ENCRYPTED PRIVATE KEY ----

+ 10 - 0
Renci.SshClient/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -342,6 +342,16 @@
       <Link>Renci.SshNet.snk</Link>
     </None>
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Data\Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Data\Key.SSH2.RSA.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Data\Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt" />
+    <EmbeddedResource Include="Data\Key.SSH2.DSA.txt" />
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 56 - 30
Renci.SshClient/Renci.SshNet/PrivateKeyFile.cs

@@ -16,17 +16,46 @@ using System.Diagnostics.CodeAnalysis;
 namespace Renci.SshNet
 {
     /// <summary>
-    /// Represents private key information
+    /// Represents private key information.
     /// </summary>
     /// <example>
     ///     <code source="..\..\Renci.SshNet.Tests\Data\Key.RSA.txt" language="Text" title="Private RSA key example" />
     /// </example>
+    /// <remarks>
+    /// <para>
+    /// Supports RSA and DSA private key in both <c>OpenSSH</c> and <c>ssh.com</c> format.
+    /// </para>
+    /// <para>
+    /// The following encryption algorithms are supported:
+    /// <list type="bullet">
+    ///     <item>
+    ///         <description>DES-EDE3-CBC</description>
+    ///     </item>
+    ///     <item>
+    ///         <description>DES-EDE3-CFB</description>
+    ///     </item>
+    ///     <item>
+    ///         <description>DES-CBC</description>
+    ///     </item>
+    ///     <item>
+    ///         <description>AES-128-CBC</description>
+    ///     </item>
+    ///     <item>
+    ///         <description>AES-192-CBC</description>
+    ///     </item>
+    ///     <item>
+    ///         <description>AES-256-CBC</description>
+    ///     </item>
+    /// </list>
+    /// </para>
+    /// </remarks>
     public class PrivateKeyFile : 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 SILVERLIGHT
-        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)?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k<keyName> PRIVATE KEY *-+", RegexOptions.Multiline);
+            RegexOptions.Multiline);
 #else
-        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)?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k<keyName> PRIVATE KEY *-+", RegexOptions.Compiled | RegexOptions.Multiline);
+            RegexOptions.Compiled | RegexOptions.Multiline);
 #endif
 
         private Key _key;
@@ -101,7 +130,7 @@ namespace Renci.SshNet
             using (var sr = new StreamReader(privateKey))
             {
                 var text = sr.ReadToEnd();
-                privateKeyMatch = _privateKeyRegex.Match(text);
+                privateKeyMatch = PrivateKeyRegex.Match(text);
             }
 
             if (!privateKeyMatch.Success)
@@ -177,7 +206,7 @@ namespace Renci.SshNet
                         throw new SshException("Invalid SSH2 private key.");
                     }
 
-                    var totalLength = reader.ReadUInt32(); //  Read total bytes length including magic number
+                    reader.ReadUInt32(); //  Read total bytes length including magic number
                     var keyType = reader.ReadString();
                     var ssh2CipherName = reader.ReadString();
                     var blobSize = (int)reader.ReadUInt32();
@@ -187,12 +216,15 @@ namespace Renci.SshNet
                     {
                         keyData = reader.ReadBytes(blobSize);
                     }
-                    //else if (ssh2CipherName == "3des-cbc")
-                    //{
-                    //    var key = GetCipherKey(passPhrase, 192 / 8);
-                    //    var ssh2Сipher = new TripleDesCipher(key, null, null);
-                    //    keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
-                    //}
+                    else if (ssh2CipherName == "3des-cbc")
+                    {
+                        if (string.IsNullOrEmpty(passPhrase))
+                            throw new SshPassPhraseNullOrEmptyException("Private key is encrypted but passphrase is empty.");
+
+                        var key = GetCipherKey(passPhrase, 192 / 8);
+                        var ssh2Сipher = new TripleDesCipher(key, new CbcCipherMode(new byte[8]), new PKCS7Padding());
+                        keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
+                    }
                     else
                     {
                         throw new SshException(string.Format("Cipher method '{0}' is not supported.", cipherName));
@@ -204,7 +236,7 @@ namespace Renci.SshNet
 
                     var decryptedLength = reader.ReadUInt32();
 
-                    if (decryptedLength + 4 != blobSize)
+                    if (decryptedLength > blobSize - 4)
                         throw new SshException("Invalid passphrase.");
                     
                     if (keyType == "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}")
@@ -245,22 +277,19 @@ namespace Renci.SshNet
 
         private static byte[] GetCipherKey(string passphrase, int length)
         {
-            List<byte> cipherKey = new List<byte>();
+            var cipherKey = new List<byte>();
 
             using (var md5 = new MD5Hash())
             {
-                byte[] passwordBytes = Encoding.UTF8.GetBytes(passphrase);
-
-                var hash = md5.ComputeHash(passwordBytes.ToArray()).AsEnumerable();
+                var passwordBytes = Encoding.UTF8.GetBytes(passphrase);
 
+                var hash = md5.ComputeHash(passwordBytes);
                 cipherKey.AddRange(hash);
 
                 while (cipherKey.Count < length)
                 {
-                    hash = passwordBytes.Concat(hash);
-
-                    hash = md5.ComputeHash(hash.ToArray());
-
+                    hash = passwordBytes.Concat(hash).ToArray();
+                    hash = md5.ComputeHash(hash);
                     cipherKey.AddRange(hash);
                 }
             }
@@ -289,25 +318,22 @@ namespace Renci.SshNet
             if (binarySalt == null)
                 throw new ArgumentNullException("binarySalt");
 
-            List<byte> cipherKey = new List<byte>();
+            var cipherKey = new List<byte>();
 
             using (var md5 = new MD5Hash())
             {
                 var passwordBytes = Encoding.UTF8.GetBytes(passPhrase);
 
-                //  Use 8 bytes binary salkt
-                var initVector = passwordBytes.Concat(binarySalt.Take(8));
-
-                var hash = md5.ComputeHash(initVector.ToArray()).AsEnumerable();
+                //  Use 8 bytes binary salt
+                var initVector = passwordBytes.Concat(binarySalt.Take(8)).ToArray();
 
+                var hash = md5.ComputeHash(initVector);
                 cipherKey.AddRange(hash);
 
                 while (cipherKey.Count < cipherInfo.KeySize / 8)
                 {
-                    hash = hash.Concat(initVector);
-
-                    hash = md5.ComputeHash(hash.ToArray());
-
+                    hash = hash.Concat(initVector).ToArray();
+                    hash = md5.ComputeHash(hash);
                     cipherKey.AddRange(hash);
                 }
             }
@@ -401,7 +427,7 @@ namespace Renci.SshNet
             {
                 var length = (int)base.ReadUInt32();
 
-                length = (int)(length + 7) / 8;
+                length = (length + 7) / 8;
 
                 var data = base.ReadBytes(length);
                 var bytesArray = new byte[data.Length + 1];