-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Expand file tree
/
Copy pathChainedEncryptionTest.java
More file actions
146 lines (122 loc) · 6.6 KB
/
Copy pathChainedEncryptionTest.java
File metadata and controls
146 lines (122 loc) · 6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
public class ChainedEncryptionTest {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
// Encrypts using AES-GCM. Returns IV concatenated with ciphertext.
public static byte[] encryptAESGCM(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // 12-byte nonce for AES-GCM
new SecureRandom().nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
return concat(iv, ciphertext);
}
// Decrypts AES-GCM ciphertext where IV is prepended.
public static byte[] decryptAESGCM(SecretKey key, byte[] ivCiphertext) throws Exception {
byte[] iv = Arrays.copyOfRange(ivCiphertext, 0, 12);
byte[] ciphertext = Arrays.copyOfRange(ivCiphertext, 12, ivCiphertext.length);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
return cipher.doFinal(ciphertext);
}
// Encrypts using ChaCha20-Poly1305. Returns nonce concatenated with ciphertext.
public static byte[] encryptChaCha20Poly1305(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305", "BC");
byte[] nonce = new byte[12]; // 12-byte nonce for ChaCha20-Poly1305
new SecureRandom().nextBytes(nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(nonce));
byte[] ciphertext = cipher.doFinal(plaintext);
return concat(nonce, ciphertext);
}
// Decrypts ChaCha20-Poly1305 ciphertext where nonce is prepended.
public static byte[] decryptChaCha20Poly1305(SecretKey key, byte[] nonceCiphertext) throws Exception {
byte[] nonce = Arrays.copyOfRange(nonceCiphertext, 0, 12);
byte[] ciphertext = Arrays.copyOfRange(nonceCiphertext, 12, nonceCiphertext.length);
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305", "BC");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(nonce));
return cipher.doFinal(ciphertext);
}
// Helper method to concatenate two byte arrays.
private static byte[] concat(byte[] a, byte[] b) {
byte[] result = new byte[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}
/**
* Performs chained encryption and decryption in one function. First,
* plaintext is encrypted with AES-GCM (inner layer), then that ciphertext
* is encrypted with ChaCha20-Poly1305 (outer layer). The decryption process
* reverses these steps.
*
* @param plaintext The input plaintext.
* @return The decrypted plaintext as a String.
* @throws Exception if any cryptographic operation fails.
*/
public static String chainEncryptDecrypt(String plaintext) throws Exception {
byte[] plainBytes = plaintext.getBytes("UTF-8");
// Generate keys for inner and outer encryption.
KeyGenerator aesGen = KeyGenerator.getInstance("AES");
aesGen.init(256, new SecureRandom());
SecretKey innerKey = aesGen.generateKey();
KeyGenerator chachaGen = KeyGenerator.getInstance("ChaCha20", "BC");
chachaGen.init(256, new SecureRandom());
SecretKey outerKey = chachaGen.generateKey();
// Inner Encryption with AES-GCM.
byte[] aesIV = new byte[12]; // Random 12-byte IV.
new SecureRandom().nextBytes(aesIV);
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, aesIV);
aesCipher.init(Cipher.ENCRYPT_MODE, innerKey, gcmSpec);
byte[] innerCiphertext = aesCipher.doFinal(plainBytes);
// Outer Encryption with ChaCha20-Poly1305.
byte[] chachaNonce = new byte[12]; // Random 12-byte nonce.
new SecureRandom().nextBytes(chachaNonce);
Cipher chachaCipher = Cipher.getInstance("ChaCha20-Poly1305", "BC");
chachaCipher.init(Cipher.ENCRYPT_MODE, outerKey, new IvParameterSpec(chachaNonce));
byte[] outerCiphertext = chachaCipher.doFinal(innerCiphertext);
// Outer Decryption.
Cipher chachaDec = Cipher.getInstance("ChaCha20-Poly1305", "BC");
chachaDec.init(Cipher.DECRYPT_MODE, outerKey, new IvParameterSpec(chachaNonce));
byte[] decryptedInnerCiphertext = chachaDec.doFinal(outerCiphertext);
// Inner Decryption.
Cipher aesDec = Cipher.getInstance("AES/GCM/NoPadding");
aesDec.init(Cipher.DECRYPT_MODE, innerKey, new GCMParameterSpec(128, aesIV));
byte[] decryptedPlaintext = aesDec.doFinal(decryptedInnerCiphertext);
return new String(decryptedPlaintext, "UTF-8");
}
public static void main(String[] args) throws Exception {
// Generate a 256-bit AES key for the first (inner) encryption.
KeyGenerator aesGen = KeyGenerator.getInstance("AES");
aesGen.init(256, new SecureRandom());
SecretKey aesKey = aesGen.generateKey();
// Generate a 256-bit key for ChaCha20-Poly1305 (outer encryption).
KeyGenerator chaChaGen = KeyGenerator.getInstance("ChaCha20");
chaChaGen.init(256, new SecureRandom());
SecretKey chaChaKey = chaChaGen.generateKey();
String originalText = "This is a secret message.";
byte[] plaintext = originalText.getBytes();
// Step 1: Encrypt plaintext with AES-GCM.
byte[] innerCiphertext = encryptAESGCM(aesKey, plaintext);
// Step 2: Encrypt the AES-GCM ciphertext with ChaCha20-Poly1305.
byte[] outerCiphertext = encryptChaCha20Poly1305(chaChaKey, innerCiphertext);
// Now, decrypt in reverse order.
// Step 3: Decrypt the outer layer (ChaCha20-Poly1305).
byte[] decryptedInnerCiphertext = decryptChaCha20Poly1305(chaChaKey, outerCiphertext);
// Step 4: Decrypt the inner layer (AES-GCM).
byte[] decryptedPlaintext = decryptAESGCM(aesKey, decryptedInnerCiphertext);
System.out.println("Original: " + originalText);
System.out.println("Decrypted: " + new String(decryptedPlaintext));
}
}