patternjavaMinor
Brute-force Vigenere Cipher using multiple threads
Viewed 0 times
forcethreadsvigenereusingmultiplebrutecipher
Problem
I'm trying to brute force a Vigenere Cipher knowing only that the key length is
Run.java (main class)
And the class that handles the brute-force attack
B
5+ characters and that the decrypted text has the word Hello, Andi. So I wrote this small Java program that tries to brute-force the key through a given numbers of threads and nested while loops.Run.java (main class)
package vignere;
import java.io.IOException;
public class Run {
public static void main(String[] args) throws IOException {
//number of threads you have and encrypted text file
Brute brute = new Brute(4, "41259.txt.enc");
Thread t1 = new Thread() {
@Override
public void run() {
try {
brute.bruteF_thread(1);
} catch (IOException e) {
e.printStackTrace();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
brute.bruteF_thread(2);
} catch (IOException e) {
e.printStackTrace();
}
}
};
Thread t3 = new Thread() {
@Override
public void run() {
try {
brute.bruteF_thread(3);
} catch (IOException e) {
e.printStackTrace();
}
}
};
Thread t4 = new Thread() {
@Override
public void run() {
try {
brute.bruteF_thread(4);
} catch (IOException e) {
e.printStackTrace();
}
}
};
t1.start();
t2.start();
t3.start();
t4.start();
try {
t1.join();
t2.join();
t3.join();
t4.join();
} catch (Exception e) {
e.printStackTrace();
}
}
}And the class that handles the brute-force attack
B
Solution
I'll have a try to decompose the problem into responsibilities:
1
2
3
4
Composing the elements
Now we have all responsibilities together we can couple them. I was really astonished as I found a proper application for streams. This is a JAVA 8 style solution:
First we create a stream of keys:
After that we load the encodedText:
Now we can decode the encoded text with every key in the stream and get a stream of decoded texts:
After that we create a predicate that defines that we are only interested in potentially successful decoded texts:
Finally we define a filter on the stream and collect our results:
All together:
Language sugar
One thing I did not adress until now: parallelism. You used threads to increase brute force speed. I want to suggest a different approach. As we are already using the JAVA 8 Streaming API we have only one thing to change to load our CPUs:
Change
to
Changing the key stream to a parallel stream will cause concurrency. Noticed the synchronized method in the KeyGenerator to work properly in parallel?
Finally
I did a little research as I was interested in a solution for limiting streams by a certain condition. I had to implement a mystic interface called "Spl
- You need a key generator that continiously produces unique keys of a certain key length that are used to shift characters
- You need a Decoder that will decode an encoded Text with a given key
- You need an indicator that your decoding was potentially successful.
1
public class KeyGenerator {
private int currentKeyLength;
public KeyGenerator(int minimumKeyLength) {
this.currentKeyLength = minimumKeyLength;
}
public synchronized Key next() {
// determine current key length
byte[] keyBytes = new byte[currentKeyLength];
// build next shift sequence
return new Key(keyBytes);
}
}2
public class Decoder {
private EncodedText encodedText;
private Key key;
public Decoder(EncodedText encodedText, Key key) {
this.encodedText = encodedText;
this.key = key;
}
public DecodedText decode() {
byte[] keyBytes = key.getKeyBytes();
byte[] decodedTextBytes = new byte[encodedText.getEncodedTextBytes().length];
// decode encoded text bytes with key bytes
return new DecodedText(decodedTextBytes);
}
}3
public class DecodeSuccessfulIndicator implements Predicate {
private String referenceString;
public DecodeSuccessfulIndicator(String referenceString) {
this.referenceString = referenceString;
}
@Override
public boolean test(DecodedText decodedText) {
return new String(decodedText.getDecodedTextBytes()).indexOf(referenceString) >= 0;
}
}- I introduced some wrapper objects as byte arrays are not that expressive: EncodedText, Key and DecodedText
4
public class EncodedText {
private byte[] encodedTextBytes;
public EncodedText(byte[] encodedTextBytes) {
this.encodedTextBytes = encodedTextBytes;
}
public byte[] getEncodedTextBytes() {
return encodedTextBytes;
}
}
public class Key {
private byte[] keyBytes;
public Key(byte[] keyBytes) {
this.keyBytes = keyBytes;
}
public byte[] getKeyBytes() {
return keyBytes;
}
}
public class DecodedText {
private byte[] decodedTextBytes;
public DecodedText(byte[] decodedTextBytes) {
this.decodedTextBytes = decodedTextBytes;
}
public byte[] getDecodedTextBytes() {
return decodedTextBytes;
}
}Composing the elements
Now we have all responsibilities together we can couple them. I was really astonished as I found a proper application for streams. This is a JAVA 8 style solution:
First we create a stream of keys:
Stream keyStream = Stream.generate(new KeyGenerator(4)::next);After that we load the encodedText:
EncodedText encodedText = new EncodedText(Files.readAllBytes(Paths.get("41259.txt.enc")));Now we can decode the encoded text with every key in the stream and get a stream of decoded texts:
Stream decodedTextStream = keyStream.map(key -> new Decoder(encodedText, key).decode());After that we create a predicate that defines that we are only interested in potentially successful decoded texts:
DecodeSuccessfulIndicator decodeSuccessfulIndicator = new DecodeSuccessfulIndicator("andi");Finally we define a filter on the stream and collect our results:
List collect = decodedTextStream.filter(decodeSuccessfulIndicator::test).collect(Collectors.toList());All together:
public class Run {
public static void main(String[] args) throws IOException {
Stream keyStream = Stream.generate(new KeyGenerator(4)::next);
EncodedText encodedText = new EncodedText(Files.readAllBytes(Paths.get("41259.txt.enc")));
Stream decodedTextStream = keyStream.map(key -> new Decoder(encodedText, key).decode());
DecodeSuccessfulIndicator decodeSuccessfulIndicator = new DecodeSuccessfulIndicator("andi");
List collect = decodedTextStream.filter(decodeSuccessfulIndicator::test).collect(Collectors.toList());
}
}Language sugar
One thing I did not adress until now: parallelism. You used threads to increase brute force speed. I want to suggest a different approach. As we are already using the JAVA 8 Streaming API we have only one thing to change to load our CPUs:
Change
Stream decodedTextStream = keyStream.map(key -> new Decoder(encodedText, key).decode());to
Stream decodedTextStream = keyStream.parallel().map(key -> new Decoder(encodedText, key).decode());Changing the key stream to a parallel stream will cause concurrency. Noticed the synchronized method in the KeyGenerator to work properly in parallel?
public class KeyGenerator {
...
public synchronized Key next() {
...
}
}Finally
I did a little research as I was interested in a solution for limiting streams by a certain condition. I had to implement a mystic interface called "Spl
Code Snippets
public class KeyGenerator {
private int currentKeyLength;
public KeyGenerator(int minimumKeyLength) {
this.currentKeyLength = minimumKeyLength;
}
public synchronized Key next() {
// determine current key length
byte[] keyBytes = new byte[currentKeyLength];
// build next shift sequence
return new Key(keyBytes);
}
}public class Decoder {
private EncodedText encodedText;
private Key key;
public Decoder(EncodedText encodedText, Key key) {
this.encodedText = encodedText;
this.key = key;
}
public DecodedText decode() {
byte[] keyBytes = key.getKeyBytes();
byte[] decodedTextBytes = new byte[encodedText.getEncodedTextBytes().length];
// decode encoded text bytes with key bytes
return new DecodedText(decodedTextBytes);
}
}public class DecodeSuccessfulIndicator implements Predicate<DecodedText> {
private String referenceString;
public DecodeSuccessfulIndicator(String referenceString) {
this.referenceString = referenceString;
}
@Override
public boolean test(DecodedText decodedText) {
return new String(decodedText.getDecodedTextBytes()).indexOf(referenceString) >= 0;
}
}public class EncodedText {
private byte[] encodedTextBytes;
public EncodedText(byte[] encodedTextBytes) {
this.encodedTextBytes = encodedTextBytes;
}
public byte[] getEncodedTextBytes() {
return encodedTextBytes;
}
}
public class Key {
private byte[] keyBytes;
public Key(byte[] keyBytes) {
this.keyBytes = keyBytes;
}
public byte[] getKeyBytes() {
return keyBytes;
}
}
public class DecodedText {
private byte[] decodedTextBytes;
public DecodedText(byte[] decodedTextBytes) {
this.decodedTextBytes = decodedTextBytes;
}
public byte[] getDecodedTextBytes() {
return decodedTextBytes;
}
}Stream<Key> keyStream = Stream.generate(new KeyGenerator(4)::next);Context
StackExchange Code Review Q#154124, answer score: 5
Revisions (0)
No revisions yet.