Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Binary file modified binary/Breaker.jar
Binary file not shown.
18 changes: 10 additions & 8 deletions src/se/bes/br/Breaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand All @@ -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}
Expand All @@ -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);

Expand All @@ -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;
}
Expand Down
86 changes: 86 additions & 0 deletions src/se/bes/br/PasswordChecker.java
Original file line number Diff line number Diff line change
@@ -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<password.length; i++) {
passwdBytes[j++] = (byte)(password[i] >> 8);
passwdBytes[j++] = (byte)password[i];
}
md.update(passwdBytes);
md.update(MAGIC_STRING);
return md;
}
}