Introduction
The Vigenere cipher is one of the most celebrated classical ciphers in the history of cryptography. It was first described in 1553 by the French cryptographer Giovan Battista Bellaso, but the name was later mistakenly attributed to the French diplomat Blaise de Vigenere in the 19th century. For nearly 300 years after its invention, the cipher was considered so strong that it earned the nickname le chiffre indechiffrable, French for "the indecipherable cipher". In this vigenere cipher java walkthrough we will implement it from scratch and see exactly why it is so much stronger than the simpler Caesar cipher.
The reason Vigenere resisted attacks for so long is that it is a polyalphabetic cipher. Instead of shifting every letter by the same amount, it shifts each letter by a different amount dictated by a keyword. This destroys the simple frequency pattern that makes Caesar so easy to crack. If you are a Java learner who has already understood the Caesar cipher, the Vigenere cipher is your natural next step up.
What is the Vigenere Cipher?
The Vigenere cipher is a polyalphabetic substitution cipher. The word polyalphabetic means that the same plaintext letter can be replaced by different ciphertext letters depending on its position in the message. For example, the letter E might become K the first time it appears, R the second time, and B the third time. This makes naive frequency analysis nearly useless, because the usual pattern of English letters is smeared across the alphabet.
Conceptually, Vigenere is a sequence of Caesar ciphers. If your keyword is LEMON (5 letters), then the first plaintext letter is shifted by L (11 positions), the second by E (4 positions), the third by M (12 positions), the fourth by O (14 positions), the fifth by N (13 positions), and then the keyword repeats: the sixth letter is shifted by L again. So a 5-letter keyword gives you 5 interwoven Caesar ciphers.
Compared to Caesar, Vigenere has a much larger effective key space. A 10-letter keyword already gives 26^10 possible keys, which is around 1.4 times 10 to the power of 14. Brute force is no longer feasible. The cipher was finally broken in the 19th century by Charles Babbage and later by Friedrich Kasiski using statistical methods that exploit the periodic repetition of the keyword.
How the Vigenere Cipher Works Step by Step
Suppose our plaintext is ATTACKATDAWN and our keyword is LEMON. We start by repeating the keyword over and over until it matches the length of the plaintext.
Step 1: Align the keyword with the plaintext. Plaintext: A T T A C K A T D A W N Keyword : L E M O N L E M O N L E
Step 2: Convert each plaintext letter and each keyword letter to a number from 0 (A) to 25 (Z). A=0, T=19, T=19, A=0, C=2, K=10, A=0, T=19, D=3, A=0, W=22, N=13 L=11, E=4, M=12, O=14, N=13, L=11, E=4, M=12, O=14, N=13, L=11, E=4
Step 3: For each column, add the two numbers modulo 26. Column 1: (0 + 11) mod 26 = 11 which is L Column 2: (19 + 4) mod 26 = 23 which is X Column 3: (19 + 12) mod 26 = 5 which is F Column 4: (0 + 14) mod 26 = 14 which is O Column 5: (2 + 13) mod 26 = 15 which is P Column 6: (10 + 11) mod 26 = 21 which is V Column 7: (0 + 4) mod 26 = 4 which is E Column 8: (19 + 12) mod 26 = 5 which is F Column 9: (3 + 14) mod 26 = 17 which is R Column 10: (0 + 13) mod 26 = 13 which is N Column 11: (22 + 11) mod 26 = 7 which is H Column 12: (13 + 4) mod 26 = 17 which is R
Step 4: Read off the ciphertext: LXFOPVEEFRNHR. To decrypt, subtract the keyword letter instead of adding, again modulo 26.
Java Program for Vigenere Cipher
public class VigenereCipher {
public static String sanitize(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isLetter(c)) {
sb.append(Character.toUpperCase(c));
}
}
return sb.toString();
}
public static String encrypt(String text, String key) {
text = sanitize(text);
key = sanitize(key);
StringBuilder result = new StringBuilder();
int keyIndex = 0;
for (int i = 0; i < text.length(); i++) {
char p = text.charAt(i);
char k = key.charAt(keyIndex);
char c = (char) (((p - 'A' + (k - 'A')) % 26) + 'A');
result.append(c);
keyIndex = (keyIndex + 1) % key.length();
}
return result.toString();
}
public static String decrypt(String text, String key) {
text = sanitize(text);
key = sanitize(key);
StringBuilder result = new StringBuilder();
int keyIndex = 0;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
char k = key.charAt(keyIndex);
char p = (char) (((c - 'A') - (k - 'A') + 26) % 26 + 'A');
result.append(p);
keyIndex = (keyIndex + 1) % key.length();
}
return result.toString();
}
public static void main(String[] args) {
String plain = "ATTACKATDAWN";
String key = "LEMON";
String cipher = encrypt(plain, key);
String recovered = decrypt(cipher, key);
System.out.println("Plain : " + plain);
System.out.println("Key : " + key);
System.out.println("Cipher : " + cipher);
System.out.println("Decoded : " + recovered);
}
}
Code Explanation Line by Line
The sanitize method strips out everything that is not a letter and uppercases the rest. This is the classical Vigenere convention: only the 26 letters are encrypted, and digits, spaces, and punctuation are simply dropped. If you want to preserve spaces you can modify this method to keep them.
In the encrypt method, we loop over each plaintext character. We extract the current plaintext letter p and the current key letter k. Both are uppercase, so subtracting 'A' from each gives a value from 0 to 25. We add the two values, take modulo 26, and add 'A' back to return to the ASCII uppercase range. The cast (char) is needed because arithmetic on chars in Java promotes them to int.
The crucial line is keyIndex = (keyIndex + 1) % key.length(). This advances the key pointer one step at a time and wraps it back to 0 when it reaches the end of the keyword. This is how the keyword repeats endlessly to match the length of the plaintext.
The decrypt method is nearly identical to encrypt, except we subtract the key letter from the ciphertext letter. The expression (c - 'A') - (k - 'A') + 26 adds 26 before taking modulo to avoid negative values, the same trick we used in the Caesar cipher. Then % 26 reduces it back to the 0 to 25 range.
Both methods are O(n) where n is the length of the message. They make a single pass through the text and do constant work per character, which is as fast as you can get.
Sample Input and Output
Plain : ATTACKATDAWN Key : LEMON Cipher : LXFOPVEEFRNHR Decoded : ATTACKATDAWN
The output shows the encrypted text using the famous LEMON keyword from textbooks. The decrypted text matches the original input exactly, confirming that the round trip works correctly. Note that this implementation produces uppercase-only output because the sanitize step uppercases everything.
Common Mistakes to Avoid
1. Forgetting to wrap the key index. If you forget keyIndex = (keyIndex + 1) % key.length() and instead use keyIndex++, you will run off the end of the keyword and crash with an IndexOutOfBoundsException.
2. Not sanitizing the input. If the plaintext contains spaces and the keyword does not, your key alignment will drift and decryption will produce garbage. Either strip non-letters or skip them while advancing the key index.
3. Using a key letter without subtracting 'A'. If you forget to normalize the key, you will be adding 65 (the ASCII of 'A') instead of 0, causing the cipher to behave like a Caesar with shift 65 which is meaningless.
4. Allowing negative shifts in decrypt without adding 26. Java's % can return negative values, so (c - k) % 26 might give a negative result for letters where c is less than k. Always add 26 before taking the modulo.
5. Reusing the same StringBuilder without resetting. If you call encrypt twice on the same StringBuilder instance, you will append new ciphertext to old ciphertext. Always create a fresh StringBuilder inside the method.
Real-World Applications
The Vigenere cipher is never used for real security today. It was broken by Kasiski in 1863 and even earlier by Babbage. However, its core idea of using a keystream to drive a polyalphabetic substitution lives on in modern stream ciphers like RC4 (now also broken) and the ChaCha20 cipher used in TLS 1.3. The principle of mixing the plaintext with a pseudo-random key stream using XOR is a direct descendant of Vigenere.
In the world of competitive programming and capture-the-flag events, Vigenere puzzles are extremely common. They are slightly harder than Caesar puzzles but still crackable in a few minutes with the right tools. The Kasiski examination, which we will implement in a later post, is the standard technique.
Vigenere also appears in some "toy" security products like basic password obfuscators. Never trust such products for real secrets, because the cipher is fundamentally a 19th-century design that cannot withstand modern cryptanalysis.
Conclusion
In this vigenere cipher java walkthrough we built a complete polyalphabetic cipher, walked through every line of code, and explained why mixing a keyword with the plaintext is so much stronger than Caesar's fixed shift. You now understand polyalphabetic substitution, key streams, and the importance of wrapping the key index correctly. Next, study the Playfair cipher, which uses a 5 by 5 grid of letters and digraph substitution to push security even further.
Comments
Comments
Post a Comment