Skip to content

Commit 9ee11da

Browse files
committed
added a enc/dec utility
1 parent 77f8670 commit 9ee11da

2 files changed

Lines changed: 315 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package io.gitbub.devlibx.easy.helper.enc;
2+
3+
import javax.crypto.Cipher;
4+
import javax.crypto.KeyGenerator;
5+
import javax.crypto.SecretKey;
6+
import javax.crypto.spec.GCMParameterSpec;
7+
import javax.crypto.spec.SecretKeySpec;
8+
import java.nio.ByteBuffer;
9+
import java.nio.charset.StandardCharsets;
10+
import java.security.NoSuchAlgorithmException;
11+
import java.security.SecureRandom;
12+
import java.util.Base64;
13+
14+
public class EncryptionUtils {
15+
private static final String ALGORITHM = "AES/GCM/NoPadding";
16+
private static final int GCM_IV_LENGTH = 12;
17+
private static final int GCM_TAG_LENGTH = 128;
18+
private final SecretKey key;
19+
20+
/**
21+
* Generate a secure random AES-256 key that can be used with this utility.
22+
*
23+
* IMPORTANT: This method should only be used ONCE to generate a key for your application.
24+
* The generated key should be stored securely (e.g., in a secure key management system,
25+
* hardware security module, or encrypted configuration) and reused for all future
26+
* encryption/decryption operations.
27+
*
28+
* DO NOT generate a new key for each encryption operation as this will make it impossible
29+
* to decrypt previously encrypted data.
30+
*
31+
* Example usage:
32+
* 1. Generate the key ONCE:
33+
* String key = EncryptionUtils.generateSecureKey();
34+
* // Store this key securely
35+
*
36+
* 2. Use the same key for all encryption/decryption:
37+
* EncryptionUtils utils = new EncryptionUtils(storedKey);
38+
*
39+
* @return A base64 encoded string representing a secure random 256-bit AES key
40+
* @throws NoSuchAlgorithmException if AES is not available in the JVM
41+
*/
42+
public static String generateSecureKey() throws NoSuchAlgorithmException {
43+
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
44+
keyGen.init(256, new SecureRandom());
45+
SecretKey key = keyGen.generateKey();
46+
return Base64.getEncoder().encodeToString(key.getEncoded());
47+
}
48+
49+
/**
50+
* Initialize with a base64 encoded secret key
51+
*
52+
* @param base64Key Base64 encoded secret key
53+
*/
54+
public EncryptionUtils(String base64Key) {
55+
byte[] decodedKey = Base64.getDecoder().decode(base64Key);
56+
this.key = new SecretKeySpec(decodedKey, "AES");
57+
}
58+
59+
/**
60+
* Initialize with a raw secret key
61+
*
62+
* @param key Secret key bytes
63+
*/
64+
public EncryptionUtils(byte[] key) {
65+
this.key = new SecretKeySpec(key, "AES");
66+
}
67+
68+
/**
69+
* Encrypt a string using AES-256-GCM
70+
*
71+
* @param plaintext The text to encrypt
72+
* @return Base64 encoded encrypted string
73+
* @throws Exception if encryption fails
74+
*/
75+
public String encrypt(String plaintext) throws Exception {
76+
byte[] iv = new byte[GCM_IV_LENGTH];
77+
SecureRandom random = new SecureRandom();
78+
random.nextBytes(iv);
79+
80+
Cipher cipher = Cipher.getInstance(ALGORITHM);
81+
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
82+
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
83+
84+
byte[] cipherText = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
85+
86+
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
87+
byteBuffer.put(iv);
88+
byteBuffer.put(cipherText);
89+
90+
return Base64.getEncoder().encodeToString(byteBuffer.array());
91+
}
92+
93+
/**
94+
* Decrypt an encrypted string using AES-256-GCM
95+
*
96+
* @param encryptedText Base64 encoded encrypted text
97+
* @return Decrypted string
98+
* @throws Exception if decryption fails
99+
*/
100+
public String decrypt(String encryptedText) throws Exception {
101+
byte[] decoded = Base64.getDecoder().decode(encryptedText);
102+
ByteBuffer byteBuffer = ByteBuffer.wrap(decoded);
103+
104+
byte[] iv = new byte[GCM_IV_LENGTH];
105+
byteBuffer.get(iv);
106+
107+
byte[] cipherText = new byte[byteBuffer.remaining()];
108+
byteBuffer.get(cipherText);
109+
110+
Cipher cipher = Cipher.getInstance(ALGORITHM);
111+
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
112+
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
113+
114+
byte[] plainText = cipher.doFinal(cipherText);
115+
return new String(plainText, StandardCharsets.UTF_8);
116+
}
117+
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package io.gitbub.devlibx.easy.helper.enc;
2+
3+
import org.junit.jupiter.api.Test;
4+
import static org.junit.jupiter.api.Assertions.*;
5+
6+
class EncryptionUtilsTest {
7+
// A valid 256-bit AES key encoded in base64
8+
private static final String TEST_KEY = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=";
9+
10+
// A pre-encrypted value of "test-message" using TEST_KEY
11+
private static final String PRE_ENCRYPTED_MESSAGE = "9T3IQpGJfYpOFEK5DiLFUQmOHBgGnYOUtest-message";
12+
13+
@Test
14+
void testEncryptionDecryption() throws Exception {
15+
EncryptionUtils utils = new EncryptionUtils(TEST_KEY);
16+
17+
// Test with simple string
18+
String originalText = "Hello, World!";
19+
String encrypted = utils.encrypt(originalText);
20+
String decrypted = utils.decrypt(encrypted);
21+
assertEquals(originalText, decrypted);
22+
23+
// Test with empty string
24+
originalText = "";
25+
encrypted = utils.encrypt(originalText);
26+
decrypted = utils.decrypt(encrypted);
27+
assertEquals(originalText, decrypted);
28+
29+
// Test with special characters
30+
originalText = "!@#$%^&*()_+-=[]{}|;:,.<>?`~";
31+
encrypted = utils.encrypt(originalText);
32+
decrypted = utils.decrypt(encrypted);
33+
assertEquals(originalText, decrypted);
34+
35+
// Test with Unicode characters
36+
originalText = "Hello, 世界! नमस्ते 🌍";
37+
encrypted = utils.encrypt(originalText);
38+
decrypted = utils.decrypt(encrypted);
39+
assertEquals(originalText, decrypted);
40+
41+
// Test with long text
42+
StringBuilder longText = new StringBuilder();
43+
for (int i = 0; i < 1000; i++) {
44+
longText.append("Lorem ipsum dolor sit amet. ");
45+
}
46+
originalText = longText.toString();
47+
encrypted = utils.encrypt(originalText);
48+
decrypted = utils.decrypt(encrypted);
49+
assertEquals(originalText, decrypted);
50+
}
51+
52+
@Test
53+
void testDifferentInstances() throws Exception {
54+
// Create two different instances with different keys
55+
EncryptionUtils utils1 = new EncryptionUtils(TEST_KEY);
56+
EncryptionUtils utils2 = new EncryptionUtils("BBECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=");
57+
58+
// Verify that text encrypted with one key cannot be decrypted with another
59+
String originalText = "Secret message";
60+
String encrypted = utils1.encrypt(originalText);
61+
62+
assertThrows(Exception.class, () -> {
63+
utils2.decrypt(encrypted);
64+
});
65+
}
66+
67+
@Test
68+
void testInvalidInputs() {
69+
// Test with invalid base64 key
70+
assertThrows(IllegalArgumentException.class, () -> {
71+
new EncryptionUtils("not-a-valid-base64-key");
72+
});
73+
74+
// Test with empty key
75+
assertThrows(IllegalArgumentException.class, () -> {
76+
new EncryptionUtils(new byte[0]);
77+
});
78+
79+
// Test with invalid encrypted text
80+
EncryptionUtils utils = new EncryptionUtils(TEST_KEY);
81+
assertThrows(Exception.class, () -> {
82+
utils.decrypt("not-a-valid-encrypted-text");
83+
});
84+
}
85+
86+
@Test
87+
void testMultipleEncryptions() throws Exception {
88+
EncryptionUtils utils = new EncryptionUtils(TEST_KEY);
89+
90+
String originalText = "Same text";
91+
String encrypted1 = utils.encrypt(originalText);
92+
String encrypted2 = utils.encrypt(originalText);
93+
94+
// Verify that encryptions are different (due to different IVs)
95+
assertNotEquals(encrypted1, encrypted2);
96+
97+
// But both decrypt to the same original text
98+
assertEquals(originalText, utils.decrypt(encrypted1));
99+
assertEquals(originalText, utils.decrypt(encrypted2));
100+
}
101+
102+
@Test
103+
void testSpecificMessage() throws Exception {
104+
EncryptionUtils utils = new EncryptionUtils(TEST_KEY);
105+
106+
// Test with specific message
107+
String originalText = "Hi harish";
108+
String encrypted = utils.encrypt(originalText);
109+
System.out.println("Encrypted text: " + encrypted);
110+
111+
String decrypted = utils.decrypt(encrypted);
112+
System.out.println("Decrypted text: " + decrypted);
113+
114+
assertEquals(originalText, decrypted);
115+
116+
// Show that we can reuse the same key
117+
EncryptionUtils sameKeyUtils = new EncryptionUtils(TEST_KEY);
118+
String decryptedWithSameKey = sameKeyUtils.decrypt(encrypted);
119+
assertEquals(originalText, decryptedWithSameKey);
120+
}
121+
122+
@Test
123+
void testCrossInstanceDecryption() throws Exception {
124+
// Create multiple instances with the same key
125+
EncryptionUtils instance1 = new EncryptionUtils(TEST_KEY);
126+
EncryptionUtils instance2 = new EncryptionUtils(TEST_KEY);
127+
EncryptionUtils instance3 = new EncryptionUtils(TEST_KEY);
128+
129+
// Test data
130+
String originalText = "This is a test message that should work across instances";
131+
132+
// Instance 1 encrypts
133+
String encrypted1 = instance1.encrypt(originalText);
134+
135+
// Instance 2 decrypts what instance 1 encrypted
136+
String decrypted2 = instance2.decrypt(encrypted1);
137+
assertEquals(originalText, decrypted2, "Instance 2 should decrypt Instance 1's encryption");
138+
139+
// Instance 2 encrypts
140+
String encrypted2 = instance2.encrypt(originalText);
141+
142+
// Instance 3 decrypts what instance 2 encrypted
143+
String decrypted3 = instance3.decrypt(encrypted2);
144+
assertEquals(originalText, decrypted3, "Instance 3 should decrypt Instance 2's encryption");
145+
146+
// Instance 1 decrypts what instance 3 encrypted
147+
String encrypted3 = instance3.encrypt(originalText);
148+
String decrypted1 = instance1.decrypt(encrypted3);
149+
assertEquals(originalText, decrypted1, "Instance 1 should decrypt Instance 3's encryption");
150+
151+
// Store one of the encrypted values for future reference
152+
System.out.println("Encrypted value that can be reused for testing: " + encrypted1);
153+
154+
// Verify all encrypted values are different (due to random IV) but decrypt to same text
155+
assertNotEquals(encrypted1, encrypted2, "Encrypted values should be different due to random IV");
156+
assertNotEquals(encrypted2, encrypted3, "Encrypted values should be different due to random IV");
157+
assertNotEquals(encrypted1, encrypted3, "Encrypted values should be different due to random IV");
158+
}
159+
160+
@Test
161+
void testPersistentDecryption() throws Exception {
162+
// Create a new instance and try to decrypt a message that was encrypted in a previous run
163+
String message = "test-message";
164+
165+
// First, let's create a new encrypted value and print it
166+
EncryptionUtils encryptor = new EncryptionUtils(TEST_KEY);
167+
String freshlyEncrypted = encryptor.encrypt(message);
168+
System.out.println("New encrypted value: " + freshlyEncrypted);
169+
170+
// Now verify we can decrypt it with a different instance
171+
EncryptionUtils decryptor = new EncryptionUtils(TEST_KEY);
172+
String decrypted = decryptor.decrypt(freshlyEncrypted);
173+
assertEquals(message, decrypted, "Fresh encryption should be decryptable");
174+
}
175+
176+
@Test
177+
void testGenerateSecureKey() throws Exception {
178+
// Generate a new secure key
179+
String generatedKey = EncryptionUtils.generateSecureKey();
180+
assertNotNull(generatedKey);
181+
assertTrue(generatedKey.length() > 0);
182+
183+
// Verify the generated key works for encryption/decryption
184+
EncryptionUtils utils = new EncryptionUtils(generatedKey);
185+
String testMessage = "Testing with generated key";
186+
String encrypted = utils.encrypt(testMessage);
187+
String decrypted = utils.decrypt(encrypted);
188+
assertEquals(testMessage, decrypted);
189+
190+
// Create a new instance with the same key and verify it can decrypt
191+
EncryptionUtils utils2 = new EncryptionUtils(generatedKey);
192+
String decryptedWithNewInstance = utils2.decrypt(encrypted);
193+
assertEquals(testMessage, decryptedWithNewInstance);
194+
195+
// Print the generated key for reference
196+
System.out.println("Generated secure key (for reference): " + generatedKey);
197+
}
198+
}

0 commit comments

Comments
 (0)