Skip to content

Commit

Permalink
Breaks the Vigenere Cipher without knowing the key or even the key le…
Browse files Browse the repository at this point in the history
…ngth, change the "cipherText" variable to be the encoded string.
  • Loading branch information
Abushawish committed Sep 27, 2016
0 parents commit c43fc44
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="output" path="bin"/>
</classpath>
17 changes: 17 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Vigenere_Cipher_Decoder</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
11 changes: 11 additions & 0 deletions .settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.7
Binary file added bin/com/abushawish/VigenereMain.class
Binary file not shown.
294 changes: 294 additions & 0 deletions src/com/abushawish/VigenereMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
package com.abushawish;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Queue;

//Mohammed Abushawish
//100857775

public class VigenereMain {

public static void main(String[] args) {

String cipherText = "jauew evpe mddbcwyy fhvf ycyw af kap cezqr rgtalde oe mss"
+ " qsdm thfzo yqt wnchswd tyty hsw xekmpf ltodvyr o tl iaj twgz xauew "
+ "evll fhv leiaapei tywxsxs jnnv lk fhv lssph teel lbo vgcbl hscw gnruws "
+ "eg xerky hsw eemxy qzeyaewxsyle bp apocl mfkxc afut tyhfusl enfpmowd "
+ "petelfpv fhrm evp kqvvg ncxemnufpbek ooleo wy wrfvve pp jqdlvpr eg m "
+ "szgrzp emxzf yoxwxy whff wwss xhzr eoa lvzd plv fhzl ss dsud thyhlazeu mss "
+ "pkeeemtow sncuxqu ajuntbazp gr aebxowaem nazsgwd hrw evzjauxawm rjmsgxo we "
+ "oaucw ms dsre wkza smyae bytwmqntxd hsw niiwd oe xurjm zpuwotvw dwyuq ik lpsxwp "
+ "tf mssx ltak mssj sxsf alr eoa lvzd pfl enfpmowd brfopr eg fhvf evll fhzl hod "
+ "fat jh l ptjp s nbyu ngyrrwpg sw eazw tg lf arxty cq hdognwgtgz aew yce gr "
+ "mrgtdfdmtzhy we sncuxqu dzaucw evpjqffkp pp jqgrkoso se a cxr hsw pijmtbrmusybyu "
+ "xsdk fy xoy ae tyx soyv fhv bygejgmvge ktlt wybnv sw povl lzw zus dbdqsaqf kap "
+ "ptjps ubo bzl gnuxcgeszd runrpxs sehhpldx s chyu hgddj ufh ezqy rvnsalqd ybd "
+ "sihxaetewzf mnu twz ezq hlfmzpj mnzflzd kqt kh hccc fo cxlfy lte exh alpum sr "
+ "ssljf ffnc zpye gfho hhg xexl moo oms zgdqcaneu hy hsw qnu plzw gr tyx mocf mbfop "
+ "hsw eemxy qzeyaewxsyle aew tb masgvk wselqrj pssy ltep alr zfoe xhe we tk hvtch "
+ "ltodvyr hsw ehvxa rpnqlfipr l yderm wwvazg whc hsae mrqta lfp owmpb lk fhvr woj az "
+ "tyx qwpdp tyxj kzmxd rew gesdt sepoeazg whff wwss xhzr eoa lvzd plv rolk wsrk "
+ "sofw ekz dqgj ulr lfp kvxa we mb ffk scfje oe xyr ywhei zcchazg kbcso gr ik "
+ "gldzdqoe mzcv fa iempfpkf ie lychtmlc l ncxeutkxpg sw eazw evll fhv xoinsfifg zt "
+ "ezq yfnyu hse mfkp wxharktyh ezmn rgjhsazg kalh ngglu up rzfq ffk evzkq wyh hscw"
+ " mlixlrj ydong fd tl tagipbpv fhrm usdkue rgo pwmqbvew vlv noka hvpdbeu lzcy srtvk "
+ "evp zmy ytcjpkf gzotbr turka mseoqee mssx la nzgp gemddp ifdaaqs rl dczf ms kapm hwde "
+ "nxlbpv zaghwszf fofd evpe mwrr qfze fhvbc azlteil dojazg kalh sw ioleo alcq hzfdswx "
+ "dejizbdanlv yzf ezqii xoinsfifg ss egak kapa fh unkh l zzxf wybnv ngglu hyzj tq "
+ "rvtnvpv ny r elrowd fihx hsw taigpgd jaod tyr ezqrv dpde lted by gfut svvwidaan "
+ "kalh ezq rvle cq lte wtca dgan whcuzl fhvbc siaetvgns ezq mplescq af napfp lte dbwy "
+ "hwzt kh hod kaoe vwsljqd li th hse mzqpr pnqrp wlm tffo kap dtye mrls hsw qaiej "
+ "oahxej ppfp faw ibasyazg rgo hsw srrld cq lte fknvljp wrl wwelqrvw hwez iiewqowde "
+ "tyx lbtemlj alr lkeudxo od s yakmpf zx oolkds ezmt kapgp oaucw ms dzmrvw zie wcurewm "
+ "zfq drr schwhei mss zjpei ppbe xarka evll mlc mss hazdwtwzd oqrv mz pp ualcxnhpv mnu "
+ "uccfytt kh evp zmrexdg cgam whc hsw";
int maxPossibleKeyLength = 30;

String cipherTextEdited = cipherText.replaceAll("\\s", "").toUpperCase();

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

System.out.print("Max length of the key (Indexs of Coincidences): " + maxPossibleKeyLength + "\n");

// Create sequences of cipher
createSequences(cipherTextEdited, maxPossibleKeyLength);

int keyLength;
while (true) {
System.out.print("\nEnter assumed key length using the above IoC (generally largest IoC): \n");
try {
keyLength = Integer.parseInt(reader.readLine());
if (keyLength > 0 && keyLength <= maxPossibleKeyLength) {
break;
} else {
System.out.println(
"Key length must be an integer greater than 0 and less than " + maxPossibleKeyLength + 1);
}
} catch (NumberFormatException e) {
System.out.println("Entered value must be an integer. Try again.");
} catch (IOException e) {
e.printStackTrace();
}
}

String guessedKey = guessKeyWithLength(cipherTextEdited, keyLength);
String typedKey;

while (true) {
System.out.println("\nThe guessed key is: " + guessedKey
+ ". Make sure that it makes sense (may need to make logical changes using above Chi values) type it to continue: ");
try {
typedKey = reader.readLine();
if (typedKey.toString().length() == guessedKey.length()) {
break;
} else {
System.out.println("Entered key length must be " + guessedKey.length());
}
} catch (IOException e) {
e.printStackTrace();
}
}

String decrypted = decryptUsingKey(cipherTextEdited, typedKey);
decrypted = placeSpaces(cipherText, decrypted); // Add spaces

System.out.println("\nThe cipherText says: \n" + decrypted);
}

// Creates sequences for given cipherText up to max keylength
public static void createSequences(String cipherText, int maxLength) {

// The IOC for every key length
ArrayList<Double> iocForKeyLengths = new ArrayList<Double>();
// The various sequences
ArrayList<String> sequences = new ArrayList<String>();

// Start from the first letter and loop to each next letter according to
// possible key lengths adding each once to a sequence which is then
// added to a list
int startAt = 0;
for (int possKeyLength = 1; possKeyLength <= maxLength; possKeyLength++) {
String newSequence = "";
while (startAt < possKeyLength) {
for (int i = startAt; i < cipherText.length(); i += possKeyLength) {
newSequence += cipherText.charAt(i);

}
sequences.add(newSequence);
newSequence = "";
startAt++;
}
System.out.println(possKeyLength + " " + calculateIoC(sequences));
iocForKeyLengths.add(calculateIoC(sequences));
sequences.clear();
startAt = 0;
}
}

// Index of Coincidence for a set of sequences/strings
public static double calculateIoC(ArrayList<String> sequences) {

ArrayList<Double> averages = new ArrayList<Double>();
for (int avgSeq = 0; avgSeq < sequences.size(); avgSeq++) {
int num = 0;
double sum = 0.0;
int[] values = new int[26];
for (int i = 0; i < 26; i++) {
values[i] = 0;
}

// Frequency of each letter in the sequence
int character;
for (int i = 0; i < sequences.get(avgSeq).length(); i++) {
character = sequences.get(avgSeq).charAt(i) - 65;
if (character >= 0 && character < 26) {
values[character]++;
num++;
}
}

for (int i = 0; i < 26; i++) {
character = values[i];
sum = sum + (character * (character - 1));
}

averages.add(sum / (num * (num - 1)));
}

// Calculate the average IoC for all sequences
double sum = 0;
for (Double avg : averages) {
sum += avg;
}

return sum / averages.size();
}

// Guess the key when given the max length of the key
public static String guessKeyWithLength(String cipherText, int maxLength) {

ArrayList<String> sequences = new ArrayList<String>();
String newSequence = "";
int startAt = 0;
while (startAt < maxLength) {
for (int i = startAt; i < cipherText.length(); i += maxLength) {
newSequence += cipherText.charAt(i);

}
sequences.add(newSequence);
newSequence = "";
startAt++;
}

String guessedKey = "";

for (int i = 0; i < maxLength; i++) {
System.out.println("\nPosition " + i + " of key: ");
System.out.println("Shift | Sequence | Chi Sum");
System.out.println("______________________________________________________________");

// The probable letters for each position
guessedKey += probLetterForIndex(maxLength, sequences.get(i).length(), sequences.get(i));
}

return guessedKey;
}

// Calculates the most probable letter for the each letter in key
private static char probLetterForIndex(int keyLength, int cipherLength, String substring) {

// The frequencies of the alphabet in the English language A to Z
double[] englishFreq = { 0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966,
0.00153, 0.00772, 0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056,
0.02758, 0.00978, 0.02360, 0.00150, 0.01974, 0.00074 };
ArrayList<Double> chiSums = new ArrayList<Double>();

// Essentially a caesar cipher on steroids, shift each letter starting
// from 0 all the way to 26, and calculate the chi sum
for (int shift = 0; shift < 26; shift++) {
double chiSum = 0;
int ASCIILetterA = 65;
int countLetterOcc = 0;
String shiftedMessage = "";

for (int i = 0; i < substring.length(); i++) {
int c = substring.charAt(i) - (shift % 26);
if (c < 'A') {
c = c + 26;
}
shiftedMessage = shiftedMessage + (char) c;
}

// Calculate the chi value for each letter of the alphabet
for (int i = 0; i < 26; i++) {

// How many times this letter occurs in the String
countLetterOcc = shiftedMessage.length()
- shiftedMessage.replace(String.valueOf(Character.toChars(ASCIILetterA + i)), "").length();

double x = countLetterOcc - ((cipherLength) * englishFreq[i]);

double numirator = Math.pow(x, 2);

chiSum += numirator / (cipherLength * englishFreq[i]); // Chill
// value
}
chiSums.add(chiSum);

System.out.println(shift + " " + shiftedMessage + " " + chiSum);

}

// Find the smallest chi value indicating most probable letter
int minLetterIndex = chiSums.indexOf(Collections.min(chiSums));
char minLetter = (char) ((minLetterIndex % 26) + 'A');

System.out.println("Prob letter: " + minLetter + " due to the chi value.");
System.out.println("_____________________________________________________________");

return minLetter;
}

// Decrypt the cipher using the known/guessed key
public static String decryptUsingKey(String cipherText, String key) {

String decrypted = "";
for (int i = 0, j = 0; i < cipherText.length(); i++) {
char c = cipherText.charAt(i);
if (c < 'A' || c > 'Z')
continue;
decrypted += (char) ((c - key.charAt(j) + 26) % 26 + 'A');
j = ++j % key.length();
}
return decrypted;
}

// Adds spaces to decrypted message because they were stripped from the
// cipherText
private static String placeSpaces(String cipherText, String decrypted) {

// FiFo for whitespaces
Queue<Integer> locationOfSpace = new LinkedList<Integer>();
for (int i = 0; i < cipherText.length(); i++) {
if (cipherText.charAt(i) == ' ') {
locationOfSpace.add(i);
}
}

System.out.println("num of spaces is " + locationOfSpace.size());

StringBuilder str = new StringBuilder(decrypted);

int numOfSpaces = locationOfSpace.size();
for (int i = 0; i < numOfSpaces; i++) {
int loc = locationOfSpace.remove();
str.insert(loc, " ");
System.out.println(i);
}
return str.toString();
}
}

0 comments on commit c43fc44

Please sign in to comment.