diff --git a/src/main/java/net/schmizz/sshj/userauth/keyprovider/PKCS5KeyFile.java b/src/main/java/net/schmizz/sshj/userauth/keyprovider/PKCS5KeyFile.java index e7aec425..2964ecbb 100644 --- a/src/main/java/net/schmizz/sshj/userauth/keyprovider/PKCS5KeyFile.java +++ b/src/main/java/net/schmizz/sshj/userauth/keyprovider/PKCS5KeyFile.java @@ -50,10 +50,31 @@ public class PKCS5KeyFile } } + /** + * Indicates a format issue with PKCS5 data + */ + public static class FormatException + extends IOException { + + FormatException(String msg) { + super(msg); + } + } + + /** + * Indicates a problem decrypting the data + */ + public static class DecryptException + extends IOException { + + DecryptException(String msg) { + super(msg); + } + } + protected PasswordFinder pwdf; protected Resource resource; protected KeyPair kp; - protected KeyType type; protected byte[] data; @@ -114,6 +135,7 @@ public class PKCS5KeyFile protected KeyPair readKeyPair() throws IOException { + BufferedReader reader = new BufferedReader(resource.getReader()); try { String line = null; @@ -152,19 +174,16 @@ public class PKCS5KeyFile String algorithm = line.substring(10,ptr); if ("DES-EDE3-CBC".equals(algorithm)) { cipher = new TripleDESCBC(); - iv = Arrays.copyOfRange(DatatypeConverter.parseHexBinary(line.substring(ptr+1)), 0, cipher.getIVSize()); } else if ("AES-128-CBC".equals(algorithm)) { cipher = new AES128CBC(); - iv = Arrays.copyOfRange(DatatypeConverter.parseHexBinary(line.substring(ptr+1)), 0, cipher.getIVSize()); } else if ("AES-192-CBC".equals(algorithm)) { cipher = new AES192CBC(); - iv = Arrays.copyOfRange(Base64.decode(line.substring(ptr+1)), 0, cipher.getIVSize()); } else if ("AES-256-CBC".equals(algorithm)) { cipher = new AES256CBC(); - iv = Arrays.copyOfRange(Base64.decode(line.substring(ptr+1)), 0, cipher.getIVSize()); } else { throw new FormatException("Not a supported algorithm: " + algorithm); } + iv = Arrays.copyOfRange(DatatypeConverter.parseHexBinary(line.substring(ptr+1)), 0, Math.min(cipher.getIVSize(), 8)); } } else if (line.length() > 0) { sb.append(line); @@ -242,7 +261,7 @@ public class PKCS5KeyFile md5.update(tmp, 0, tmp.length); } md5.update(passphrase, 0, passphrase.length); - md5.update(iv, 0, iv.length > 8 ? 8 : iv.length); + md5.update(iv, 0, iv.length); tmp = md5.digest(); System.arraycopy(tmp, 0, hn, i, tmp.length); i += tmp.length; @@ -260,24 +279,6 @@ public class PKCS5KeyFile throw new DecryptException("Decryption failed"); } - /** - * Indicates a format issue with PKCS5 data - */ - public static class FormatException extends IOException { - FormatException(String msg) { - super(msg); - } - } - - /** - * Indicates a problem decrypting the data - */ - public static class DecryptException extends IOException { - DecryptException(String msg) { - super(msg); - } - } - class ASN1Data { static final byte MAGIC = (byte)0x30; diff --git a/src/test/java/net/schmizz/sshj/keyprovider/PKCS5KeyFileTest.java b/src/test/java/net/schmizz/sshj/keyprovider/PKCS5KeyFileTest.java index 952365e2..08d23dea 100644 --- a/src/test/java/net/schmizz/sshj/keyprovider/PKCS5KeyFileTest.java +++ b/src/test/java/net/schmizz/sshj/keyprovider/PKCS5KeyFileTest.java @@ -56,4 +56,39 @@ public class PKCS5KeyFileTest { assertEquals(rsa.getType(), KeyType.RSA); } + final char[] correctPassphrase = "test_passphrase".toCharArray(); + final char[] incorrectPassphrase = new char[]{' '}; + + static final String g = "23b0484f5ad9cba2b3dba7129419fbec7f8c014e22d3b19de4ebbca20d0ebd2e9f5225dabdd48de75f87e3193377fb1072c08433f82f6e6e581a319d4fc7d283cdcd2ae2000fe572c0a800fd47b7590d6a6afe3df54aedd57696c6538029daebf11d9e277edc0c7e905e237d3b9e6a6f674d83da5cc0131ac0be2e55ac69730e"; + static final String p = "92b746cf7c0e9ea35fd9b09b0c3dbdfde453468984698ff168fefef3f0457d29bcf81c88830ac1099223d00745423e44cdef66f4cdc3fad1d95ce2868b3e885c1d518c9fcda597d5c373f05f6f323553f60bd992404183dab41d82ab6d3b3ecf2dfc3c136fa67c4312ec0b7bbac77a634e1eb5dd9a62efd0ddab477d0b49c0b9"; + static final String q = "96a05e07b9e52d6f1137d11d5d270b568b94162f"; + static final String x = "8981aebb71c60b5951f0ab3ed1a00b5307742f43"; + static final String y = "7e845aada202d31004c52ab170cbe62ce9a962b9f4acbc67a57f62eb090a67b3faa53d38050f87b2b66ddf1185472f27842c3e3e58d025f9148a28f49ebdfb6efefee8ee10fe84a2d56535dddb301dfee15538108639e8a0ec7aa237ddb999f35b6a5c6b875052998233374163ad031f974d29c2631394436ae186b418348193"; + + final PasswordFinder givesOn3rdTry = new PasswordFinder() { + int triesLeft = 3; + + @Override + public char[] reqPassword(Resource resource) { + if (triesLeft == 0) + return correctPassphrase; + else { + triesLeft--; + return incorrectPassphrase; + } + } + + @Override + public boolean shouldRetry(Resource resource) { + return triesLeft >= 0; + } + }; + + @Test + public void retries() + throws IOException, GeneralSecurityException { + FileKeyProvider dsa = new PKCS5KeyFile(); + dsa.init(new File("src/test/resources/id_dsa"), givesOn3rdTry); + assertEquals(KeyUtil.newDSAPrivateKey(x, p, q, g), dsa.getPrivate()); + } }