So I thought Mdog’s Java Playfair implementation was pretty cool, so I wrote my own implementation of a similar cipher named Foursquare.
http://en.wikipedia.org/wiki/Four-square_cipher
The four-square cipher uses four 5 by 5 matrices arranged in a square. Each of the 5 by 5 matrices contains the letters of the alphabet (usually omitting "Q" or putting both "I" and "J" in the same location to reduce the alphabet to fit). In general, the upper-left and lower-right matrices are the "plaintext squares" and each contain a standard alphabet. The upper-right and lower-left squares are the "ciphertext squares" and contain a mixed alphabetic sequence. To generate the ciphertext squares, one would first fill in the spaces in the matrix with the letters of a keyword or phrase (dropping any duplicate letters), then fill the remaining spaces with the rest of the letters of the alphabet in order (again omitting "Q" to reduce the alphabet to fit). The key can be written in the top rows of the table, from left to right, or in some other pattern, such as a spiral beginning in the upper-left-hand corner and ending in the center. The keyword together with the conventions for filling in the 5 by 5 table constitute the cipher key. The four-square algorithm allows for two separate keys, one for each of the two ciphertext matrices.
If you’re interested on how the ciphertext is generated, go on the article, there is alot of information about the cipher on there.
import java.util.ArrayList;
import java.util.List;
/**
* Java implementation of the Foursquare cipher.
* Made by Ollie123 with portions taken from m.dog311's Playfair implementation.
* @author Ollie
*/
public class Foursquare {
/**
* Alphabet template, excluding Q.
*/
private static final char[] ALPHABET = "ABCDEFGHIJKLMNOPRSTUVWXYZ".toCharArray();
/**
* Alphabet square.
*/
private static final char[][] ALPHABET_SQUARE = new char[5][5];
/**
* Static block to populate the Alphabet Square.
*/
static {
int x = 0, y = 0;
for (char c : ALPHABET) {
ALPHABET_SQUARE[x][y] = c;
x++;
if (x == 5) {
x = 0;
y++;
}
}
}
/**
* Cleans a plaintext or keyword ready for use in the Foursquare cipher.
* @param input the text to clean
* @return the clean text
*/
private static String clean(String input) {
input = input.trim().replace(" ", "").replace("Q", "").toUpperCase();
StringBuilder clean = new StringBuilder();
for (char c : input.toCharArray()) {
if (Character.getType(c) == Character.UPPERCASE_LETTER) {
clean.append(c);
}
}
return clean.toString();
}
/**
* Generates a 5*5 key table for the specified keyword.
* @param keyword the keyword from which to generate the table
* @return the newly generated key table
*/
private static char[][] generateKeyTable(String keyword) {
keyword = clean(keyword);
char[][] keyTable = new char[5][5];
List<Character> used = new ArrayList<Character>();
int x = 0, y = 0;
for (char c : keyword.toCharArray()) {
if (!used.contains(c)) {
keyTable[x][y] = c;
used.add(c);
x++;
if (x == 5) {
x = 0;
y++;
if (y == 5) {
return keyTable;
}
}
}
}
for (char c : ALPHABET) {
if (!used.contains(c)) {
keyTable[x][y] = c;
x++;
if (x == 5) {
x = 0;
y++;
if (y == 5) {
return keyTable;
}
}
}
}
return keyTable;
}
/**
* Splits a string into two-letter strings (digraphs). Portions courtesy m.dog311.
* @param plaintext the string to split
* @return an array of two-letter strings
*/
private static String[] split(String plaintext) {
if (plaintext.length() % 2 != 0) {
plaintext = plaintext + "X";
}
String[] pairs = new String[plaintext.length() / 2];
int count = 0;
for (int i = 0; i < (plaintext.length() / 2); i++) {
pairs[i] = plaintext.substring(count, count + 2);
count = count + 2;
}
return pairs;
}
/**
* Encrypts a plaintext with Foursquare using the two specified keywords.
* @param plaintext the text to encrypt
* @param keyword1 the first keyword to use
* @param keyword2 the second keyword to use
* @return the ciphertext
*/
public static String encrypt(String plaintext, String keyword1, String keyword2) {
plaintext = clean(plaintext);
String[] pairs = split(plaintext);
char[][] keytable1 = generateKeyTable(keyword1);
char[][] keytable2 = generateKeyTable(keyword2);
char first, second;
int xFirst = 0, yFirst = 0, xSecond = 0, ySecond = 0;
StringBuilder ciphertext = new StringBuilder();
for (String s : pairs) {
first = s.charAt(0);
second = s.charAt(1);
for (int y = 0; y < 5; y++) {
for (int x = 0; x < 5; x++) {
if (ALPHABET_SQUARE[x][y] == first) {
xFirst = x;
yFirst = y;
} else if (ALPHABET_SQUARE[x][y] == second) {
xSecond = x;
ySecond = y;
}
}
}
ciphertext.append(keytable1[xSecond][yFirst]).append(keytable2[xFirst][ySecond]);
}
return ciphertext.toString();
}
/**
* Decrypts a Foursquare-encrypted ciphertext using the two specified keywords.
* @param ciphertext the text to decrypt
* @param keyword1 the first keyword to use
* @param keyword2 the second keyword to use
* @return the plaintext
*/
public static String decrypt(String ciphertext, String keyword1, String keyword2) {
String[] pairs = split(ciphertext);
char[][] keytable1 = generateKeyTable(keyword1);
char[][] keytable2 = generateKeyTable(keyword2);
char first, second;
int xFirst = 0, yFirst = 0, xSecond = 0, ySecond = 0;
StringBuilder plaintext = new StringBuilder();
for (String s : pairs) {
first = s.charAt(0);
second = s.charAt(1);
for (int y = 0; y < 5; y++) {
for (int x = 0; x < 5; x++) {
if (keytable1[x][y] == first) {
xFirst = x;
yFirst = y;
} else if (keytable2[x][y] == second) {
xSecond = x;
ySecond = y;
}
}
}
plaintext.append(ALPHABET_SQUARE[xSecond][yFirst]).append(ALPHABET_SQUARE[xFirst][ySecond]);
}
return plaintext.toString();
}
public static void main(String[] args) {
try {
if(args[0].equalsIgnoreCase("-d")) {
System.out.println(decrypt(args[1], args[2], args[3]));
} else {
System.out.println(encrypt(args[0], args[1], args[2]));
}
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("USAGE: java Foursquare [-d] <plaintext|ciphertext> <first keyword> <second keyword>");
}
}
}