mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-06 17:29:31 +08:00
Enhance docs, add more tests in XORCipher
(#5900)
This commit is contained in:
@ -597,6 +597,7 @@
|
||||
* slidingwindow
|
||||
* [LongestSubstringWithoutRepeatingCharacters](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharacters.java)
|
||||
* [MaxSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarray.java)
|
||||
* [MinSumKSizeSubarray](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarray.java)
|
||||
* sorts
|
||||
* [AdaptiveMergeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/AdaptiveMergeSort.java)
|
||||
* [BeadSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BeadSort.java)
|
||||
@ -1217,6 +1218,7 @@
|
||||
* slidingwindow
|
||||
* [LongestSubstringWithoutRepeatingCharactersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/LongestSubstringWithoutRepeatingCharactersTest.java)
|
||||
* [MaxSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MaxSumKSizeSubarrayTest.java)
|
||||
* [MinSumKSizeSubarrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/slidingwindow/MinSumKSizeSubarrayTest.java)
|
||||
* sorts
|
||||
* [AdaptiveMergeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/AdaptiveMergeSortTest.java)
|
||||
* [BeadSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BeadSortTest.java)
|
||||
|
@ -5,18 +5,46 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.HexFormat;
|
||||
|
||||
/**
|
||||
* A simple implementation of XOR cipher that, given a key, allows to encrypt and decrypt a plaintext.
|
||||
* A simple implementation of the XOR cipher that allows both encryption and decryption
|
||||
* using a given key. This cipher works by applying the XOR bitwise operation between
|
||||
* the bytes of the input text and the corresponding bytes of the key (repeating the key
|
||||
* if necessary).
|
||||
*
|
||||
* Usage:
|
||||
* - Encryption: Converts plaintext into a hexadecimal-encoded ciphertext.
|
||||
* - Decryption: Converts the hexadecimal ciphertext back into plaintext.
|
||||
*
|
||||
* Characteristics:
|
||||
* - Symmetric: The same key is used for both encryption and decryption.
|
||||
* - Simple but vulnerable: XOR encryption is insecure for real-world cryptography,
|
||||
* especially when the same key is reused.
|
||||
*
|
||||
* Example:
|
||||
* Plaintext: "Hello!"
|
||||
* Key: "key"
|
||||
* Encrypted: "27090c03120b"
|
||||
* Decrypted: "Hello!"
|
||||
*
|
||||
* Reference: <a href="https://en.wikipedia.org/wiki/XOR_cipher">XOR Cipher - Wikipedia</a>
|
||||
*
|
||||
* @author <a href="https://github.com/lcsjunior">lcsjunior</a>
|
||||
*
|
||||
*/
|
||||
public final class XORCipher {
|
||||
|
||||
// Default character encoding for string conversion
|
||||
private static final Charset CS_DEFAULT = StandardCharsets.UTF_8;
|
||||
|
||||
private XORCipher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the XOR operation between the input bytes and the key bytes.
|
||||
* If the key is shorter than the input, it wraps around (cyclically).
|
||||
*
|
||||
* @param inputBytes The input byte array (plaintext or ciphertext).
|
||||
* @param keyBytes The key byte array used for XOR operation.
|
||||
* @return A new byte array containing the XOR result.
|
||||
*/
|
||||
public static byte[] xor(final byte[] inputBytes, final byte[] keyBytes) {
|
||||
byte[] outputBytes = new byte[inputBytes.length];
|
||||
for (int i = 0; i < inputBytes.length; ++i) {
|
||||
@ -25,14 +53,40 @@ public final class XORCipher {
|
||||
return outputBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given plaintext using the XOR cipher with the specified key.
|
||||
* The result is a hexadecimal-encoded string representing the ciphertext.
|
||||
*
|
||||
* @param plainText The input plaintext to encrypt.
|
||||
* @param key The encryption key.
|
||||
* @throws IllegalArgumentException if the key is empty.
|
||||
* @return A hexadecimal string representing the encrypted text.
|
||||
*/
|
||||
public static String encrypt(final String plainText, final String key) {
|
||||
if (key.isEmpty()) {
|
||||
throw new IllegalArgumentException("Key must not be empty");
|
||||
}
|
||||
|
||||
byte[] plainTextBytes = plainText.getBytes(CS_DEFAULT);
|
||||
byte[] keyBytes = key.getBytes(CS_DEFAULT);
|
||||
byte[] xorResult = xor(plainTextBytes, keyBytes);
|
||||
return HexFormat.of().formatHex(xorResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given ciphertext (in hexadecimal format) using the XOR cipher
|
||||
* with the specified key. The result is the original plaintext.
|
||||
*
|
||||
* @param cipherText The hexadecimal string representing the encrypted text.
|
||||
* @param key The decryption key (must be the same as the encryption key).
|
||||
* @throws IllegalArgumentException if the key is empty.
|
||||
* @return The decrypted plaintext.
|
||||
*/
|
||||
public static String decrypt(final String cipherText, final String key) {
|
||||
if (key.isEmpty()) {
|
||||
throw new IllegalArgumentException("Key must not be empty");
|
||||
}
|
||||
|
||||
byte[] cipherBytes = HexFormat.of().parseHex(cipherText);
|
||||
byte[] keyBytes = key.getBytes(CS_DEFAULT);
|
||||
byte[] xorResult = xor(cipherBytes, keyBytes);
|
||||
|
@ -1,34 +1,85 @@
|
||||
package com.thealgorithms.ciphers;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class XORCipherTest {
|
||||
|
||||
@Test
|
||||
void xorEncryptTest() {
|
||||
// given
|
||||
void xorEncryptDecryptTest() {
|
||||
String plaintext = "My t&xt th@t will be ençrypted...";
|
||||
String key = "My ç&cret key!";
|
||||
|
||||
// when
|
||||
String cipherText = XORCipher.encrypt(plaintext, key);
|
||||
String decryptedText = XORCipher.decrypt(cipherText, key);
|
||||
|
||||
// then
|
||||
assertEquals("000000b7815e1752111c601f450e48211500a1c206061ca6d35212150d4429570eed", cipherText);
|
||||
assertEquals("My t&xt th@t will be ençrypted...", decryptedText);
|
||||
}
|
||||
|
||||
@Test
|
||||
void xorDecryptTest() {
|
||||
// given
|
||||
String cipherText = "000000b7815e1752111c601f450e48211500a1c206061ca6d35212150d4429570eed";
|
||||
String key = "My ç&cret key!";
|
||||
void testEmptyPlaintext() {
|
||||
String plaintext = "";
|
||||
String key = "anykey";
|
||||
|
||||
// when
|
||||
String plainText = XORCipher.decrypt(cipherText, key);
|
||||
String cipherText = XORCipher.encrypt(plaintext, key);
|
||||
String decryptedText = XORCipher.decrypt(cipherText, key);
|
||||
|
||||
// then
|
||||
assertEquals("My t&xt th@t will be ençrypted...", plainText);
|
||||
assertEquals("", cipherText);
|
||||
assertEquals("", decryptedText);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyKey() {
|
||||
String plaintext = "Hello World!";
|
||||
String key = "";
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> XORCipher.encrypt(plaintext, key));
|
||||
assertThrows(IllegalArgumentException.class, () -> XORCipher.decrypt(plaintext, key));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShortKey() {
|
||||
String plaintext = "Short message";
|
||||
String key = "k";
|
||||
|
||||
String cipherText = XORCipher.encrypt(plaintext, key);
|
||||
String decryptedText = XORCipher.decrypt(cipherText, key);
|
||||
|
||||
assertEquals(plaintext, decryptedText);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonASCIICharacters() {
|
||||
String plaintext = "こんにちは世界"; // "Hello World" in Japanese (Konichiwa Sekai)
|
||||
String key = "key";
|
||||
|
||||
String cipherText = XORCipher.encrypt(plaintext, key);
|
||||
String decryptedText = XORCipher.decrypt(cipherText, key);
|
||||
|
||||
assertEquals(plaintext, decryptedText);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSameKeyAndPlaintext() {
|
||||
String plaintext = "samekey";
|
||||
String key = "samekey";
|
||||
|
||||
String cipherText = XORCipher.encrypt(plaintext, key);
|
||||
String decryptedText = XORCipher.decrypt(cipherText, key);
|
||||
|
||||
assertEquals(plaintext, decryptedText);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLongPlaintextShortKey() {
|
||||
String plaintext = "This is a long plaintext message.";
|
||||
String key = "key";
|
||||
|
||||
String cipherText = XORCipher.encrypt(plaintext, key);
|
||||
String decryptedText = XORCipher.decrypt(cipherText, key);
|
||||
|
||||
assertEquals(plaintext, decryptedText);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user