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);
}
}
}