Files
codeql/java/ql/test/experimental/library-tests/quantum/jca/ChainedEncryptionTest.java
2025-10-06 10:46:09 -04:00

147 lines
6.6 KiB
Java

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));
}
}