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