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

325 lines
13 KiB
Java

package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
// import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* AsymmetricEncryptionMacHybridCryptosystem demonstrates hybrid cryptosystems
* that combine asymmetric encryption with a MAC.
*
* Flows: 1. RSA-OAEP + HMAC: - Secure Flow: Uses 2048-bit RSA-OAEP (with
* SHA256andMGF1Padding) to encapsulate a freshly generated AES key; then
* encrypts using AES-GCM with a random nonce and computes HMAC-SHA256 over the
* ciphertext. - Insecure Flow: Uses 1024-bit RSA (RSA/ECB/PKCS1Padding),
* AES-GCM with a fixed IV, and HMAC-SHA1.
*
* 2. ECIES + HMAC: - Secure Flow: Uses ephemeral ECDH key pairs (secp256r1);
* derives a shared secret and applies a simple KDF (SHA-256) to derive a
* 128-bit AES key; then uses AES-GCM with a random nonce and computes
* HMAC-SHA256. - Insecure Flow: Reuses a static EC key pair, directly truncates
* the shared secret without a proper KDF, uses a fixed IV, and computes
* HMAC-SHA1.
*
* 3. Dynamic Hybrid Selection: - Chooses between flows based on a configuration
* string.
*
* SAST/CBOM Notes: - Secure flows use proper ephemeral key generation, secure
* key sizes, KDF usage, and random nonces/IVs. - Insecure flows (static key
* reuse, fixed nonces, weak key sizes, raw shared secret truncation, and
* deprecated algorithms) should be flagged.
*/
public class AsymmetricEncryptionMacHybridCryptosystem {
// static {
// Security.addProvider(new BouncyCastleProvider());
// Security.addProvider(new BouncyCastlePQCProvider());
// }
// ---------- Result Class ----------
public static class HybridResult {
private final byte[] encapsulatedKey;
private final byte[] ciphertext;
private final byte[] mac;
public HybridResult(byte[] encapsulatedKey, byte[] ciphertext, byte[] mac) {
this.encapsulatedKey = encapsulatedKey;
this.ciphertext = ciphertext;
this.mac = mac;
}
public byte[] getEncapsulatedKey() {
return encapsulatedKey;
}
public byte[] getCiphertext() {
return ciphertext;
}
public byte[] getMac() {
return mac;
}
public String toBase64String() {
return "EncapsulatedKey: " + Base64.getEncoder().encodeToString(encapsulatedKey)
+ "\nCiphertext: " + Base64.getEncoder().encodeToString(ciphertext)
+ "\nMAC: " + Base64.getEncoder().encodeToString(mac);
}
}
// ---------- Helper Methods ----------
/**
* Generates an ephemeral ECDH key pair on secp256r1.
*/
public KeyPair generateECDHKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
return kpg.generateKeyPair();
}
/**
* Generates an ephemeral X25519 key pair.
*/
public KeyPair generateX25519KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X25519", "BC");
kpg.initialize(255, new SecureRandom());
return kpg.generateKeyPair();
}
/**
* Derives a shared secret using the provided key agreement algorithm.
*
* @param privateKey The private key.
* @param publicKey The corresponding public key.
* @param algorithm The key agreement algorithm (e.g., "ECDH" or "X25519").
* @return The shared secret.
*/
public byte[] deriveSharedSecret(PrivateKey privateKey, PublicKey publicKey, String algorithm) throws Exception {
KeyAgreement ka = KeyAgreement.getInstance(algorithm, "BC");
ka.init(privateKey);
ka.doPhase(publicKey, true);
return ka.generateSecret();
}
/**
* A simple KDF that hashes the input with SHA-256 and returns the first
* numBytes.
*
* @param input The input byte array.
* @param numBytes The desired number of output bytes.
* @return The derived key material.
*/
public byte[] simpleKDF(byte[] input, int numBytes) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input);
return Arrays.copyOf(hash, numBytes);
}
/**
* Concatenates two byte arrays.
*/
public byte[] concatenate(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;
}
// =====================================================
// 1. RSA-OAEP + HMAC Hybrid Cryptosystem
// =====================================================
/**
* Generates a secure 2048-bit RSA key pair.
*/
public KeyPair generateRSAKeyPairGood() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
return kpg.generateKeyPair();
}
/**
* Generates an insecure 1024-bit RSA key pair.
*/
public KeyPair generateRSAKeyPairBad() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
return kpg.generateKeyPair();
}
/**
* Secure hybrid encryption using RSA-OAEP + HMAC-SHA256.
*/
public HybridResult secureRSAHybridEncryption(byte[] plaintext) throws Exception {
KeyPair rsaKP = generateRSAKeyPairGood();
SecretKey aesKey = generateAESKey();
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.WRAP_MODE, rsaKP.getPublic());
byte[] encapsulatedKey = rsaCipher.wrap(aesKey);
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(128, iv));
byte[] ciphertext = aesCipher.doFinal(plaintext);
byte[] fullCiphertext = concatenate(iv, ciphertext);
byte[] macKey = generateAESKey().getEncoded();
byte[] mac = secureHMACSHA256(new String(fullCiphertext), macKey);
return new HybridResult(encapsulatedKey, fullCiphertext, mac);
}
/**
* Insecure hybrid encryption using RSA/ECB/PKCS1Padding + HMAC-SHA1.
*/
public HybridResult insecureRSAHybridEncryption(byte[] plaintext) throws Exception {
KeyPair rsaKP = generateRSAKeyPairBad();
SecretKey aesKey = generateAESKey();
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsaCipher.init(Cipher.WRAP_MODE, rsaKP.getPublic());
byte[] encapsulatedKey = rsaCipher.wrap(aesKey);
byte[] fixedIV = new byte[12]; // All zeros
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(128, fixedIV));
byte[] ciphertext = aesCipher.doFinal(plaintext);
byte[] fullCiphertext = concatenate(fixedIV, ciphertext);
byte[] macKey = generateAESKey().getEncoded();
byte[] mac = insecureHMACSHA1(new String(fullCiphertext), macKey);
return new HybridResult(encapsulatedKey, fullCiphertext, mac);
}
// =====================================================
// 2. ECIES + HMAC Hybrid Cryptosystem
// =====================================================
/**
* Secure hybrid encryption using ECIES (via ECDH) + HMAC-SHA256.
*/
public HybridResult secureECIESHybridEncryption(byte[] plaintext) throws Exception {
KeyPair aliceKP = generateECDHKeyPair();
KeyPair bobKP = generateECDHKeyPair();
byte[] sharedSecret = deriveSharedSecret(aliceKP.getPrivate(), bobKP.getPublic(), "ECDH");
byte[] aesKeyBytes = simpleKDF(sharedSecret, 16);
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(128, iv));
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] fullCiphertext = concatenate(iv, ciphertext);
byte[] macKey = generateAESKey().getEncoded();
byte[] mac = secureHMACSHA256(new String(fullCiphertext), macKey);
byte[] ephemeralPubKey = aliceKP.getPublic().getEncoded();
return new HybridResult(ephemeralPubKey, fullCiphertext, mac);
}
/**
* Insecure hybrid encryption using ECIES (via ECDH) + HMAC-SHA1.
*/
public HybridResult insecureECIESHybridEncryption(byte[] plaintext) throws Exception {
KeyPair staticKP = generateECDHKeyPair();
byte[] sharedSecret = deriveSharedSecret(staticKP.getPrivate(), staticKP.getPublic(), "ECDH");
byte[] aesKeyBytes = Arrays.copyOf(sharedSecret, 16);
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES");
byte[] fixedIV = new byte[12]; // Fixed IV
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(128, fixedIV));
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] fullCiphertext = concatenate(fixedIV, ciphertext);
byte[] macKey = generateAESKey().getEncoded();
byte[] mac = insecureHMACSHA1(new String(fullCiphertext), macKey);
byte[] staticPubKey = staticKP.getPublic().getEncoded();
return new HybridResult(staticPubKey, fullCiphertext, mac);
}
// =====================================================
// 3. Dynamic Hybrid Selection
// =====================================================
/**
* Dynamically selects a hybrid encryption flow based on configuration.
* SAST: Dynamic selection introduces risk if insecure defaults are chosen.
*
* @param config The configuration string ("secureRSA", "insecureRSA",
* "secureECIES", "insecureECIES").
* @param plaintext The plaintext to encrypt.
* @return A Base64-encoded string representation of the hybrid encryption
* result.
* @throws Exception if an error occurs.
*/
public String dynamicHybridEncryption(String config, byte[] plaintext) throws Exception {
HybridResult result;
if ("secureRSA".equalsIgnoreCase(config)) {
result = secureRSAHybridEncryption(plaintext);
} else if ("insecureRSA".equalsIgnoreCase(config)) {
result = insecureRSAHybridEncryption(plaintext);
} else if ("secureECIES".equalsIgnoreCase(config)) {
result = secureECIESHybridEncryption(plaintext);
} else if ("insecureECIES".equalsIgnoreCase(config)) {
result = insecureECIESHybridEncryption(plaintext);
} else {
// Fallback to insecure RSA hybrid encryption.
result = insecureRSAHybridEncryption(plaintext);
}
return result.toBase64String();
}
// =====================================================
// 4. Helper Methods for HMAC and Symmetric Encryption
// =====================================================
/**
* Secure HMAC using HMAC-SHA256. SAST: HMAC-SHA256 is secure.
*/
public byte[] secureHMACSHA256(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Insecure HMAC using HMAC-SHA1. SAST: HMAC-SHA1 is deprecated and
* insecure.
*/
public byte[] insecureHMACSHA1(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA1", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA1");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
// =====================================================
// 5. Helper Methods for Key/Nonce Generation
// =====================================================
/**
* Generates a secure 256-bit AES key. SAST: Uses SecureRandom for key
* generation.
*/
public SecretKey generateAESKey() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256, new SecureRandom());
return kg.generateKey();
}
}