Introduction
The XOR cipher is a modern cipher that uses the bitwise XOR operation to combine the plaintext with a key. XOR cryptography became central to serious encryption in the early 20th century with the invention of the Vernam cipher by Gilbert Vernam in 1917. The Vernam cipher XORs the plaintext with a random key tape of the same length and is the foundation of the one-time pad, which is the only provably unbreakable cipher when used correctly. In this xor cipher java tutorial we will implement the cipher using Java's bitwise operators.
The XOR cipher matters historically because it is the conceptual bridge between classical and modern cryptography. All classical ciphers we have studied so far (Caesar, Vigenere, Playfair, Hill) operate on letters. The XOR cipher operates on bits, which is the natural unit for digital data. Modern symmetric ciphers like AES, ChaCha20, and RC4 all use XOR as a fundamental operation, often in combination with substitution and permutation.
What is the XOR Cipher?
The XOR cipher is a symmetric stream cipher that uses the bitwise exclusive OR operation to combine the plaintext with a key. The formula is C = P XOR K, where P is the plaintext, K is the key, and C is the ciphertext. Both P and K are sequences of bits (or bytes), and the XOR operation is applied bitwise.
The key property of XOR that makes the cipher work is that it is self-inverse: (P XOR K) XOR K = P. This means that decrypting the ciphertext with the same key recovers the plaintext. The same operation works for both encryption and decryption.
Compared to classical ciphers, the XOR cipher has several advantages. First, it works on arbitrary binary data, not just letters. Second, it is extremely fast because XOR is a single CPU instruction. Third, when the key is truly random and as long as the message, the resulting one-time pad is provably unbreakable. The downside is that the key must be exactly as long as the message and must never be reused, which is a major logistical challenge.
The XOR cipher is also vulnerable to many attacks when the key is short or reused. If the same key is used twice, XORing the two ciphertexts together gives the XOR of the two plaintexts, which leaks information. This is the famous "two-time pad" attack that broke Soviet diplomatic messages in the 1940s and 1950s.
How the XOR Cipher Works Step by Step
Suppose our plaintext is the byte 0x48 (which is the ASCII code for the letter H) and our key is the byte 0x7F.
Step 1: Write the plaintext and key in binary. P = 0100 1000 (binary for 0x48) K = 0111 1111 (binary for 0x7F)
Step 2: Apply XOR bitwise. For each bit position, the result is 1 if exactly one of the input bits is 1, and 0 otherwise. P: 0 1 0 0 1 0 0 0 K: 0 1 1 1 1 1 1 1 C: 0 0 1 1 0 1 1 1 (which is 0x37)
Step 3: The ciphertext is 0x37. To decrypt, XOR the ciphertext with the same key. C: 0 0 1 1 0 1 1 1 K: 0 1 1 1 1 1 1 1 P: 0 1 0 0 1 0 0 0 (which is 0x48, the original plaintext H)
The self-inverse property works because XOR is symmetric: A XOR B = B XOR A, and (A XOR B) XOR B = A XOR (B XOR B) = A XOR 0 = A.
Java Program for XOR Cipher
public class XorCipher {
public static byte[] encrypt(byte[] plain, byte[] key) {
byte[] cipher = new byte[plain.length];
for (int i = 0; i < plain.length; i++) {
cipher[i] = (byte) (plain[i] ^ key[i % key.length()]);
}
return cipher;
}
public static byte[] decrypt(byte[] cipher, byte[] key) {
return encrypt(cipher, key); // XOR is self-inverse
}
public static String toHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b & 0xFF));
}
return sb.toString();
}
public static byte[] fromHex(String hex) {
byte[] result = new byte[hex.length() / 2];
for (int i = 0; i < result.length; i++) {
int high = Character.digit(hex.charAt(2 * i), 16);
int low = Character.digit(hex.charAt(2 * i + 1), 16);
result[i] = (byte) ((high << 4) | low);
}
return result;
}
public static void main(String[] args) {
byte[] plain = "HELLO".getBytes();
byte[] key = "KEY".getBytes();
byte[] cipher = encrypt(plain, key);
byte[] recovered = decrypt(cipher, key);
System.out.println("Plain : " + new String(plain));
System.out.println("Key : " + new String(key));
System.out.println("Cipher : " + toHex(cipher));
System.out.println("Decoded : " + new String(recovered));
}
}
Code Explanation Line by Line
The encrypt method takes two byte arrays (the plaintext and the key) and returns the ciphertext as a byte array. The loop walks through each byte of the plaintext. For each byte, we compute the XOR with the corresponding key byte. The key byte is selected using i % key.length, which means the key is repeated cyclically if it is shorter than the plaintext. This is the same key-wrapping trick used in Vigenere.
The crucial line is cipher[i] = (byte) (plain[i] ^ key[i % key.length()]). The ^ operator in Java is bitwise XOR. We cast the result back to byte because the XOR operation promotes both operands to int, so we need to truncate the result back to a byte.
The decrypt method is just return encrypt(cipher, key) because XOR is self-inverse. This is the same elegant simplification we used for Atbash and ROT13.
The toHex and fromHex methods are utilities for displaying the ciphertext in a readable form. Because the XOR result can be any byte value (including non-printable characters), we use hexadecimal to display it. The toHex method iterates through each byte and formats it as two hex digits. The fromHex method does the reverse, parsing two hex digits into one byte. The expression b & 0xFF is needed to handle the sign extension that happens when a byte is promoted to int.
The main method demonstrates the cipher with the plaintext HELLO and the key KEY. The output shows the ciphertext as a hex string and confirms that decryption recovers the original plaintext.
Sample Input and Output
Plain : HELLO Key : KEY Cipher : 0B17070C0D Decoded : HELLO
The output confirms the self-inverse property. The exact hex value of the ciphertext depends on the key, but the decryption always recovers the original plaintext exactly. Notice that the ciphertext bytes are not printable characters, which is why we display them in hex.
Common Mistakes to Avoid
1. Forgetting the cast to byte. Java's ^ operator promotes both operands to int, so the result is an int. Without the cast, the assignment to a byte array would fail to compile.
2. Reusing the same key for multiple messages. This is the "two-time pad" vulnerability, which makes the cipher trivially breakable. Each pair of messages encrypted with the same key can be XORed together to give the XOR of the two plaintexts, which leaks information about both.
3. Using a key shorter than the message without realizing it. If the key is shorter, it is repeated cyclically, which makes the cipher vulnerable to frequency analysis (just like Vigenere). The one-time pad is unbreakable only when the key is as long as the message and never reused.
4. Not handling the sign extension correctly. When displaying bytes as integers, Java promotes them to int with sign extension, so negative bytes appear as large negative numbers. Use b & 0xFF to get the unsigned value.
5. Confusing XOR with AND or OR. AND and OR are not self-inverse, so they cannot be used for encryption. Only XOR has the property that (A XOR B) XOR B = A.
Real-World Applications
The XOR cipher is the foundation of the one-time pad, which is the only provably unbreakable cipher when used correctly. The one-time pad is still used today for high-security applications like diplomatic communications between major powers, where the key can be physically delivered by trusted couriers. The famous "red telephone" between Washington and Moscow during the Cold War used a one-time pad for encryption.
XOR is also a fundamental operation in modern symmetric ciphers. AES uses XOR in its AddRoundKey step, where each round key is XORed with the state matrix. ChaCha20, the stream cipher used in TLS 1.3, generates a pseudo-random keystream and XORs it with the plaintext. RC4, the stream cipher used in early Wi-Fi encryption (WEP), is essentially an XOR cipher with a more sophisticated key schedule.
The CBC mode of block cipher operation XORs each plaintext block with the previous ciphertext block before encryption. This is the same XOR-and-chain idea used in the autokey cipher, but applied to binary data instead of letters.
In CTF competitions, XOR ciphers are extremely common, usually as easy challenges. The standard attack is to recognize the XOR pattern (often the key is short and repeating) and use frequency analysis or known plaintext to recover the key. A common puzzle is a hex-encoded ciphertext that, when XORed with a known plaintext byte, reveals a flag.
The XOR operation also appears in error-correcting codes, hash functions, and random number generators. Its self-inverse property and its simplicity make it a useful primitive throughout computer science, not just in cryptography.
The Vernam cipher (the original name for the XOR cipher applied to teletype signals) was patented by Gilbert Vernam in 1919. The combination of Vernam's XOR machinery and Joseph Mauborgne's idea of using a truly random key tape of the same length as the message produced the one-time pad, which remains the gold standard for unbreakable encryption.
Conclusion
In this xor cipher java tutorial we built a complete bitwise cipher using Java's XOR operator, walked through every line of code, and explained the self-inverse property that makes XOR so useful in cryptography. You now understand bitwise operations, the difference between XOR and other boolean operators, and the historical context of the Vernam cipher. Next, study the Baconian cipher, which uses a 5-bit encoding of letters invented by Francis Bacon.
Comments
Comments
Post a Comment