Introduction
The nihilist cipher java is a hybrid construction that combines a Polybius square with a Vigenere style addition. It was reportedly used by Russian Nihilists in the late nineteenth century, who needed a cipher that could be operated quickly with only pencil and paper. The construction starts by encoding each plaintext letter into a two-digit coordinate using a keyword mixed Polybius square, then adds a numeric key stream derived from a second keyword. This nihilist cipher java tutorial walks through both stages of the algorithm and produces a runnable Java implementation.
The Nihilist cipher is interesting because it sits at the intersection of substitution and addition based ciphers. The first stage is a fractionation step that converts letters into two-digit numbers, and the second stage is a Vigenere style addition that uses numeric keys. The result is a cipher that resists simple frequency analysis while remaining easy to operate by hand.
What is the Nihilist Cipher?
The Nihilist cipher is a polyalphabetic substitution cipher that combines a Polybius square with a numeric key stream. The Polybius square is a 5 by 5 grid that contains a keyword mixed alphabet, with the letter I and J sharing a cell to fit 26 letters into 25 slots. Each plaintext letter is encoded as a two-digit number consisting of its row and column in the square. A numeric key stream is generated by encoding a second keyword through the same Polybius square.
Compared to a plain Polybius square, the Nihilist cipher adds an addition step that uses a key, which makes the cipher polyalphabetic rather than monoalphabetic. Compared to a Vigenere cipher, the Nihilist operates on two-digit numbers rather than single letters, which gives a slightly larger per-character information density.
The addition step produces numbers between 22 (11 + 11) and 110 (55 + 55), so each ciphertext symbol is a two or three digit number. The variable length is a minor inconvenience for the operator but does not affect the algorithm.
How the Nihilist Cipher Works Step by Step
Step 1: Build the Polybius square from a keyword. We use CIPHER as the keyword. The mixed alphabet is CIPHERABDFGKLMNOQSTUVWXY (Z is merged into a shared cell, typically with the letter I and J sharing one cell). The 5 by 5 grid is: C I/J P H E R A B D F G K L M N O Q S T U V W X Y Z
Step 2: Encode the plaintext. We use HELLO as the plaintext. H is at row 1, column 4, so its coordinate is 14. E is at row 1, column 5, coordinate 15. L is at row 3, column 3, coordinate 33. L is 33. O is at row 4, column 1, coordinate 41. The plaintext coordinates are 14, 15, 33, 33, 41.
Step 3: Generate the key stream from a second keyword. We use KEY as the second keyword. K is at row 3, column 2, coordinate 32. E is at row 1, column 5, coordinate 15. Y is at row 5, column 4, coordinate 54. The key stream is 32, 15, 54, 32, 15, repeating as needed to match the plaintext length.
Step 4: Add the plaintext coordinates and the key stream. 14 + 32 = 46. 15 + 15 = 30. 33 + 54 = 87. 33 + 32 = 65. 41 + 15 = 56. The ciphertext is the sequence 46 30 87 65 56.
Step 5: Decrypt by subtracting the key stream from each ciphertext number and looking up the resulting coordinate in the Polybius square. 46 - 32 = 14, which is H. 30 - 15 = 15, which is E. And so on.
Java Program for Nihilist Cipher
import java.util.LinkedHashSet;
import java.util.Set;
public class NihilistCipher {
private final char[][] grid = new char[5][5];
private final String keyStreamKeyword;
public NihilistCipher(String squareKeyword, String keyStreamKeyword) {
this.keyStreamKeyword = keyStreamKeyword.toUpperCase();
buildGrid(squareKeyword);
}
private void buildGrid(String keyword) {
Set<Character> seen = new LinkedHashSet<>();
keyword = keyword.toUpperCase();
for (char ch : keyword.toCharArray()) {
if (ch == 'J') ch = 'I';
if (ch >= 'A' && ch <= 'Z') seen.add(ch);
}
for (char ch = 'A'; ch <= 'Z'; ch++) {
if (ch == 'J') continue;
seen.add(ch);
}
int i = 0;
for (Character c : seen) {
grid[i / 5][i % 5] = c;
i++;
}
}
private int[] coord(char ch) {
if (ch == 'J') ch = 'I';
for (int r = 0; r < 5; r++) {
for (int c = 0; c < 5; c++) {
if (grid[r][c] == ch) {
return new int[]{r + 1, c + 1};
}
}
}
throw new IllegalArgumentException("Invalid char: " + ch);
}
private char charAt(int row, int col) {
return grid[row - 1][col - 1];
}
private int[] keyStream(int length) {
int[] keys = new int[length];
for (int i = 0; i < length; i++) {
char k = keyStreamKeyword.charAt(i % keyStreamKeyword.length());
int[] rc = coord(k);
keys[i] = rc[0] * 10 + rc[1];
}
return keys;
}
public String encrypt(String plain) {
plain = plain.toUpperCase().replaceAll("[^A-Z]", "").replace("J", "I");
int[] keys = keyStream(plain.length());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < plain.length(); i++) {
int[] rc = coord(plain.charAt(i));
int p = rc[0] * 10 + rc[1];
sb.append(p + keys[i]).append(" ");
}
return sb.toString().trim();
}
public String decrypt(String cipher) {
String[] parts = cipher.trim().split("\s+");
int[] keys = keyStream(parts.length);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
int sum = Integer.parseInt(parts[i]);
int p = sum - keys[i];
int row = p / 10;
int col = p % 10;
sb.append(charAt(row, col));
}
return sb.toString();
}
public static void main(String[] args) {
NihilistCipher nc = new NihilistCipher("CIPHER", "KEY");
String plain = "HELLO";
String cipher = nc.encrypt(plain);
String back = nc.decrypt(cipher);
System.out.println("Plain : " + plain);
System.out.println("Cipher: " + cipher);
System.out.println("Back : " + back);
}
}
Code Explanation Line by Line
The constructor takes two keywords: one to build the Polybius square and one to generate the key stream. The buildGrid method uses a LinkedHashSet to collect letters from the keyword first, then from the regular alphabet, skipping the letter J because I and J share a cell. The result is a 5 by 5 char array that maps coordinates to letters.
The coord method finds the row and column of a given letter in the grid. It returns a two-element int array, with the row and column both one-indexed to match the traditional Polybius convention. The charAt method is the inverse: given a row and column, it returns the letter at that position.
keyStream generates the numeric key stream by encoding each key letter through the same Polybius square. The key stream is repeated to match the plaintext length using the modulo expression i % keyStreamKeyword.length(). Each key stream entry is a two-digit number formed by concatenating the row and column of the key letter.
encrypt walks each plaintext letter, looks up its coordinate, encodes it as a two-digit number, adds the key stream entry, and appends the sum to the output. The sums are separated by spaces because they can be two or three digits, and without separators the parsing would be ambiguous.
decrypt splits the ciphertext on whitespace, subtracts the key stream entry from each sum, splits the result into row and column digits, and looks up the letter. The result is the recovered plaintext.
Sample Input and Output
Square keyword: CIPHER Key keyword: KEY Plain : HELLO Cipher: 46 30 87 65 56 Back : HELLO
The plaintext HELLO is encrypted to the numeric sequence 46 30 87 65 56 using square keyword CIPHER and key keyword KEY. The decrypted output matches the original plaintext exactly. The two-digit plaintext coordinates are added to the two-digit key stream coordinates to produce sums in the range 22 to 110.
Common Mistakes to Avoid
First, building the Polybius square without merging I and J. The traditional Nihilist cipher uses a 5 by 5 grid, which only fits 25 letters, so I and J must share a cell. If you skip this merge, you will not be able to fit all 26 letters, and the cipher will not work for plaintexts containing J.
Second, forgetting to handle J in the plaintext. If the plaintext contains a J, you must replace it with an I before encoding. Otherwise, the coord lookup will throw an exception because J is not in the grid.
Third, using spaces in the ciphertext without separators. The sums can be two or three digits, so without spaces or other separators, the parsing is ambiguous. A ciphertext like 4630876556 could be parsed as 46, 30, 87, 65, 56 or as 46, 308, 7, 65, 56, and the two parses produce different plaintexts.
Fourth, mixing up the row and column order. The Polybius convention is row first, column second, both one-indexed. If you swap them, the cipher still works, but the grid layout differs from the canonical Nihilist cipher.
Real-World Applications
The Nihilist cipher is not used in production today. Its security is weak by modern standards because the keyword mixed Polybius square is vulnerable to known plaintext attacks, and the key stream repeats with the length of the second keyword.
However, the Nihilist cipher is a useful teaching example because it combines two fundamental cryptographic primitives: substitution (the Polybius square) and addition (the key stream). This combination is the same pattern used by modern stream ciphers, which generate a pseudorandom key stream and XOR it with the plaintext. The Nihilist cipher uses addition instead of XOR, but the structure is identical.
The Nihilist cipher also demonstrates the concept of fractionation, where each plaintext letter is split into multiple symbols before encryption. Fractionation is used by stronger classical ciphers such as the ADFGVX and the Trifid, which split each letter into two or three symbols and then transposes them. The Nihilist cipher is the simplest example of fractionation followed by addition.
In capture the flag events, the Nihilist cipher is sometimes used as a puzzle that requires the solver to recognize the two-stage construction. The hint is usually a numeric ciphertext with values between 22 and 110, which is a strong indicator of a Polybius plus addition cipher.
The Nihilist cipher is also historically significant because it was one of the first ciphers used by a revolutionary political movement. The Russian Nihilists of the late nineteenth century needed a cipher that could be operated by untrained operatives in the field, and the Nihilist cipher met that need by combining two simple primitives into a reasonably strong construction for its era.
Conclusion
The Nihilist cipher combines a Polybius square with a Vigenere style numeric key stream. We built the square from a keyword, generated the key stream from a second keyword, and produced an encrypt and decrypt pair that handles the variable length sums correctly. Next you should study the ADFGVX cipher (which extends the Nihilist idea with a transposition stage), the Trifid cipher (which uses a 3 by 3 by 3 cube instead of a 5 by 5 square), and the Bifid cipher (which uses fractionation without the addition step).
Comments
Comments
Post a Comment