From d4b02d452b2733005b572ba248d79cc97df8ce2b Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Sun, 18 Apr 2021 18:49:01 +0200 Subject: [PATCH] OPENSSH KeyReader for more keys (#614) * OPENSSH KeyReader for more keys Add support to parse OpenSSH Keys with ECDSA 256/384/521 and RSA. https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key Change-Id: Iaa9cce0f2522e5fee377a82cb252f81f0b7cc563 * Fix ED25519Key KeyLength * Fix ED25519 PubKey-auth LeadingZeros of BigInteger-Conversion have to be removed before sending the Key. --- README.md | 2 +- .../Classes/PrivateKeyFileTest.cs | 98 ++++++++++++++-- .../Data/Key.OPENSSH.ECDSA.Encrypted.txt | 9 ++ .../Data/Key.OPENSSH.ECDSA.txt | 9 ++ .../Data/Key.OPENSSH.ECDSA384.Encrypted.txt | 11 ++ .../Data/Key.OPENSSH.ECDSA384.txt | 10 ++ .../Data/Key.OPENSSH.ECDSA521.Encrypted.txt | 12 ++ .../Data/Key.OPENSSH.ECDSA521.txt | 12 ++ .../Data/Key.OPENSSH.RSA.Encrypted.txt | 28 +++++ .../Data/Key.OPENSSH.RSA.txt | 27 +++++ .../Renci.SshNet.Tests.csproj | 8 ++ src/Renci.SshNet/PrivateKeyFile.cs | 111 ++++++++++++------ .../Security/Cryptography/ED25519Key.cs | 4 +- .../Security/Cryptography/RsaKey.cs | 9 ++ src/Renci.SshNet/Security/KeyHostAlgorithm.cs | 7 +- 15 files changed, 309 insertions(+), 48 deletions(-) mode change 100644 => 100755 README.md create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.txt diff --git a/README.md b/README.md old mode 100644 new mode 100755 index b44ae97bb..242c6dc6a --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ the missing test once you figure things out. 🤓 * RSA in OpenSSL PEM and ssh.com format * DSA in OpenSSL PEM and ssh.com format * ECDSA 256/384/521 in OpenSSL PEM format -* ED25519 in OpenSSH key format +* ECDSA 256/384/521, ED25519 and RSA in OpenSSH key format Private keys can be encrypted using one of the following cipher methods: * DES-EDE3-CBC diff --git a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs index 99434d71a..130e9922d 100644 --- a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs +++ b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs @@ -545,13 +545,10 @@ public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsShar } } - /// - /// A test for opening an openssh v1 keyfile where there is no passphrase. - /// [TestMethod()] [Owner("bhalbright")] [TestCategory("PrivateKey")] - public void TestOpenSshV1KeyFileNoPassphrase() + public void Test_PrivateKey_OPENSSH_ED25519() { using (var stream = GetData("Key.OPENSSH.ED25519.txt")) { @@ -559,13 +556,10 @@ public void TestOpenSshV1KeyFileNoPassphrase() } } - /// - /// A test for opening an openssh v1 keyfile where there is a passphrase. - /// [TestMethod()] [Owner("bhalbright")] [TestCategory("PrivateKey")] - public void TestOpenSshV1KeyFileWithPassphrase() + public void Test_PrivateKey_OPENSSH_ED25519_ENCRYPTED() { using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.txt")) { @@ -573,6 +567,94 @@ public void TestOpenSshV1KeyFileWithPassphrase() } } + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_RSA() + { + using (var stream = GetData("Key.OPENSSH.RSA.txt")) + { + new PrivateKeyFile(stream); + } + } + + [TestMethod()] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_OPENSSH_RSA_ENCRYPTED() + { + using (var stream = GetData("Key.OPENSSH.RSA.Encrypted.txt")) + { + 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"); + } + } + private void SaveStreamToFile(Stream stream, string fileName) { var buffer = new byte[4000]; diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.Encrypted.txt new file mode 100644 index 000000000..ab76db4a2 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.Encrypted.txt @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCPSPglZ3 +w/7DmCJxYohONLAAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz +dHAyNTYAAABBBK6YY2NwwLDSMnJTD+a4OfitCDuG/MnY/AstPgh54xMrZF6Qr0U1H6kRMK +Y6JJsj31CI97qDYrnTA00Sx5Jy6ywAAACwq4qisorVCP6yvrmf/fcPacX4+FVEmrHNn3fW +TiYsat7oKoItqTiDaHkIloSX93ue3fzcKXpGPR/qnpu4SezkhL9Uk6ntiwO4coB/kbEnjk +IFY6ZK0HENRXkdIuDG9qmoB0wjVPJ6L9e5RWZwiCPvNI2O60bpKOUs+tUSah1W7eTWy5Ss +ttdTgmwqS84c5+uitK1DJh2jsDqfdGm7h1XpDJsRmIEXxTVu/EdtD0hZ/x4= +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.txt new file mode 100644 index 000000000..94f16bcbd --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.txt @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSP3ZTb37LFvSmKweu03A5s/cwcw3+3 +jL1LqQK6D929xY1J2J6S91LXOBpBfz4l+8Ng7sWhu9P/hF/wmB2QRygrAAAAqMq583bKuf +N2AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/dlNvfssW9KYrB +67TcDmz9zBzDf7eMvUupAroP3b3FjUnYnpL3Utc4GkF/PiX7w2DuxaG70/+EX/CYHZBHKC +sAAAAhALVqID3K/N7IazKNbhrg09r7rLLtjy81RLV+VDxloQnxAAAAC3NyaW5rZXNATkVP +AQIDBA== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.Encrypted.txt new file mode 100644 index 000000000..500654500 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.Encrypted.txt @@ -0,0 +1,11 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAMZphcrt +UKJMlSabtzt2GdAAAAEAAAAAEAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlz +dHAzODQAAABhBFL5NEL9uRhgkF2q+8m58EvtZq4mDGgcVEzafPRuNIn1018m9KuqNpOQ6d ++435n+MRYThe4MUdijSIDuopX2i14Z35oKZ9x2LsV+RxQczjmbnoWZdvgcvdOo6jiJdY7X +JwAAAOBvXOaTq8vPRy7y5BBzr26QAYouJfGprYOqpywiIAZaICu0FJ8EXmmen6310CTG6Z +CZ4VhC5MWCWRYTaOnPNn8FvGqo2bxEqWZmyZfVvv1Z35MtSAZEfwgfXaOZKJ/lPKsRndg5 +okpqNU1aG2u+4J7eZ7QyCD/1RCCEL5wwVcrDeuMkTDPpnJc1NEGz8HbfcZ5xZavrz6Wa9t +tX7pFICqK9IIeOGMJ2WRXR6sQGyag+jNn9KmsIya7hkNJVeZeY2GKAk2s/0vxfYx9RFD55 +ewB34oHyTdxAQT3L+FZT6XfRHw== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.txt new file mode 100644 index 000000000..e5e6e06a9 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.txt @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQRTP1DMXoHgW+RX+S/NxUEElou1cmLD +6CEiR+zpzaGG6mzl6LhUY+Z3f2M3u4u7tcM8TgB/jiHbnI9TaeN5QK4HX1D9DXkH5RhfnL +frm3kCTNoCFKd0Wa/QAvKrlNKiRi8AAADYlABjHJQAYxwAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEEUz9QzF6B4FvkV/kvzcVBBJaLtXJiw+ghIkfs6c2hhu +ps5ei4VGPmd39jN7uLu7XDPE4Af44h25yPU2njeUCuB19Q/Q15B+UYX5y365t5AkzaAhSn +dFmv0ALyq5TSokYvAAAAMAXLUgK32yWzUrpeLzpdFB2/eiNnkxQlu5OneTPukKcZYclfgo +jv0YHK5LCvAtF8lwAAAAtzcmlua2VzQE5FTwECAwQF +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.Encrypted.txt new file mode 100644 index 000000000..da831cf9c --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.Encrypted.txt @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBWjIyzbM +MQ3UPE8BiQ0n4LAAAAEAAAAAEAAACsAAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlz +dHA1MjEAAACFBAH9BVM6bRhbELtgdMGsin5lM42R2EWoT+6Akakl5rQy2tHHLIYGLEfaqI ++0iUo2V6MxEf9w0hVz6SEsF+yDgyrYPQCIieaB1oBvIl+PZmL1XsuAXs2uMRsNJb4myGU/ +DiekxqzIPa0LMrBZ4xmErcn5Gazkw1EA0B3HoaW5wj+geI/efQAAARDi+GGTYH1T+5Dd8N +EVCiL+J7fm8uP8yAcvQNh3JBYIf1g/GZ0hJDuA47fcTzXEfTGZLGWdgaE8cxIUICpjBoak +EpNS1HyhqYZAt2J8o/14t2GbXczJfoQLOIQl2S1zXQ9shof12odu9DGcBhSAz9hswlndBE +d99uCz/ymzwQ0i2Pp+urUXo7+YXB6HMh9YTMeGQAiDJFO3NPDqDczfUECtTUkQMhy8r06m +hAp/oZ6K1KBbZzdc0xyqDePKAqqyHnN4FD7Wfv11SWoOhlUcEVg2ZvNj/O+CsoWzMpN+dt +DPKZHmH/kegWKBsdtAC9f5Hg3b2oQAK0pKghms1+/J9iilnIMwv80CPzGdv0YAG9Vx5w== +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.txt new file mode 100644 index 000000000..0ad09eb24 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.txt @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAa7p4WVga+08qu160BrdzKyRNJMQC +eGhFluNdq73/uaW3qlqoEiaNtc5uB7HHyjCWQTmfzrSRLRZ7YBwUancwyh4Aq6gBgGsXVz +wNvY3kxRDlGrSfMmsHlXz41dgw9wm1fBcf97niexRQ/xGlhkyf7IYlQ/s5BpXF2lS9l0H5 +hBippRgAAAEI/9prf//aa38AAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEAGu6eFlYGvtPKrtetAa3cyskTSTEAnhoRZbjXau9/7mlt6paqBImjbXObgexx8ow +lkE5n860kS0We2AcFGp3MMoeAKuoAYBrF1c8Db2N5MUQ5Rq0nzJrB5V8+NXYMPcJtXwXH/ +e54nsUUP8RpYZMn+yGJUP7OQaVxdpUvZdB+YQYqaUYAAAAQXwQnI20tNxwLKHPMDmumblD +b0sBqW5Y9248L//x4sWFrkjk6k1LcZno9KLqz8+tIFMJ5sji+axRoUZCXb3cIPzPAAAAC3 +NyaW5rZXNATkVP +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.Encrypted.txt new file mode 100644 index 000000000..b9eed58be --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.Encrypted.txt @@ -0,0 +1,28 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCoWhCSaG +psKT80oPIOAlqJAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCxOfnmxC1g +mmX18LCBG/X73BoCXQEBEAJz3V9w0FMTgUBebK3fkfOMLNzWn5aMR608wQHOEPYhHffCJf +dJR6lUWLHZz5+EZRfM9oHpysMtToYQoGtb9xM7D5J3lnWZgSea2R7xSeqpClN5nQUMGu8y +2d2S3g9o1vrTdeu71u09QFOx2AXBPUmCjpCuBlNyYEEQMfRMtQ6PDbPdvLM1uylbQqB+/6 +jMsCyEFvlLit9GcZ7ItKQN+jsZNmP+f7ytVXLsZTjPLd5mWWrf6T1T1Xt9DBoLXnMrmDiC +f/EXiTYonIO6B0FHvNUCrDzZ7rxIebqePLes1Q2yoUqmC8g+cww3AAADwNRNGSnB7cRpgU +BxdyC0ofj0hUONXjmoT+OGPph9lgZMUnzcon9Z1bpsJuMoRXL14Cdbds7YPmw1YB94Uc+S +8QexLJG0wGel2yvzJhU+QFsLeVRS4tayERFXGCoVpu7RunEYy+hvaiX5CD+luEkiarfj9I +N8+9QUMhDYkELwWBV4rde18Vr8m1P1FuFgqikY0TfSKUGCkvjl4FvDxrxqsewaEkkzwRTI +PhOFCCM5jBPWE+uWVcwKoidvAqcNbmwIzDNZGwXtrAvYYzZa62C/MNLHuFU1weuJiM8sYa +6iKrk681BrrpGcSEZEXd41CFY3BWlIDTozrWn03xFlIpeLG2YMPcuYqFhR/41BJfa+fW5B +Ei0SuUx2xjdRiamqpPku4H6ulkjl0KlFCr976Y2V1JZMQh7bd0huubmf4P4poBk6ZgGpSf +snhcv1HjCVkvfA2yhOcXogzK2HOZgDS5sdSb/kUGURdjlj6ccSzc3OYaHAy9gZXj8Q58pA +4KrXTDlCJ9BTR8PIND54j6gMKu5ijX0TP9nJf/hG9GXx+Xss8T3xdPxdNBapPCcuxGZGJN +H+KFqrpmZYHm0evqFPS7BCUp2VvID6SMgrTYiH0IIbMHLStfdNchtn3EudMbW9vRhxg3Do +npT7Px2JYp87PNoHg2eOx0yGy9r81n2+Wi7SpGCWD8MFfxqd4JIQ8+zjrIRAA1q53uuSUh +m/hlmJWEjQWmcBw5bKrOU0CfGGoT3o6HWYRQ9d5+kKeoS+dOINxxf80G5b7vOrE3PbFxT3 +W8zwRd90Msr3LXgPaN0V4RJeBX38e0EvVbArL2MgSs/BC5aID0N0Sqiu+13AqqNYxj6RH2 +FA7FN+BBa16fvdi5h5kNnZUrQUKOAImjEE494O8uGKQImviGqB5PY6DJqHPTtn7RSwFx9E +rR7nbAZPTucIN/OIfURxTedhROk0PXjWnwpjuz+UpaMRWqgWTv3bLOuqorqMLibAFLRQEQ +6pR0wbmTpTfEW1jNmAohxB4N14YdSfhThzkCAgpQW6UCLc83y3EDzQFi5862a+2ixULKhK +220tZRk2GU7OFAPRpgQ/sxptGqZbNdOV80wk1MgykoFkoptRkm7bfJcdLHZnP7E6yU0ssP +rCbQlfD0/dD2QE/7HqxHsipNNuEagULjK6WUYXkpx1Siq2vecjZw8dNp7EBh+KlujEm+Dr +R7KFdFCw8DUwrzXwfMIogeRVbW8H0/fQEqsX5oPLTEOnNBjzf8pHush7CCrprbo0ZK3xFp +Vr3LUCoA== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.txt new file mode 100644 index 000000000..893001ebb --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.txt @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEA7W7Oigi7Hj1msa2l8HimLPzagVmE/CseMKCRxPMTtWTvoZdMt7hf +kthWA7h20a7oSSeH2t7FqeOpVNGFYvRV3BVWsKZJMRcdJiTCZAjH38rYk90XvZ3mKrKN/3 +fcQsh4OiPnWqT6HSqg14oiV8UPtUwnE65skWSxvxt+ELlVpCqG5vE2O/GNDKQE7FzYt6vQ +ihMFSABBqjvau0mjX002KYiCMr6vgl8XYkDA4n5+JyQSQxznnfrL93ZoaqujRP2bT5UhXg +bzr8HFwuqQGvURECZTTI8Biv/6tOIn9x2hEaN6vxDLtXc99E2d6NW6cdriwaGFJ4jgVWDE +5mU006XdPQAAA8jzN6zf8zes3wAAAAdzc2gtcnNhAAABAQDtbs6KCLsePWaxraXweKYs/N +qBWYT8Kx4woJHE8xO1ZO+hl0y3uF+S2FYDuHbRruhJJ4fa3sWp46lU0YVi9FXcFVawpkkx +Fx0mJMJkCMffytiT3Re9neYqso3/d9xCyHg6I+dapPodKqDXiiJXxQ+1TCcTrmyRZLG/G3 +4QuVWkKobm8TY78Y0MpATsXNi3q9CKEwVIAEGqO9q7SaNfTTYpiIIyvq+CXxdiQMDifn4n +JBJDHOed+sv3dmhqq6NE/ZtPlSFeBvOvwcXC6pAa9REQJlNMjwGK//q04if3HaERo3q/EM +u1dz30TZ3o1bpx2uLBoYUniOBVYMTmZTTTpd09AAAAAwEAAQAAAQEA6Xgq+gppzOt9nrts +z5Ajf1tHlSesn7XaYuCRVgPb3mOZSuEW3BUdTa0Sr2fk1nzSBpUrfqnN3idyK2g3bD1sbB +RDgUKR+AaNcCN3TpxfxgyVeJhQLvEkEdovzQRUfwrXRfxmE3jkRGfVbvxylrG8p35xcmXy +del46r2i8dj8gIY3tKp0RMvZ4ZlNbhWHPd5OxyHWL9e3gbOSyIyjQgTKZezhzErS/X/KJ/ +XYqzyBAqNqZ2Rg1kKiHRlHS6KEI2tyFwYfh+Rb6L9xch1SqOtQhTWirmxS25dpGD2jgalX +eyiw8PmuqTiWCqUmUMx6MdF3tFsirr54K4QA9kqMeaRLtQAAAIEAsUQT0Uhq7l0ugTd4Y9 +89bH6eW0fol21/m7B5zkJQepNadUPTs188uvv4inW8n2O3RCanXWHZfCJ1AsR/MEEW2C6Q +DtvqKXHbzfWQlCYSVxB17CjURKa8fNaIAk98zgmNNwO53NBleyrUhPgvm3xt7ACgpzXY5R +wNJL8/a0WOmgwAAACBAP508Op6wWPAwn1JfBZuqQtjcfnJeN4NkYQBdybn0vVu5UdyqSQL +a8hlAzROhA+qJvrlsZgM9h8CyLTyuim8likZHocwO13zBTdVaQ8c2lJvf2uXTIXNZHseS7 +ITkfBiO1hSB4z8RDkOr35mGfdbyJIFAwFZF4Xs8WnQF+vHEadrAAAAgQDu329eCwVFf9g0 +zNHfZu31p0WtErcsRv57fq+UoPtov8nxUF71oOWe5KSGnGtMICI31kBtPhUbvfOmuqNrgJ +BjgjbPQmi0xSAE5L3QuEKRNjlaE3/WadKBwzhJDtauuYk1ifkrdAVp67XyQ5puyuGgVaQB +NPbrxA9g1IbyeL4/9wAAAAtzcmlua2VzQE5FTwECAwQFBg== +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index d7ee81e9a..8ba9f3077 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -35,6 +35,14 @@ + + + + + + + + diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs index 31fac7db4..1424134f6 100644 --- a/src/Renci.SshNet/PrivateKeyFile.cs +++ b/src/Renci.SshNet/PrivateKeyFile.cs @@ -26,13 +26,13 @@ namespace Renci.SshNet /// The following private keys are supported: /// /// - /// RSA in OpenSSL PEM and ssh.com format + /// RSA in OpenSSL PEM, ssh.com and OpenSSH key format /// /// /// DSA in OpenSSL PEM and ssh.com format /// /// - /// ECDSA 256/384/521 in OpenSSL PEM format + /// ECDSA 256/384/521 in OpenSSL PEM and OpenSSH key format /// /// /// ED25519 in OpenSSH key format @@ -373,7 +373,7 @@ private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, strin /// the key file data (i.e. base64 encoded data between the header/footer) /// passphrase or null if there isn't one /// - private ED25519Key ParseOpenSshV1Key(byte [] keyFileData, string passPhrase) + private Key ParseOpenSshV1Key(byte[] keyFileData, string passPhrase) { var keyReader = new SshDataReader(keyFileData); @@ -387,8 +387,10 @@ private ED25519Key ParseOpenSshV1Key(byte [] keyFileData, string passPhrase) //cipher will be "aes256-cbc" if using a passphrase, "none" otherwise var cipherName = keyReader.ReadString(Encoding.UTF8); + //key derivation function (kdf): bcrypt or nothing var kdfName = keyReader.ReadString(Encoding.UTF8); + //kdf options length: 24 if passphrase, 0 if no passphrase var kdfOptionsLen = (int)keyReader.ReadUInt32(); byte[] salt = null; @@ -407,29 +409,21 @@ private ED25519Key ParseOpenSshV1Key(byte [] keyFileData, string passPhrase) throw new SshException("At this time only one public key in the openssh key is supported."); } - //length of first public key section - keyReader.ReadUInt32(); - var keyType = keyReader.ReadString(Encoding.UTF8); - if(keyType != "ssh-ed25519") - { - throw new SshException("openssh key type: " + keyType + " is not supported"); - } - - //read public key - var publicKeyLength = (int)keyReader.ReadUInt32(); //32 - var publicKey = keyReader.ReadBytes(publicKeyLength); + //read public key in ssh-format, but we dont need it + keyReader.ReadString(Encoding.UTF8); //possibly encrypted private key var privateKeyLength = (int)keyReader.ReadUInt32(); var privateKeyBytes = keyReader.ReadBytes(privateKeyLength); //decrypt private key if necessary - if (cipherName == "aes256-cbc") + if (cipherName != "none") { if (string.IsNullOrEmpty(passPhrase)) { throw new SshPassPhraseNullOrEmptyException("Private key is encrypted but passphrase is empty."); } + if (string.IsNullOrEmpty(kdfName) || kdfName != "bcrypt") { throw new SshException("kdf " + kdfName + " is not supported for openssh key file"); @@ -445,14 +439,21 @@ private ED25519Key ParseOpenSshV1Key(byte [] keyFileData, string passPhrase) byte[] iv = new byte[16]; Array.Copy(keyiv, 32, iv, 0, 16); - //now that we have the key/iv, use a cipher to decrypt the bytes - var cipher = new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); + AesCipher cipher; + switch (cipherName) + { + case "aes256-cbc": + cipher = new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); + break; + case "aes256-ctr": + cipher = new AesCipher(key, new CtrCipherMode(iv), new PKCS7Padding()); + break; + default: + throw new SshException("Cipher '" + cipherName + "' is not supported for an OpenSSH key."); + } + privateKeyBytes = cipher.Decrypt(privateKeyBytes); } - else if (cipherName != "none") - { - throw new SshException("cipher name " + cipherName + " for openssh key file is not supported"); - } //validate private key length privateKeyLength = privateKeyBytes.Length; @@ -470,22 +471,49 @@ private ED25519Key ParseOpenSshV1Key(byte [] keyFileData, string passPhrase) int checkInt1 = (int)privateKeyReader.ReadUInt32(); int checkInt2 = (int)privateKeyReader.ReadUInt32(); if (checkInt1 != checkInt2) - { - throw new SshException("The checkints differed, the openssh key was not correctly decoded."); - } + throw new SshException("The random check bytes of the OpenSSH key do not match (" + checkInt1 + " <->" + checkInt2 + ")."); - //key type, we already know it is ssh-ed25519 - privateKeyReader.ReadString(Encoding.UTF8); + //key type + var keyType = privateKeyReader.ReadString(Encoding.UTF8); - //public key length/bytes (again) - var publicKeyLength2 = (int)privateKeyReader.ReadUInt32(); - privateKeyReader.ReadBytes(publicKeyLength2); - - //length of private and public key (64) - privateKeyReader.ReadUInt32(); - var unencryptedPrivateKey = privateKeyReader.ReadBytes(32); - //public key (again) - privateKeyReader.ReadBytes(32); + Key parsedKey; + byte[] publicKey; + byte[] unencryptedPrivateKey; + switch (keyType) + { + case "ssh-ed25519": + //public key + publicKey = privateKeyReader.ReadBignum2(); + //private key + unencryptedPrivateKey = privateKeyReader.ReadBignum2(); + parsedKey = new ED25519Key(publicKey.Reverse(), unencryptedPrivateKey); + break; +#if FEATURE_ECDSA + case "ecdsa-sha2-nistp256": + case "ecdsa-sha2-nistp384": + case "ecdsa-sha2-nistp521": + // curve + int len = (int)privateKeyReader.ReadUInt32(); + var curve = Encoding.ASCII.GetString(privateKeyReader.ReadBytes(len)); + //public key + publicKey = privateKeyReader.ReadBignum2(); + //private key + unencryptedPrivateKey = privateKeyReader.ReadBignum2(); + parsedKey = new EcdsaKey(curve, publicKey, unencryptedPrivateKey.TrimLeadingZeros()); + break; +#endif + case "ssh-rsa": + var modulus = privateKeyReader.ReadBignum(); //n + var exponent = privateKeyReader.ReadBignum(); //e + var d = privateKeyReader.ReadBignum(); //d + var inverseQ = privateKeyReader.ReadBignum(); // iqmp + var p = privateKeyReader.ReadBignum(); //p + var q = privateKeyReader.ReadBignum(); //q + parsedKey = new RsaKey(modulus, exponent, d, p, q, inverseQ); + break; + default: + 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); @@ -501,7 +529,7 @@ private ED25519Key ParseOpenSshV1Key(byte [] keyFileData, string passPhrase) } } - return new ED25519Key(publicKey.Reverse(), unencryptedPrivateKey); + return parsedKey; } #region IDisposable Members @@ -594,6 +622,17 @@ public BigInteger ReadBigIntWithBits() return new BigInteger(bytesArray.Reverse()); } + public BigInteger ReadBignum() + { + return new BigInteger(ReadBignum2().Reverse()); + } + + public byte[] ReadBignum2() + { + var length = (int)base.ReadUInt32(); + return base.ReadBytes(length); + } + protected override void LoadData() { } diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs index 83ac1c8cd..deb4b1181 100644 --- a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs +++ b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs @@ -33,7 +33,7 @@ public override BigInteger[] Public { get { - return new BigInteger[] { publicKey.ToBigInteger() }; + return new BigInteger[] { publicKey.ToBigInteger2() }; } set { @@ -51,7 +51,7 @@ public override int KeyLength { get { - return PublicKey.Length; + return PublicKey.Length * 8; } } diff --git a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs index 6d8a55534..ba5b17464 100644 --- a/src/Renci.SshNet/Security/Cryptography/RsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/RsaKey.cs @@ -9,6 +9,15 @@ namespace Renci.SshNet.Security /// public class RsaKey : Key, IDisposable { + + /// + /// Gets the Key String. + /// + public override string ToString() + { + return "ssh-rsa"; + } + /// /// Gets the modulus. /// diff --git a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs index ca94fa70e..11717197b 100644 --- a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs +++ b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Renci.SshNet.Common; +using Renci.SshNet.Security.Chaos.NaCl; namespace Renci.SshNet.Security { @@ -101,7 +102,11 @@ private set _keys = new List(value.Length); foreach (var key in value) { - _keys.Add(key.ToByteArray().Reverse()); + var keyData = key.ToByteArray().Reverse(); + if (Name == "ssh-ed25519") + keyData = keyData.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + + _keys.Add(keyData); } } }