From c98ad22a7a6a32e1c2249e253806efee2a9f73f0 Mon Sep 17 00:00:00 2001 From: Billy Keyes Date: Mon, 17 Aug 2015 15:05:02 -0700 Subject: [PATCH] Skip blank lines when detecting key formats Some private keys found in the wild start with a blank line, which breaks SSHJ. OpenSSH utilities worked as expected with these key files. Also add some basic tests for key formats. --- .../userauth/keyprovider/KeyProviderUtil.java | 56 +++++++++++-------- .../sshj/keyprovider/KeyProviderUtilTest.java | 40 +++++++++++++ src/test/resources/keyformats/openssh | 15 +++++ src/test/resources/keyformats/openssh.pub | 1 + src/test/resources/keyformats/pkcs8 | 15 +++++ src/test/resources/keyformats/pkcs8-blanks | 18 ++++++ src/test/resources/keyformats/putty | 19 +++++++ 7 files changed, 140 insertions(+), 24 deletions(-) create mode 100644 src/test/java/net/schmizz/sshj/keyprovider/KeyProviderUtilTest.java create mode 100644 src/test/resources/keyformats/openssh create mode 100644 src/test/resources/keyformats/openssh.pub create mode 100644 src/test/resources/keyformats/pkcs8 create mode 100644 src/test/resources/keyformats/pkcs8-blanks create mode 100644 src/test/resources/keyformats/putty diff --git a/src/main/java/net/schmizz/sshj/userauth/keyprovider/KeyProviderUtil.java b/src/main/java/net/schmizz/sshj/userauth/keyprovider/KeyProviderUtil.java index eb857fb8..f7f41d3e 100644 --- a/src/main/java/net/schmizz/sshj/userauth/keyprovider/KeyProviderUtil.java +++ b/src/main/java/net/schmizz/sshj/userauth/keyprovider/KeyProviderUtil.java @@ -51,8 +51,7 @@ public class KeyProviderUtil { * @return name of the key file format * @throws java.io.IOException */ - public static KeyFormat detectKeyFileFormat(String privateKey, - boolean separatePubKey) + public static KeyFormat detectKeyFileFormat(String privateKey, boolean separatePubKey) throws IOException { return detectKeyFileFormat(new StringReader(privateKey), separatePubKey); } @@ -67,35 +66,44 @@ public class KeyProviderUtil { * @return name of the key file format * @throws java.io.IOException */ - public static KeyFormat detectKeyFileFormat(Reader privateKey, - boolean separatePubKey) + public static KeyFormat detectKeyFileFormat(Reader privateKey, boolean separatePubKey) throws IOException { - BufferedReader br = new BufferedReader(privateKey); - final String firstLine; - try { - firstLine = br.readLine(); - } - finally { - IOUtils.closeQuietly(br); - } - if(firstLine == null) { + String header = readHeader(privateKey); + if (header == null) { throw new IOException("Empty file"); } - if(firstLine.startsWith("-----BEGIN") && firstLine.endsWith("PRIVATE KEY-----")) { - if(separatePubKey) - // Can delay asking for password since have unencrypted pubkey - { - return KeyFormat.OpenSSH; + return keyFormatFromHeader(header, separatePubKey); + } + + private static String readHeader(Reader privateKey) throws IOException { + BufferedReader br = new BufferedReader(privateKey); + try { + String header; + while ((header = br.readLine()) != null) { + header = header.trim(); + if (!header.isEmpty()) { + break; + } } - else - // More general - { + return header; + } finally { + IOUtils.closeQuietly(br); + } + } + + private static KeyFormat keyFormatFromHeader(String header, boolean separatePubKey) { + if (header.startsWith("-----BEGIN") && header.endsWith("PRIVATE KEY-----")) { + if (separatePubKey) { + // Can delay asking for password since have unencrypted pubkey + return KeyFormat.OpenSSH; + } else { + // More general return KeyFormat.PKCS8; } - } - if(firstLine.startsWith("PuTTY-User-Key-File-")) { + } else if (header.startsWith("PuTTY-User-Key-File-")) { return KeyFormat.PuTTY; + } else { + return KeyFormat.Unknown; } - return KeyFormat.Unknown; } } diff --git a/src/test/java/net/schmizz/sshj/keyprovider/KeyProviderUtilTest.java b/src/test/java/net/schmizz/sshj/keyprovider/KeyProviderUtilTest.java new file mode 100644 index 00000000..a599657c --- /dev/null +++ b/src/test/java/net/schmizz/sshj/keyprovider/KeyProviderUtilTest.java @@ -0,0 +1,40 @@ +package net.schmizz.sshj.keyprovider; + +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import net.schmizz.sshj.userauth.keyprovider.KeyFormat; +import net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil; + +public class KeyProviderUtilTest { + + private static final File ROOT = new File("src/test/resources/keyformats"); + + @Test + public void testOpenSsh() throws IOException { + KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "openssh")); + assertEquals(KeyFormat.OpenSSH, format); + } + + @Test + public void testPkcs8() throws IOException { + KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs8")); + assertEquals(KeyFormat.PKCS8, format); + } + + @Test + public void testPutty() throws IOException { + KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "putty")); + assertEquals(KeyFormat.PuTTY, format); + } + + @Test + public void testSkipsBlankLines() throws IOException { + KeyFormat format = KeyProviderUtil.detectKeyFileFormat(new File(ROOT, "pkcs8-blanks")); + assertEquals(KeyFormat.PKCS8, format); + } +} diff --git a/src/test/resources/keyformats/openssh b/src/test/resources/keyformats/openssh new file mode 100644 index 00000000..221453d8 --- /dev/null +++ b/src/test/resources/keyformats/openssh @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCm2IJ9gWDkPTlQ37NNUB0za5mCsQ8bi++8fyEqw7wl8ZNBh3qt +TcnL+m+NZfQjUC0BXic7PcMLVm4A3ID2IAZQM+axfq9aL4huWerm4ua6tvdt4gQK +oL1+8JFmdFvFw5pWW/NZHtkIprbVf7KtYrU27WmMhXruN071UzqLsw08cwIDAQAB +AoGAHQ7cOyuLSnT3RISRX8eyLkBxLffUX8HRcQzbI+2PGTSnpuQHk6NWn/Xv87pr ++LKABBr3zjOFgrX81p2QwEz3jDxNXzbOeZzhuvGXCX5GocuEO4n5EhDvXRDF4uht +uvVV5FsQv/sTOR0PNo1nELiAA8k3NYDxraB83q7wtsmErtECQQDYWMnq8mwRe49d +jIXNKJeNiuLUYxO3CLI/vx279gDKlKrt677trr1e7JZqm/DapEWG511tw3cW63gQ ++qxtgkw1AkEAxW0UeaNaJd7DApqwGAcS1JkygCKwzQ4ns/Co15qUgMkqCkmQU9AU +/zQpt2+BjdYVe50r/nr8K1KYwrBsyndrBwJBALe90N+FvFqswfoFmq2/R9eimTsg +WmIdNKYHPs2gBNQIp5MhoSpkOdkgvi8U+d33nkUQwryyQbZpjbN98mufOfECQEML +eBiW0NZrf+4yefqu7EYmgG/jWAdK91C0OaJ+bFAQAKbdtJXB5F+GZ2RUCbsRKNqB +1Z7mRRyxQA9dupRHWaECQQCM9bbCtfGesgvZlhBavlWavu8iCvJlAbGdf5QMlFQE +kABmZg84Fy3NUFCD+RXCuatb4Oo9P/WPIbjYiC4p0hLJ +-----END RSA PRIVATE KEY----- diff --git a/src/test/resources/keyformats/openssh.pub b/src/test/resources/keyformats/openssh.pub new file mode 100644 index 00000000..39c3f9a0 --- /dev/null +++ b/src/test/resources/keyformats/openssh.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCm2IJ9gWDkPTlQ37NNUB0za5mCsQ8bi++8fyEqw7wl8ZNBh3qtTcnL+m+NZfQjUC0BXic7PcMLVm4A3ID2IAZQM+axfq9aL4huWerm4ua6tvdt4gQKoL1+8JFmdFvFw5pWW/NZHtkIprbVf7KtYrU27WmMhXruN071UzqLsw08cw== diff --git a/src/test/resources/keyformats/pkcs8 b/src/test/resources/keyformats/pkcs8 new file mode 100644 index 00000000..221453d8 --- /dev/null +++ b/src/test/resources/keyformats/pkcs8 @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCm2IJ9gWDkPTlQ37NNUB0za5mCsQ8bi++8fyEqw7wl8ZNBh3qt +TcnL+m+NZfQjUC0BXic7PcMLVm4A3ID2IAZQM+axfq9aL4huWerm4ua6tvdt4gQK +oL1+8JFmdFvFw5pWW/NZHtkIprbVf7KtYrU27WmMhXruN071UzqLsw08cwIDAQAB +AoGAHQ7cOyuLSnT3RISRX8eyLkBxLffUX8HRcQzbI+2PGTSnpuQHk6NWn/Xv87pr ++LKABBr3zjOFgrX81p2QwEz3jDxNXzbOeZzhuvGXCX5GocuEO4n5EhDvXRDF4uht +uvVV5FsQv/sTOR0PNo1nELiAA8k3NYDxraB83q7wtsmErtECQQDYWMnq8mwRe49d +jIXNKJeNiuLUYxO3CLI/vx279gDKlKrt677trr1e7JZqm/DapEWG511tw3cW63gQ ++qxtgkw1AkEAxW0UeaNaJd7DApqwGAcS1JkygCKwzQ4ns/Co15qUgMkqCkmQU9AU +/zQpt2+BjdYVe50r/nr8K1KYwrBsyndrBwJBALe90N+FvFqswfoFmq2/R9eimTsg +WmIdNKYHPs2gBNQIp5MhoSpkOdkgvi8U+d33nkUQwryyQbZpjbN98mufOfECQEML +eBiW0NZrf+4yefqu7EYmgG/jWAdK91C0OaJ+bFAQAKbdtJXB5F+GZ2RUCbsRKNqB +1Z7mRRyxQA9dupRHWaECQQCM9bbCtfGesgvZlhBavlWavu8iCvJlAbGdf5QMlFQE +kABmZg84Fy3NUFCD+RXCuatb4Oo9P/WPIbjYiC4p0hLJ +-----END RSA PRIVATE KEY----- diff --git a/src/test/resources/keyformats/pkcs8-blanks b/src/test/resources/keyformats/pkcs8-blanks new file mode 100644 index 00000000..b49fac32 --- /dev/null +++ b/src/test/resources/keyformats/pkcs8-blanks @@ -0,0 +1,18 @@ + + + +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCm2IJ9gWDkPTlQ37NNUB0za5mCsQ8bi++8fyEqw7wl8ZNBh3qt +TcnL+m+NZfQjUC0BXic7PcMLVm4A3ID2IAZQM+axfq9aL4huWerm4ua6tvdt4gQK +oL1+8JFmdFvFw5pWW/NZHtkIprbVf7KtYrU27WmMhXruN071UzqLsw08cwIDAQAB +AoGAHQ7cOyuLSnT3RISRX8eyLkBxLffUX8HRcQzbI+2PGTSnpuQHk6NWn/Xv87pr ++LKABBr3zjOFgrX81p2QwEz3jDxNXzbOeZzhuvGXCX5GocuEO4n5EhDvXRDF4uht +uvVV5FsQv/sTOR0PNo1nELiAA8k3NYDxraB83q7wtsmErtECQQDYWMnq8mwRe49d +jIXNKJeNiuLUYxO3CLI/vx279gDKlKrt677trr1e7JZqm/DapEWG511tw3cW63gQ ++qxtgkw1AkEAxW0UeaNaJd7DApqwGAcS1JkygCKwzQ4ns/Co15qUgMkqCkmQU9AU +/zQpt2+BjdYVe50r/nr8K1KYwrBsyndrBwJBALe90N+FvFqswfoFmq2/R9eimTsg +WmIdNKYHPs2gBNQIp5MhoSpkOdkgvi8U+d33nkUQwryyQbZpjbN98mufOfECQEML +eBiW0NZrf+4yefqu7EYmgG/jWAdK91C0OaJ+bFAQAKbdtJXB5F+GZ2RUCbsRKNqB +1Z7mRRyxQA9dupRHWaECQQCM9bbCtfGesgvZlhBavlWavu8iCvJlAbGdf5QMlFQE +kABmZg84Fy3NUFCD+RXCuatb4Oo9P/WPIbjYiC4p0hLJ +-----END RSA PRIVATE KEY----- diff --git a/src/test/resources/keyformats/putty b/src/test/resources/keyformats/putty new file mode 100644 index 00000000..1e002b48 --- /dev/null +++ b/src/test/resources/keyformats/putty @@ -0,0 +1,19 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: none +Comment: rsa-key-20150817 +Public-Lines: 4 +AAAAB3NzaC1yc2EAAAABJQAAAIEAhoCAakZdrCNrHNpJHRIED4movsposAlk4ZPz +n/IFGkiIZOWRF/p4Sq+CaLigamwpe3f2/vYxCwtF3oCcJMQdn6CLYytHgrC2pRWa +bNBBClSO4jzWMlBRBZnzXBHKJ04kviqIybEvrN2weg5ArSOK7297DU2id+kDxSJz +QleZ+9c= +Private-Lines: 8 +AAAAgBItCm858PuWFWTDjVb0mMPUVRLdFRDerMSJnXZ6pr5c1ClPdHjcqHjLnABQ +TQd2Znh3/siCIk2ZvVVrU17qEdcZyivntbi8NuehqcVQr2ny8pc+sXvxXv2inoA3 +4mIVqjhIoljYf1VWgDXUxUsGU6QZdMfkDCEuoxL9QM7RdFXNAAAAQQDk01miHj3M +LozpMovCU2oKDFLsmOrXe7jroff2sM4BX5Iwym4N197O7ZIs5E52K0bWIDH9SouX +RTseqiHsPxexAAAAQQCWeZFwG7qm8cyAGSzsDsN35cmzgkvlFl4uIuJ9jh0S2Yt5 +2couD/AoQVmHqGaxwYPc+q24yvbFbjCxtlkqMTYHAAAAQQCcpItltKrhNW2svG2P +NuOi0TQmMZQigMOYQx4wd/8G2n+nr0Qsi4wuq/qccgKViVRyobB7nxQqoJdGAI90 +ECNG +Private-MAC: 4f200e5f5b766351b996d92f3b733b671b9ff957 +