diff --git a/README.md b/README.md index 05064da..9934e23 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ Performance On Linux with a Core2Quad I got about 150000 keys / second, but on a Windows 7 machine (Also Core2Quad) I only got about 100000 keys / second. +EDIT: I believe I've managed to increase that by 2-3x. + + Proof of concept ---------------- There is a .keystore file in the test/ directory that has been prepared with the diff --git a/binary/Breaker.jar b/binary/Breaker.jar index fba0522..4a682b6 100644 Binary files a/binary/Breaker.jar and b/binary/Breaker.jar differ diff --git a/src/se/bes/br/Breaker.java b/src/se/bes/br/Breaker.java index 2596a30..e1fc1d7 100644 --- a/src/se/bes/br/Breaker.java +++ b/src/se/bes/br/Breaker.java @@ -123,7 +123,9 @@ public String getPassphrase() throws InterruptedException { long totalTime = (System.currentTimeMillis() - totalStartTime) / 1000; System.out.print("Tested " + mCounter - + " pws (" + totalTime + " s -- " + rate + " pw/s): " + + " pws (" + totalTime + " s -- " + rate + " pw/s avg: " + + (totalTime > 0 ? (mCounter / totalTime) : 0) + + "): " + new String(globalPass) + " \r"); } @@ -135,6 +137,7 @@ private class PasswordTester extends Thread { * The bytes of a {@link KeyStore} loaded into RAM. */ private ByteArrayInputStream mStream; + private int dataLength; /** * Loads a {@link KeyStore} on file into a {@link ByteArrayInputStream} @@ -147,6 +150,8 @@ public PasswordTester(String fileName) { FileInputStream fis = new FileInputStream(file); byte[] fileBytes = new byte[(int)file.length()]; + + dataLength = fileBytes.length; fis.read(fileBytes); @@ -162,19 +167,16 @@ public PasswordTester(String fileName) { */ @Override public void run() { - KeyStore ks = null; - try { - ks = KeyStore.getInstance(KeyStore.getDefaultType()); - } catch (KeyStoreException e) { - e.printStackTrace(); - } + PasswordChecker pc = new PasswordChecker(); + pc.bytes = new byte[dataLength - PasswordChecker.HASH_LENGTH]; char[] passwd = null; while(!mIsFound) { //System.out.println("Next pw"); mStream.reset(); try { passwd = mGenerator.getNextPassword(); - ks.load(mStream, passwd); + if (!pc.passwordMatches(mStream, dataLength, passwd)) + continue; } catch (Throwable t) { continue; } diff --git a/src/se/bes/br/PasswordChecker.java b/src/se/bes/br/PasswordChecker.java new file mode 100644 index 0000000..741ed07 --- /dev/null +++ b/src/se/bes/br/PasswordChecker.java @@ -0,0 +1,86 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package se.bes.br; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author mewer + */ +public class PasswordChecker { + public static final int HASH_LENGTH = 20; + private MessageDigest md = null; + public byte[] bytes = null; + private byte[] actual = new byte[HASH_LENGTH]; + private static final byte[] MAGIC_STRING; + static { + byte[] bucket = null; + try { + bucket = "Mighty Aphrodite".getBytes("UTF8"); + } catch (UnsupportedEncodingException ex) { + Logger.getLogger(PasswordChecker.class.getName()).log(Level.SEVERE, null, ex); + } + MAGIC_STRING = bucket; + } + + /** + * Checks if the given password yields a SHA digest matching the one at the + * end of the keystore stream. One instance should not have this method run + * more than once at a time; it reuses one MessageDigest for the instance. + * Also reuses several arrays in the same way. + */ + public boolean passwordMatches(InputStream stream, int length, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + DataInputStream dis; + + md = getPreKeyedHash(password); + dis = new DataInputStream(new DigestInputStream(stream, md)); + + dis.readFully(bytes); + + byte[] computed = md.digest(); + dis.readFully(actual); + for (int i = 0; i < HASH_LENGTH; i++) { + if (computed[i] != actual[i]) { + return false; + } + } + return true; + } + + /** + * To guard against tampering with the keystore, we append a keyed + * hash with a bit of whitener. + */ + private MessageDigest getPreKeyedHash(char[] password) + throws NoSuchAlgorithmException, UnsupportedEncodingException + { + int i, j; + + if (md == null) { + md = MessageDigest.getInstance("SHA"); + } else { + md.reset(); + } + byte[] passwdBytes = new byte[password.length * 2]; + for (i=0, j=0; i> 8); + passwdBytes[j++] = (byte)password[i]; + } + md.update(passwdBytes); + md.update(MAGIC_STRING); + return md; + } +} \ No newline at end of file