Crypto: Add jca unit tests.

This commit is contained in:
REDMOND\brodes
2025-10-03 13:32:02 -04:00
parent f4fea6d635
commit 66e9d7671d
29 changed files with 10556 additions and 0 deletions

View File

@@ -0,0 +1,241 @@
package com.example.crypto.algorithms;
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
/**
* AesWrapAndPBEWithTest demonstrates key wrapping and password-based encryption
* using various transformations.
*
* This file includes:
*
* 1. AESWrap Examples:
* - secureAESWrap(): Uses a randomly generated wrapping key.
* - insecureAESWrap(): Uses a fixed, hard-coded wrapping key.
*
* 2. PBEWith Examples:
* - insecurePBEExample(): Uses the legacy PBEWithMD5AndDES.
* - securePBEExample(): Uses PBKDF2WithHmacSHA256.
* - additionalPBEExample(): Uses PBEWithSHA256And128BitAES-CBC-BC.
* - additionalPBEExample2(): Uses PBEWithSHA1And128BitAES-CBC-BC.
*
* 3. Dynamic PBE Encryption:
* - dynamicPBEEncryption(): Chooses the PBE transformation based on a
* configuration string.
*
* Best Practices:
* - Use secure random keys and salts.
* - Avoid legacy algorithms like PBEWithMD5AndDES.
* - Prefer modern KDFs (PBKDF2WithHmacSHA256) and secure provider-specific PBE
* transformations.
*
* SAST/CBOM Notes:
* - Insecure examples (PBEWithMD5AndDES, fixed keys) should be flagged.
* - Secure examples use random salt, high iteration counts, and strong
* algorithms.
*/
public class AesWrapAndPBEWith {
// static {
// // Register BouncyCastle as a provider.
// Security.addProvider(new BouncyCastleProvider());
// }
// ===========================
// 1. AESWrap Examples
// ===========================
/**
* Secure AES key wrapping.
* Generates a random 256-bit wrapping key to wrap a target AES key.
*
* @return The wrapped key (Base64-encoded).
* @throws Exception if an error occurs.
*/
public String secureAESWrap() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256, new SecureRandom());
SecretKey wrappingKey = kg.generateKey();
kg.init(128, new SecureRandom());
SecretKey targetKey = kg.generateKey();
Cipher cipher = Cipher.getInstance("AESWrap");
cipher.init(Cipher.WRAP_MODE, wrappingKey);
byte[] wrappedKey = cipher.wrap(targetKey);
return Base64.getEncoder().encodeToString(wrappedKey);
}
/**
* Insecure AES key wrapping.
* Uses a fixed (hard-coded) wrapping key.
*
* @return The wrapped key (Base64-encoded).
* @throws Exception if an error occurs.
*/
public String insecureAESWrap() throws Exception {
byte[] fixedKeyBytes = new byte[32];
Arrays.fill(fixedKeyBytes, (byte) 0x01);
SecretKey wrappingKey = new SecretKeySpec(fixedKeyBytes, "AES");
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, new SecureRandom());
SecretKey targetKey = kg.generateKey();
Cipher cipher = Cipher.getInstance("AESWrap");
cipher.init(Cipher.WRAP_MODE, wrappingKey);
byte[] wrappedKey = cipher.wrap(targetKey);
return Base64.getEncoder().encodeToString(wrappedKey);
}
// ===========================
// 2. PBEWith Examples
// ===========================
/**
* Insecure PBE example using PBEWithMD5AndDES.
*
* @param password The input password.
* @return The derived key (Base64-encoded).
* @throws Exception if key derivation fails.
*/
public String insecurePBEExample(String password) throws Exception {
byte[] salt = new byte[8];
Arrays.fill(salt, (byte) 0x00); // Fixed salt (insecure)
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 64);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
byte[] keyBytes = factory.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(keyBytes);
}
/**
* Secure PBE example using PBKDF2WithHmacSHA256.
*
* @param password The input password.
* @return The derived 256-bit AES key (Base64-encoded).
* @throws Exception if key derivation fails.
*/
public String securePBEExample(String password) throws Exception {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] keyBytes = factory.generateSecret(spec).getEncoded();
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
return Base64.getEncoder().encodeToString(aesKey.getEncoded());
}
/**
* Additional PBE example using PBEWithSHA256And128BitAES-CBC-BC.
*
* @param password The input password.
* @param plaintext The plaintext to encrypt.
* @return The IV concatenated with ciphertext (Base64-encoded).
* @throws Exception if key derivation or encryption fails.
*/
public String additionalPBEExample(String password, String plaintext) throws Exception {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithSHA256And128BitAES-CBC-BC");
SecretKey pbeKey = factory.generateSecret(spec);
SecretKey aesKey = new SecretKeySpec(pbeKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
byte[] output = concatenate(iv, ciphertext);
return Base64.getEncoder().encodeToString(output);
}
/**
* Additional PBE example using PBEWithSHA1And128BitAES-CBC-BC.
* This is less preferred than PBKDF2WithHmacSHA256 but demonstrates another
* variant.
*
* @param password The input password.
* @param plaintext The plaintext to encrypt.
* @return The IV concatenated with ciphertext (Base64-encoded).
* @throws Exception if key derivation or encryption fails.
*/
public String additionalPBEExample2(String password, String plaintext) throws Exception {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithSHA1And128BitAES-CBC-BC");
SecretKey pbeKey = factory.generateSecret(spec);
SecretKey aesKey = new SecretKeySpec(pbeKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes());
byte[] output = concatenate(iv, ciphertext);
return Base64.getEncoder().encodeToString(output);
}
// ===========================
// 3. Dynamic PBE Encryption
// ===========================
/**
* Dynamically selects a PBE transformation based on a configuration string.
*
* Acceptable values:
* - "PBKDF2": Uses PBKDF2WithHmacSHA256.
* - "SHA256AES": Uses PBEWithSHA256And128BitAES-CBC-BC.
* - "SHA1AES": Uses PBEWithSHA1And128BitAES-CBC-BC.
* - Otherwise, falls back to insecure PBEWithMD5AndDES.
*
* @param config The configuration string.
* @param password The input password.
* @param plaintext The plaintext to encrypt.
* @return The Base64-encoded encrypted output.
* @throws Exception if an error occurs.
*/
public String dynamicPBEEncryption(String config, String password, String plaintext) throws Exception {
if ("PBKDF2".equalsIgnoreCase(config)) {
return securePBEExample(password);
} else if ("SHA256AES".equalsIgnoreCase(config)) {
return additionalPBEExample(password, plaintext);
} else if ("SHA1AES".equalsIgnoreCase(config)) {
return additionalPBEExample2(password, plaintext);
} else {
// Fallback insecure option.
return insecurePBEExample(password);
}
}
// ===========================
// Helper Methods
// ===========================
/**
* Concatenates two byte arrays.
*/
private 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;
}
}

View File

@@ -0,0 +1,339 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
// import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
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;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Base64;
/**
* 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();
}
}

View File

@@ -0,0 +1,151 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Arrays;
import java.util.Base64;
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));
}
}

View File

@@ -0,0 +1,257 @@
package com.example.crypto.artifacts;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
/**
* DigestTestCase demonstrates the further use of cryptographic digests
* as inputs to more complex cryptosystems. In real-world applications,
* digest outputs are often used as keys, key material for key derivation,
* or as identifiers. This file shows several flows:
*
* 1. Basic digest generation using SHA-256 (secure) and MD5/SHA-1 (insecure).
* 2. Unsalted versus salted digest for password input.
* 3. PBKDF2 for secure key derivation.
* 4. Using a digest as direct key material for AES encryption (processDigest).
* 5. Using a digest as an identifier (alternativeDigestFlow).
* 6. **Further Use**: Deriving two separate keys (one for encryption and one
* for MAC)
* from a digest via PBKDF2 and using them in an authenticated encryption flow.
*
* SAST/CBOM notes:
* - Secure algorithms (e.g. SHA-256, HMAC-SHA256, PBKDF2WithHmacSHA256) are
* acceptable.
* - Insecure functions (e.g. MD5, SHA-1) and unsalted password digests are
* flagged.
* - Using a raw digest directly as key material is ambiguous unless produced by
* a proper KDF.
*/
public class Digest {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
// ---------- Digest Generation Flows ----------
/**
* Secure digest generation using SHA-256.
* SAST: SHA-256 is secure.
*/
public void simpleHashing() throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest("Simple Test Data".getBytes());
processDigest(hash);
}
/**
* Insecure digest generation using MD5.
* SAST: MD5 is deprecated and insecure.
*/
public void insecureMD5Hashing() throws Exception {
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
byte[] hash = md5Digest.digest("Weak Hash Example".getBytes());
processDigest(hash);
}
/**
* Insecure unsalted password hashing using SHA-256.
* SAST: Unsalted password hashing is vulnerable to rainbow table attacks.
*/
public void insecureUnsaltedPasswordHashing(String password) throws Exception {
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
byte[] hash = sha256Digest.digest(password.getBytes());
processDigest(hash);
}
/**
* Secure salted hashing using SHA-256.
* SAST: Salting the input improves security.
*/
public void secureSaltedHashing(String password) throws Exception {
byte[] salt = generateSalt(16);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(salt);
byte[] hash = digest.digest(password.getBytes());
processDigest(hash);
}
/**
* Secure key derivation using PBKDF2 with HMAC-SHA256.
* SAST: PBKDF2 with sufficient iterations is recommended.
*/
public void securePBKDF2Hashing(String password) throws Exception {
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
processDigest(hash);
}
/**
* Insecure digest generation using SHA-1.
* SAST: SHA-1 is deprecated due to collision vulnerabilities.
*/
public void insecureRawSHA1Hashing(String input) throws Exception {
MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
byte[] hash = sha1Digest.digest(input.getBytes());
processDigest(hash);
}
/**
* Secure MAC computation using HMAC-SHA256.
* SAST: HMAC-SHA256 is considered secure.
*/
public void secureHMACHashing(String input, byte[] key) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
hmac.init(secretKey);
byte[] hash = hmac.doFinal(input.getBytes());
processDigest(hash);
}
// ---------- Further Use of Digest Outputs ----------
/**
* Processes the digest by using it directly as key material for AES encryption.
* SAST: Using a raw digest as key material is acceptable only if the digest is
* produced
* via a secure KDF. This method is ambiguous if the digest is from an insecure
* function.
*
* @param digest The computed digest.
* @throws Exception if encryption fails.
*/
public void processDigest(byte[] digest) throws Exception {
// Derive a 128-bit AES key from the digest.
SecretKey key = new SecretKeySpec(digest, 0, 16, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal("Sensitive Data".getBytes());
storeEncryptedDigest(encryptedData);
}
/**
* Alternative flow: Uses the digest as an identifier (e.g., checksum) and
* encrypts it.
* SAST: Using a digest as an identifier is common; encryption must use secure
* primitives.
*
* @param digest The computed digest.
* @throws Exception if encryption fails.
*/
public void alternativeDigestFlow(byte[] digest) throws Exception {
byte[] identifier = Base64.getEncoder().encode(digest);
encryptAndSend(identifier);
}
/**
* Further use: Derives two separate keys from a digest using PBKDF2,
* then uses one key for encryption and the other for computing a MAC over the
* ciphertext.
*
* SAST: This approach of key derivation and splitting is acceptable if PBKDF2
* is used securely.
*
* @param digest The input digest (must be generated from a secure source).
* @throws Exception if key derivation or encryption fails.
*/
public void furtherUseDigestForKeyDerivation(byte[] digest) throws Exception {
// Treat the digest (in Base64) as a password input to PBKDF2.
String digestAsPassword = Base64.getEncoder().encodeToString(digest);
byte[] salt = generateSalt(16);
// Derive 256 bits (32 bytes) of key material.
PBEKeySpec spec = new PBEKeySpec(digestAsPassword.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] keyMaterial = factory.generateSecret(spec).getEncoded();
// Split into two 128-bit keys.
byte[] encryptionKeyBytes = Arrays.copyOfRange(keyMaterial, 0, 16);
byte[] macKeyBytes = Arrays.copyOfRange(keyMaterial, 16, 32);
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
// Encrypt sample data using the derived encryption key.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new SecureRandom());
byte[] ciphertext = cipher.doFinal("Further Use Test Data".getBytes());
// Compute HMAC over the ciphertext using the derived MAC key.
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(macKey);
byte[] computedMac = mac.doFinal(ciphertext);
// In production, these outputs would be securely stored or transmitted.
byte[] output = new byte[ciphertext.length + computedMac.length];
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
storeEncryptedDigest(output);
}
/**
* Encrypts data using AES-GCM and simulates secure transmission or storage.
* SAST: Uses a securely generated AES key.
*
* @param data The data to encrypt.
* @throws Exception if encryption fails.
*/
public void encryptAndSend(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKey key = generateAESKey();
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal(data);
storeEncryptedDigest(encryptedData);
}
/**
* Simulates secure storage or transmission of an encrypted digest.
* SAST: In production, this method would implement secure storage/transmission.
*
* @param encryptedDigest The encrypted digest.
*/
public void storeEncryptedDigest(byte[] encryptedDigest) {
// For static analysis purposes, this method represents a secure output
// mechanism.
String stored = Base64.getEncoder().encodeToString(encryptedDigest);
}
// ---------- Helper Methods ----------
/**
* Generates a secure 256-bit AES key.
* SAST: Key generation uses a strong RNG.
*
* @return A SecretKey for AES.
* @throws NoSuchAlgorithmException if AES is unsupported.
*/
private SecretKey generateAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
return keyGen.generateKey();
}
/**
* Generates a random salt of the specified length using SecureRandom.
* SAST: Salting is essential for secure digest computations.
*
* @param length The salt length.
* @return A byte array representing the salt.
*/
private byte[] generateSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}

View File

@@ -0,0 +1,156 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.util.Base64;
/**
* EllipticCurve1 demonstrates generating EC key pairs for various curve
* categories.
*
* Curve categories covered:
* - NIST: e.g., secp256r1, secp384r1, secp521r1.
* - SEC: e.g., secp256k1 (from the Standards for Efficient Cryptography, SEC2).
* - BRAINPOOL: e.g., brainpoolP256r1.
* - CURVE25519: for key agreement (X25519) or signatures (Ed25519).
* - CURVE448: for key agreement (X448).
* - C2: Binary curves; for example, sect163r2 (if available).
* - SM2: Chinese SM2 curve, often named sm2p256v1.
* - ES: Elliptic curve signature based on EdDSA, here using Ed25519.
* - OtherEllipticCurveType: A fallback (using secp256r1).
*
* Best practices:
* - Use ephemeral key generation with a strong RNG.
* - Select curves from secure families (e.g., NIST, Brainpool, Curve25519/448,
* SM2).
* - Use a crypto provider (e.g., BouncyCastle) that supports the desired
* curves.
*
* In a production environment, the curve type may be externally configured.
*/
public class EllipticCurve1 {
// static {
// // Register the BouncyCastle provider to access a wide range of curves.
// Security.addProvider(new BouncyCastleProvider());
// }
/**
* Generates a key pair using a NIST curve (e.g., secp256r1).
*/
public KeyPair generateNISTKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
// secp256r1 is widely used (also known as P-256)
kpg.initialize(new java.security.spec.ECGenParameterSpec("secp256r1"));
return kpg.generateKeyPair();
}
/**
* Generates a key pair using a SEC curve (e.g., secp256k1).
*/
public KeyPair generateSECCurveKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
// secp256k1 is commonly used in Bitcoin and other blockchain applications.
kpg.initialize(new java.security.spec.ECGenParameterSpec("secp256k1"));
return kpg.generateKeyPair();
}
/**
* Generates a key pair using a Brainpool curve (e.g., brainpoolP256r1).
*/
public KeyPair generateBrainpoolKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
// "brainpoolP256r1" is a commonly recommended Brainpool curve.
kpg.initialize(new java.security.spec.ECGenParameterSpec("brainpoolP256r1"));
return kpg.generateKeyPair();
}
/**
* Generates an X25519 key pair (for key agreement).
*/
public KeyPair generateCurve25519KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X25519", "BC");
// No further parameters are needed for X25519.
return kpg.generateKeyPair();
}
/**
* Generates an X448 key pair (for key agreement).
*/
public KeyPair generateCurve448KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X448", "BC");
return kpg.generateKeyPair();
}
/**
* Generates a key pair for a binary (C2) curve.
* Example: sect163r2 is a binary field curve.
*/
public KeyPair generateC2CurveKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
// "sect163r2" is one of the binary field curves supported by BouncyCastle.
kpg.initialize(new java.security.spec.ECGenParameterSpec("sect163r2"));
return kpg.generateKeyPair();
}
/**
* Generates a key pair for the SM2 curve.
* SM2 is a Chinese cryptographic standard.
*/
public KeyPair generateSM2KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
// "sm2p256v1" is the standard SM2 curve.
kpg.initialize(new java.security.spec.ECGenParameterSpec("sm2p256v1"));
return kpg.generateKeyPair();
}
/**
* Generates a key pair for ES (Elliptic curve signature using EdDSA).
* This example uses Ed25519.
*/
public KeyPair generateESKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519", "BC");
return kpg.generateKeyPair();
}
/**
* Generates a key pair for an "Other" elliptic curve type.
* This serves as a fallback example (using secp256r1).
*/
public KeyPair generateOtherEllipticCurveKeyPair() throws Exception {
return generateNISTKeyPair(); // Fallback to secp256r1
}
/**
* Main method demonstrating key pair generation for various curve types.
*/
public static void main(String[] args) {
try {
EllipticCurve1 examples = new EllipticCurve1();
System.out.println("NIST (secp256r1): " +
Base64.getEncoder().encodeToString(examples.generateNISTKeyPair().getPublic().getEncoded()));
System.out.println("SEC (secp256k1): " +
Base64.getEncoder().encodeToString(examples.generateSECCurveKeyPair().getPublic().getEncoded()));
System.out.println("Brainpool (brainpoolP256r1): " +
Base64.getEncoder().encodeToString(examples.generateBrainpoolKeyPair().getPublic().getEncoded()));
System.out.println("Curve25519 (X25519): " +
Base64.getEncoder().encodeToString(examples.generateCurve25519KeyPair().getPublic().getEncoded()));
System.out.println("Curve448 (X448): " +
Base64.getEncoder().encodeToString(examples.generateCurve448KeyPair().getPublic().getEncoded()));
System.out.println("C2 (sect163r2): " +
Base64.getEncoder().encodeToString(examples.generateC2CurveKeyPair().getPublic().getEncoded()));
System.out.println("SM2 (sm2p256v1): " +
Base64.getEncoder().encodeToString(examples.generateSM2KeyPair().getPublic().getEncoded()));
System.out.println("ES (Ed25519): " +
Base64.getEncoder().encodeToString(examples.generateESKeyPair().getPublic().getEncoded()));
System.out.println("Other (Fallback, secp256r1): " +
Base64.getEncoder()
.encodeToString(examples.generateOtherEllipticCurveKeyPair().getPublic().getEncoded()));
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,288 @@
package com.example.crypto.algorithms;
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Base64;
/**
* EllipticCurve2 demonstrates real-world uses of elliptic curve algorithms,
* including key pair generation, key agreement (ECDH), digital signatures
* (ECDSA, EdDSA),
* and a simple simulation of ECIES (using ECDH + AES-GCM).
*
* Curve types shown include:
* - NIST (e.g., secp256r1)
* - SEC (e.g., secp256k1)
* - Brainpool (e.g., brainpoolP256r1)
* - CURVE25519 (for X25519 key agreement)
* - ES (e.g., Ed25519 for signatures)
* - Other fallback (e.g., secp256r1 for “OtherEllipticCurveType”)
*
* Best practices:
* - Use ephemeral keys and a strong RNG.
* - Use proper key agreement (with a KDF if needed) and digital signature
* schemes.
* - Avoid static key reuse or using weak curves.
*
* SAST/CBOM considerations:
* - Secure implementations use ephemeral keys and modern curves.
* - Insecure practices (e.g., static keys or reusing keys) must be flagged.
*/
public class EllipticCurve2 {
// static {
// // Register BouncyCastle provider for additional curves and algorithms.
// Security.addProvider(new BouncyCastleProvider());
// }
// ----------------------------
// 1. Key Pair Generation Examples
// ----------------------------
/**
* Generates a key pair using a NIST curve (secp256r1).
*/
public KeyPair generateNISTKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
return kpg.generateKeyPair();
}
/**
* Generates a key pair using a SEC curve (secp256k1).
*/
public KeyPair generateSECCurveKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECGenParameterSpec("secp256k1"), new SecureRandom());
return kpg.generateKeyPair();
}
/**
* Generates a key pair using a Brainpool curve (brainpoolP256r1).
*/
public KeyPair generateBrainpoolKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(new ECGenParameterSpec("brainpoolP256r1"), new SecureRandom());
return kpg.generateKeyPair();
}
/**
* Generates an X25519 key pair.
*/
public KeyPair generateX25519KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X25519", "BC");
return kpg.generateKeyPair();
}
/**
* Generates an Ed25519 key pair (used for signatures).
*/
public KeyPair generateEd25519KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519", "BC");
return kpg.generateKeyPair();
}
/**
* Generates a key pair for "OtherEllipticCurveType" as a fallback (using
* secp256r1).
*/
public KeyPair generateOtherEllipticCurveKeyPair() throws Exception {
return generateNISTKeyPair();
}
// ----------------------------
// 2. Key Agreement (ECDH) Examples
// ----------------------------
/**
* Performs ECDH key agreement using two ephemeral NIST key pairs.
* Secure Example: Uses ephemeral keys and a strong RNG.
*
* @return The shared secret.
*/
public byte[] performECDHKeyAgreement() throws Exception {
KeyPair aliceKP = generateNISTKeyPair();
KeyPair bobKP = generateNISTKeyPair();
KeyAgreement ka = KeyAgreement.getInstance("ECDH", "BC");
ka.init(aliceKP.getPrivate());
ka.doPhase(bobKP.getPublic(), true);
return ka.generateSecret();
}
/**
* Insecure ECDH Example: Uses a static key pair for both parties.
* SAST: Reusing the same key pair eliminates forward secrecy and is insecure.
*
* @return The (insecure) shared secret.
*/
public byte[] insecureECDHKeyAgreement() throws Exception {
KeyPair staticKP = generateNISTKeyPair();
KeyAgreement ka = KeyAgreement.getInstance("ECDH", "BC");
ka.init(staticKP.getPrivate());
ka.doPhase(staticKP.getPublic(), true);
return ka.generateSecret();
}
// ----------------------------
// 3. Digital Signature Examples
// ----------------------------
/**
* Generates an ECDSA signature using a NIST key pair.
* Secure Example.
*
* @param message The message to sign.
* @return The signature.
*/
public byte[] generateECDSASignature(byte[] message) throws Exception {
KeyPair kp = generateNISTKeyPair();
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initSign(kp.getPrivate());
signature.update(message);
return signature.sign();
}
/**
* Verifies an ECDSA signature using the corresponding NIST key pair.
*
* @param message The original message.
* @param signatureBytes The signature to verify.
* @param kp The key pair used for signing.
* @return True if the signature is valid.
*/
public boolean verifyECDSASignature(byte[] message, byte[] signatureBytes, KeyPair kp) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initVerify(kp.getPublic());
signature.update(message);
return signature.verify(signatureBytes);
}
/**
* Generates an Ed25519 signature.
* Secure Example: Ed25519 is a modern, high-performance signature scheme.
*
* @param message The message to sign.
* @return The signature.
*/
public byte[] generateEd25519Signature(byte[] message) throws Exception {
KeyPair kp = generateEd25519KeyPair();
Signature signature = Signature.getInstance("Ed25519", "BC");
signature.initSign(kp.getPrivate());
signature.update(message);
return signature.sign();
}
/**
* Verifies an Ed25519 signature.
*
* @param message The original message.
* @param signatureBytes The signature to verify.
* @param kp The key pair used for signing.
* @return True if the signature is valid.
*/
public boolean verifyEd25519Signature(byte[] message, byte[] signatureBytes, KeyPair kp) throws Exception {
Signature signature = Signature.getInstance("Ed25519", "BC");
signature.initVerify(kp.getPublic());
signature.update(message);
return signature.verify(signatureBytes);
}
// ----------------------------
// 4. ECIES-like Encryption (ECDH + AES-GCM)
// ----------------------------
/**
* A simple simulation of ECIES using ECDH for key agreement and AES-GCM for
* encryption.
* Secure Example: Uses ephemeral ECDH key pairs, a KDF to derive a symmetric
* key,
* and AES-GCM with a random nonce.
*
* @param plaintext The plaintext to encrypt.
* @return The concatenation of the ephemeral public key, IV, and ciphertext
* (Base64-encoded).
* @throws Exception if encryption fails.
*/
public String eciesEncryptionExample(byte[] plaintext) throws Exception {
// Generate ephemeral key pairs for two parties.
KeyPair senderKP = generateNISTKeyPair();
KeyPair receiverKP = generateNISTKeyPair();
// Perform ECDH key agreement.
KeyAgreement ka = KeyAgreement.getInstance("ECDH", "BC");
ka.init(senderKP.getPrivate());
ka.doPhase(receiverKP.getPublic(), true);
byte[] sharedSecret = ka.generateSecret();
// Derive a symmetric key from the shared secret using SHA-256 (first 16 bytes
// for AES-128).
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] derivedKey = digest.digest(sharedSecret);
derivedKey = Arrays.copyOf(derivedKey, 16);
SecretKey aesKey = new SecretKeySpec(derivedKey, "AES");
// Encrypt plaintext using AES-GCM with a random nonce.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
// For ECIES, include the sender's ephemeral public key with the output.
byte[] senderPub = senderKP.getPublic().getEncoded();
byte[] output = concatenate(senderPub, concatenate(iv, ciphertext));
return Base64.getEncoder().encodeToString(output);
}
// ----------------------------
// 5. Main Method for Demonstration
// ----------------------------
public static void main(String[] args) {
try {
EllipticCurve2 test = new EllipticCurve2();
// Key Agreement Example:
byte[] sharedSecret = test.performECDHKeyAgreement();
System.out.println("ECDH Shared Secret (Base64): " + Base64.getEncoder().encodeToString(sharedSecret));
// ECDSA Signature Example:
byte[] message = "Test message for ECDSA".getBytes();
KeyPair nistKP = test.generateNISTKeyPair();
byte[] ecdsaSig = test.generateECDSASignature(message);
boolean validSig = test.verifyECDSASignature(message, ecdsaSig, nistKP);
System.out.println("ECDSA Signature valid? " + validSig);
// Ed25519 Signature Example:
byte[] edSig = test.generateEd25519Signature(message);
KeyPair edKP = test.generateEd25519KeyPair();
boolean validEdSig = test.verifyEd25519Signature(message, edSig, edKP);
System.out.println("Ed25519 Signature valid? " + validEdSig);
// ECIES-like Encryption Example:
String eciesOutput = test.eciesEncryptionExample("Secret ECIES Message".getBytes());
System.out.println("ECIES-like Encrypted Output (Base64): " + eciesOutput);
} catch (Exception e) {
e.printStackTrace();
}
}
private 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;
}
}

View File

@@ -0,0 +1,182 @@
package com.example.crypto.algorithms;
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;
import java.util.Base64;
/**
* This class demonstrates several encryption schemes along with SAST/CBOM
* classification notes.
*
* Methods include:
*
* 1. simpleAESEncryption: Uses AES in GCM mode.
* - CBOM: AES-GCM is classified as secure (Parent: AEAD).
* - SAST: Secure symmetric encryption pattern; safe when used properly.
*
* 2. insecureAESWithECB: Uses AES in ECB mode.
* - CBOM: AES-ECB is classified as insecure (Parent: SymmetricEncryption).
* - SAST: Insecure encryption pattern; flagged as vulnerable due to lack of IV
* and predictable structure.
*
* 3. rsaOaepEncryption / rsaOaepDecryption: Use RSA with OAEP padding.
* - CBOM: RSA-OAEP is classified as secure for public-key encryption (Parent:
* Hybrid Cryptosystem).
* - SAST: Secure for small payloads/key encapsulation; must only encrypt small
* data blocks.
*
* 4. rsaKemEncryption: Demonstrates a key encapsulation mechanism (KEM) using
* RSA-OAEP.
* - CBOM: RSA-KEM is recognized as secure (Parent: RSA-OAEP based KEM).
* - SAST: Secure when used to encapsulate symmetric keys in a hybrid system.
*
* 5. hybridEncryption: Combines RSA-OAEP for key encapsulation with AES-GCM for
* data encryption.
* - CBOM: Hybrid encryption (Parent: RSA-OAEP + AES-GCM) is classified as
* secure.
* - SAST: Secure hybrid encryption pattern; recommended for large data
* encryption.
*/
public class Encryption1 {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
/**
* Simple AES-GCM encryption.
*
* SAST/CBOM Notes:
* - Algorithm: AES/GCM/NoPadding with a 256-bit key.
* - Parent Classification: AEAD (Authenticated Encryption with Associated
* Data).
* - SAST: Considered safe when properly implemented (uses IV and tag).
*/
public void simpleAESEncryption() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // 256-bit AES key.
SecretKey key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // 12-byte IV recommended for GCM.
new SecureRandom().nextBytes(iv);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); // 128-bit authentication tag.
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
byte[] encryptedData = cipher.doFinal("Sensitive Data".getBytes());
System.out.println("AES-GCM Encrypted Data: " + Base64.getEncoder().encodeToString(encryptedData));
}
/**
* Insecure AES encryption using ECB mode.
*
* SAST/CBOM Notes:
* - Algorithm: AES/ECB/NoPadding with a 256-bit key.
* - Parent Classification: SymmetricEncryption (ECB mode is inherently
* insecure).
* - SAST: Flagged as vulnerable; ECB mode does not use an IV and reveals data
* patterns.
*/
public void insecureAESWithECB() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // 256-bit AES key.
SecretKey key = keyGen.generateKey();
// AES/ECB mode is insecure due to the deterministic nature of the block cipher
// without an IV.
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal("Sensitive Data".getBytes());
System.out.println("AES-ECB Encrypted Data (Insecure): " + Base64.getEncoder().encodeToString(encryptedData));
}
/**
* RSA encryption using OAEP with SHA-256 and MGF1 padding.
*
* SAST/CBOM Notes:
* - Algorithm: RSA/ECB/OAEPWithSHA-256AndMGF1Padding.
* - Parent Classification: Hybrid Cryptosystem. RSA-OAEP is commonly used in
* hybrid schemes.
* - SAST: Secure for encrypting small payloads or for key encapsulation;
* caution when encrypting large data.
*/
public void rsaOaepEncryption(PublicKey publicKey, String data) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedData = cipher.doFinal(data.getBytes());
System.out.println("RSA-OAEP Encrypted Data: " + Base64.getEncoder().encodeToString(encryptedData));
}
/**
* RSA decryption using OAEP with SHA-256 and MGF1 padding.
*
* SAST/CBOM Notes:
* - Algorithm: RSA/ECB/OAEPWithSHA-256AndMGF1Padding.
* - Parent Classification: Hybrid Cryptosystem.
* - SAST: Secure when used with the correct corresponding private key.
*/
public void rsaOaepDecryption(PrivateKey privateKey, byte[] encryptedData) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
System.out.println("Decrypted RSA-OAEP Data: " + new String(decryptedData));
}
/**
* RSA-KEM encryption: encapsulates an AES key using RSA-OAEP.
*
* SAST/CBOM Notes:
* - Algorithm: RSA-OAEP used as a Key Encapsulation Mechanism (KEM) for an AES
* key.
* - Parent Classification: RSA-OAEP based KEM.
* - SAST: Recognized as a secure key encapsulation pattern; used as part of
* hybrid encryption schemes.
*/
public void rsaKemEncryption(PublicKey rsaPublicKey) throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // 256-bit AES key.
SecretKey aesKey = keyGen.generateKey();
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
byte[] encryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded());
System.out.println("RSA-KEM Encrypted AES Key: " + Base64.getEncoder().encodeToString(encryptedAesKey));
}
/**
* Hybrid encryption: combines RSA-OAEP for key encapsulation with AES-GCM for
* data encryption.
*
* SAST/CBOM Notes:
* - Algorithms: RSA-OAEP (for encrypting the AES key) and AES-GCM (for
* encrypting the data).
* - Parent Classification: Hybrid Cryptosystem (RSA-OAEP + AES-GCM).
* - SAST: This pattern is considered secure when implemented correctly;
* recommended for large data encryption.
*/
public void hybridEncryption(PublicKey rsaPublicKey, String data) throws Exception {
// Generate a 256-bit AES key for symmetric encryption.
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey aesKey = keyGen.generateKey();
// Encrypt the AES key using RSA-OAEP.
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
byte[] encryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded());
// Encrypt the actual data using AES-GCM.
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // 12-byte IV recommended for GCM.
new SecureRandom().nextBytes(iv);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
byte[] encryptedData = aesCipher.doFinal(data.getBytes());
System.out.println(
"Hybrid Encryption - Encrypted AES Key: " + Base64.getEncoder().encodeToString(encryptedAesKey));
System.out.println("Hybrid Encryption - Encrypted Data: " + Base64.getEncoder().encodeToString(encryptedData));
}
}

View File

@@ -0,0 +1,198 @@
package com.example.crypto.algorithms;
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
import java.util.Arrays;
import javax.crypto.Mac;
/**
* This class demonstrates encryption schemes using elliptic-curve
* Diffie-Hellman (ECDH)
* and hybrid encryption methods, including a post-quantum hybrid scheme.
*
* SAST/CBOM Classification:
*
* 1. EC Key Generation & ECDH Key Agreement:
* - Parent Classification: Asymmetric Key Generation / Key Agreement.
* - SAST: Secure when using established curves (secp256r1) and reputable
* providers (BouncyCastle).
*
* 2. ECDH Hybrid Encryption:
* - Parent Classification: Hybrid Cryptosystem (ECDH + AEAD).
* - SAST: Uses ECDH for key agreement and AES/GCM for encryption. However, the
* derivation of an AES key
* by applying a single SHA-256 hash to the shared secret may be flagged as a
* weak key derivation method.
* A dedicated KDF (e.g., HKDF) is recommended.
*
* 3. Post-Quantum Hybrid Encryption:
* - Parent Classification: Hybrid Cryptosystem (Classical ECDH + Post-Quantum
* Secret + KDF + AEAD).
* - SAST: Combining classical and post-quantum components is advanced and
* secure if implemented properly.
* The custom HKDF expand function provided here is simplistic and may be
* flagged in a CBOM analysis;
* a standard HKDF library should be used in production.
*/
public class Encryption2 {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
/**
* Generates an Elliptic Curve (EC) key pair using the secp256r1 curve.
*
* SAST/CBOM Notes:
* - Algorithm: EC key pair generation.
* - Parent Classification: Asymmetric Key Generation.
* - SAST: Considered secure when using strong randomness and a reputable
* provider.
*
* @return an EC KeyPair.
*/
public KeyPair generateECKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
keyPairGenerator.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
return keyPairGenerator.generateKeyPair();
}
/**
* Derives a shared secret using Elliptic Curve Diffie-Hellman (ECDH).
*
* SAST/CBOM Notes:
* - Algorithm: ECDH key agreement.
* - Parent Classification: Asymmetric Key Agreement.
* - SAST: Secure when both parties use strong EC keys and proper randomness.
*
* @param privateKey the private key of one party.
* @param publicKey the public key of the other party.
* @return the derived shared secret as a byte array.
*/
public byte[] deriveSharedSecret(PrivateKey privateKey, PublicKey publicKey) throws Exception {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
return keyAgreement.generateSecret();
}
/**
* Performs hybrid encryption using ECDH to derive a shared secret, then derives
* an AES key
* by hashing the shared secret with SHA-256, and finally encrypts the data with
* AES-GCM.
*
* SAST/CBOM Notes:
* - Parent Classification: Hybrid Cryptosystem (ECDH + AES-GCM).
* - SAST: While ECDH and AES-GCM are secure, the key derivation method here (a
* single SHA-256 hash)
* is not as robust as using a dedicated KDF. This approach may be flagged and
* is recommended for
* improvement.
*
* @param recipientPublicKey the recipient's public EC key.
* @param data the plaintext data to encrypt.
*/
public void ecdhHybridEncryption(PublicKey recipientPublicKey, String data) throws Exception {
// Generate an ephemeral EC key pair for the sender.
KeyPair senderKeyPair = generateECKeyPair();
// Derive the shared secret using ECDH.
byte[] sharedSecret = deriveSharedSecret(senderKeyPair.getPrivate(), recipientPublicKey);
// Derive an AES key by hashing the shared secret with SHA-256.
// SAST Note: Using a direct hash for key derivation is simplistic and may be
// flagged.
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] aesKeyBytes = sha256.digest(sharedSecret);
// Use the first 16 bytes (128 bits) as the AES key.
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, 0, 16, "AES");
// Encrypt the data using AES-GCM.
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // 12-byte IV recommended for GCM.
new SecureRandom().nextBytes(iv);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); // 128-bit authentication tag.
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
byte[] encryptedData = aesCipher.doFinal(data.getBytes());
System.out.println(
"ECDH Hybrid Encryption - Encrypted Data: " + Base64.getEncoder().encodeToString(encryptedData));
}
/**
* Performs post-quantum hybrid encryption by combining a classical ECDH-derived
* secret with a
* post-quantum shared secret. The two secrets are combined using a custom HKDF
* expansion, and the
* derived key is used to encrypt data with AES-GCM.
*
* SAST/CBOM Notes:
* - Parent Classification: Hybrid Cryptosystem (Classical ECDH + Post-Quantum
* Secret + KDF + AES-GCM).
* - SAST: The combination of classical and post-quantum secrets is a modern
* approach. However, the
* custom HKDF expand function is simplistic and may be flagged as insecure. Use
* a standard HKDF
* implementation in production.
*
* @param ecPublicKey the recipient's EC public key.
* @param pqSharedSecret the post-quantum shared secret from a separate
* algorithm.
*/
public void postQuantumHybridEncryption(PublicKey ecPublicKey, byte[] pqSharedSecret) throws Exception {
// Step 1: Perform classical ECDH key agreement to derive a shared secret.
byte[] ecdhSharedSecret = deriveSharedSecret(generateECKeyPair().getPrivate(), ecPublicKey);
// Step 2: Combine the ECDH secret and the post-quantum secret using a
// simplified HKDF expansion.
// SAST Note: This custom HKDF implementation is minimal and does not follow the
// full HKDF spec.
byte[] combinedSecret = hkdfExpand(ecdhSharedSecret, pqSharedSecret, 32);
// Use the first 16 bytes as the AES key (128-bit key).
SecretKey aesKey = new SecretKeySpec(combinedSecret, 0, 16, "AES");
// Step 3: Encrypt the data using AES-GCM.
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // 12-byte IV recommended for GCM.
new SecureRandom().nextBytes(iv);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
byte[] encryptedData = aesCipher.doFinal("Post-Quantum Hybrid Encryption Data".getBytes());
System.out.println("Post-Quantum Hybrid Encryption - Encrypted Data: "
+ Base64.getEncoder().encodeToString(encryptedData));
}
/**
* A simplified HKDF expansion function that uses HMAC-SHA256 to derive a key of
* a desired length.
*
* SAST/CBOM Notes:
* - Parent Classification: Key Derivation Function (KDF).
* - SAST: Custom KDF implementations are risky if not thoroughly vetted. This
* simple HKDF expand
* function lacks the full HKDF mechanism (e.g., multiple iterations, info, and
* context parameters)
* and may be flagged. It is recommended to use a standardized HKDF library.
*
* @param inputKey the input key material.
* @param salt a salt value (here, the post-quantum shared secret is used as
* the salt).
* @param length the desired length of the derived key.
* @return a derived key of the specified length.
*/
private byte[] hkdfExpand(byte[] inputKey, byte[] salt, int length) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKey secretKey = new SecretKeySpec(salt, "HmacSHA256");
hmac.init(secretKey);
byte[] extractedKey = hmac.doFinal(inputKey);
return Arrays.copyOf(extractedKey, length);
}
}

View File

@@ -0,0 +1,341 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.crypto.digests.SHA3Digest;
// import org.bouncycastle.crypto.digests.Blake2bDigest;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.util.Base64;
import java.util.Properties;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.PBEKeySpec;
/**
* This class demonstrates various hashing, HMAC, and password hashing
* techniques.
*
* SAST/CBOM Classification Notes:
*
* 1. simpleSHA256Hash:
* - Parent Classification: Cryptographic Hash Function.
* - SAST: Uses SHA-256, which is widely regarded as secure.
*
* 2. insecureMD5Hash:
* - Parent Classification: Cryptographic Hash Function.
* - SAST: MD5 is cryptographically broken and should be flagged as insecure.
*
* 3. hashWithBouncyCastleSHA3:
* - Parent Classification: Cryptographic Hash Function (SHA3).
* - SAST: Uses SHA3-256 from BouncyCastle; considered secure.
*
* 4. hashWithBouncyCastleBlake2b:
* - Parent Classification: Cryptographic Hash Function (BLAKE2).
* - SAST: Uses BLAKE2b-512; considered secure if used correctly.
*
* 5. hashAndSign & verifyHashSignature:
* - Parent Classification: Digital Signature (RSA-based).
* - SAST: Uses SHA256withRSA for signing and verification; secure if key
* management is proper.
*
* 6. hashForDataIntegrityCheck:
* - Parent Classification: Data Integrity Check.
* - SAST: Uses SHA-256 to verify integrity; considered secure.
*
* 7. hashWithVariousAlgorithms:
* - Parent Classification: Cryptographic Hash Function.
* - SAST: Iterates through multiple algorithms; insecure algorithms (MD5,
* SHA-1) may be flagged.
*
* 8. hmacWithVariousAlgorithms:
* - Parent Classification: Message Authentication Code (MAC).
* - SAST: Iterates through various HMAC algorithms; HmacSHA1 is considered
* weaker than SHA256 and above.
*
* 9. hashForPasswordStorage:
* - Parent Classification: Password-Based Key Derivation Function (PBKDF).
* - SAST: Uses PBKDF2WithHmacSHA256 with salt and iteration count; considered
* secure,
* though iteration counts should be reviewed against current standards.
*
* 10. hashFromUnknownConfig:
* - Parent Classification: Dynamic Cryptographic Hash Function.
* - SAST: Loading the hash algorithm from an external configuration introduces
* risk of misconfiguration.
*
* 11. insecureHashBasedRNG:
* - Parent Classification: Pseudo-Random Number Generator (PRNG) using hash.
* - SAST: Uses a fixed seed with various hash algorithms; flagged as insecure
* due to predictability.
*/
public class Hash {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
/**
* Computes a SHA-256 hash of static test data.
*
* CBOM/SAST Classification:
* - Uses SHA-256: Classified as secure.
*/
public void simpleSHA256Hash() throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest("Simple Test Data".getBytes());
System.out.println("SHA-256 Hash: " + Base64.getEncoder().encodeToString(hash));
}
/**
* Computes an MD5 hash of static data.
*
* CBOM/SAST Classification:
* - Uses MD5: Classified as insecure.
* - SAST: MD5 is deprecated for cryptographic purposes due to collision
* vulnerabilities.
*/
public void insecureMD5Hash() throws Exception {
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
byte[] hash = md5Digest.digest("Weak Hash Example".getBytes());
System.out.println("MD5 Hash (Insecure): " + Base64.getEncoder().encodeToString(hash));
}
// /**
// * Computes a SHA3-256 hash using BouncyCastle's SHA3Digest.
// *
// * CBOM/SAST Classification:
// * - Uses SHA3-256: Classified as secure.
// * - SAST: BouncyCastle's implementation is considered reliable.
// */
// public void hashWithBouncyCastleSHA3(String input) {
// SHA3Digest digest = new SHA3Digest(256);
// byte[] inputBytes = input.getBytes();
// digest.update(inputBytes, 0, inputBytes.length);
// byte[] hash = new byte[digest.getDigestSize()];
// digest.doFinal(hash, 0);
// System.out.println("SHA3-256 (BC) Hash: " + Base64.getEncoder().encodeToString(hash));
// }
// /**
// * Computes a BLAKE2b-512 hash using BouncyCastle's Blake2bDigest.
// *
// * CBOM/SAST Classification:
// * - Uses BLAKE2b-512: Classified as secure.
// * - SAST: BLAKE2b is modern and fast, considered secure when used correctly.
// */
// public void hashWithBouncyCastleBlake2b(String input) {
// Blake2bDigest digest = new Blake2bDigest(512);
// byte[] inputBytes = input.getBytes();
// digest.update(inputBytes, 0, inputBytes.length);
// byte[] hash = new byte[digest.getDigestSize()];
// digest.doFinal(hash, 0);
// System.out.println("BLAKE2b-512 (BC) Hash: " + Base64.getEncoder().encodeToString(hash));
// }
/**
* Signs the hash of the input using SHA256withRSA.
*
* CBOM/SAST Classification:
* - Digital Signature (RSA): Classified as secure if keys are managed
* correctly.
* - SAST: The combination of SHA256 and RSA is a standard and secure pattern.
*
* @param input The input data to be signed.
* @param privateKey The RSA private key used for signing.
*/
public void hashAndSign(String input, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(input.getBytes());
byte[] signedData = signature.sign();
System.out.println("Signed Hash: " + Base64.getEncoder().encodeToString(signedData));
}
/**
* Verifies the signature of the input data.
*
* CBOM/SAST Classification:
* - Digital Signature Verification: Classified as secure when using
* SHA256withRSA.
* - SAST: Should correctly verify that the signed hash matches the input.
*
* @param input The original input data.
* @param signedHash The signed hash to verify.
* @param publicKey The RSA public key corresponding to the private key that
* signed the data.
* @return true if the signature is valid, false otherwise.
*/
public boolean verifyHashSignature(String input, byte[] signedHash, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(input.getBytes());
return signature.verify(signedHash);
}
/**
* Computes a SHA-256 hash for data integrity checking and compares it with an
* expected hash.
*
* CBOM/SAST Classification:
* - Data Integrity: Uses SHA-256 for integrity checks, which is secure.
* - SAST: A correct implementation for verifying data has not been tampered
* with.
*
* @param data The input data.
* @param expectedHash The expected Base64-encoded hash.
*/
public void hashForDataIntegrityCheck(String data, String expectedHash) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data.getBytes());
String computedHash = Base64.getEncoder().encodeToString(hash);
System.out.println("Computed Hash: " + computedHash);
System.out.println("Validation: " + (computedHash.equals(expectedHash) ? "Pass" : "Fail"));
}
/**
* Computes hashes of the input data using various algorithms.
*
* CBOM/SAST Classification:
* - Cryptographic Hash Functions: Iterates through multiple hash functions.
* - SAST: While many are secure (e.g., SHA-256, SHA-512, SHA3), MD5 and SHA-1
* are insecure
* and should be flagged if used in security-critical contexts.
*
* @param input The input data to hash.
*/
public void hashWithVariousAlgorithms(String input) throws Exception {
String[] algorithms = { "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA3-256", "SHA3-512",
"BLAKE2B-512", "BLAKE2S-256", "MD5" };
for (String algorithm : algorithms) {
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] hash = digest.digest(input.getBytes());
System.out.println(algorithm + " Hash: " + Base64.getEncoder().encodeToString(hash));
}
}
/**
* Computes HMACs of the input data using various algorithms.
*
* CBOM/SAST Classification:
* - Message Authentication Code (MAC): Iterates through different HMAC
* algorithms.
* - SAST: HmacSHA256, HmacSHA384, HmacSHA512, HmacSHA3-256, and HmacSHA3-512
* are secure;
* HmacSHA1 is considered less secure and may be flagged.
*
* @param input The input data.
* @param key The secret key used for HMAC computation.
*/
public void hmacWithVariousAlgorithms(String input, byte[] key) throws Exception {
String[] algorithms = { "HmacSHA1", "HmacSHA256", "HmacSHA384", "HmacSHA512", "HmacSHA3-256", "HmacSHA3-512" };
for (String algorithm : algorithms) {
Mac mac = Mac.getInstance(algorithm);
SecretKey secretKey = new SecretKeySpec(key, algorithm);
mac.init(secretKey);
byte[] hmac = mac.doFinal(input.getBytes());
System.out.println(algorithm + " HMAC: " + Base64.getEncoder().encodeToString(hmac));
}
}
/**
* Computes a PBKDF2 hash for password storage.
*
* CBOM/SAST Classification:
* - Password-Based Key Derivation Function (PBKDF): Uses PBKDF2WithHmacSHA256.
* - SAST: Considered secure when using a strong salt and an appropriate
* iteration count.
* Note: The iteration count (10000) should be reviewed against current security
* standards.
*
* @param password The password to hash.
*/
public void hashForPasswordStorage(String password) throws Exception {
byte[] salt = generateSecureSalt(16);
// 10,000 iterations and a 256-bit derived key.
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
System.out.println("PBKDF2 Hash: " + Base64.getEncoder().encodeToString(hash));
}
/**
* Dynamically loads a hash algorithm from configuration and computes a hash.
*
* CBOM/SAST Classification:
* - Dynamic Cryptographic Hash Selection: Algorithm is loaded from a config
* file.
* - SAST: May be flagged as risky because an insecure or unintended algorithm
* might be chosen.
*/
public void hashFromUnknownConfig() throws Exception {
String algorithm = loadHashAlgorithmFromConfig("config.properties");
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] hash = digest.digest("Config-based Hashing".getBytes());
System.out.println("Dynamically Loaded Hash Algorithm (" + algorithm + "): " +
Base64.getEncoder().encodeToString(hash));
}
/**
* Demonstrates an insecure method for generating pseudo-random bytes by using a
* fixed seed with hash algorithms.
*
* CBOM/SAST Classification:
* - Insecure RNG: Uses a fixed seed with various hash algorithms.
* - SAST: This approach is insecure because it produces predictable output and
* should be flagged.
*/
public void insecureHashBasedRNG() throws Exception {
String[] algorithms = { "SHA-256", "SHA-512", "SHA3-256", "SHA3-512" };
for (String algorithm : algorithms) {
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] seed = "fixed-seed".getBytes(); // Fixed seed: insecure and predictable.
digest.update(seed);
byte[] pseudoRandomBytes = digest.digest();
System.out.println("Insecure RNG using " + algorithm + ": " +
Base64.getEncoder().encodeToString(pseudoRandomBytes));
}
}
/**
* Loads the hash algorithm from an external configuration file.
*
* CBOM/SAST Classification:
* - Dynamic Configuration: External config loading.
* - SAST: The use of external configuration may introduce risks if the config
* file is compromised.
*
* @param configPath Path to the configuration file.
* @return The hash algorithm to be used (default is SHA-256).
*/
private String loadHashAlgorithmFromConfig(String configPath) {
Properties properties = new Properties();
try (FileInputStream fis = new FileInputStream(configPath)) {
properties.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
return properties.getProperty("hash.algorithm", "SHA-256");
}
/**
* Generates a secure salt using a cryptographically strong random number
* generator.
*
* CBOM/SAST Classification:
* - Secure Salt Generation: Uses SecureRandom.
* - SAST: This is a best-practice approach for generating salts for password
* hashing.
*
* @param length The desired salt length.
* @return A byte array representing the salt.
*/
private byte[] generateSecureSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}

View File

@@ -0,0 +1,293 @@
package com.example.crypto.artifacts;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;
import java.util.Base64;
import java.util.random.*;
import java.util.Properties;
import java.util.Random;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class IVArtifact {
// static {
// Security.addProvider(new BouncyCastleProvider()); // Ensure BouncyCastle is available
// }
/**
* Simple Case: Generates a secure IV and encrypts with AES/CBC/PKCS5Padding.
*/
public void simpleIVEncryption() throws Exception {
SecretKey key = generateAESKey();
IvParameterSpec ivSpec = new IvParameterSpec(secureIV(16));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal("Simple Test Data".getBytes());
}
public void encryptWithIV(byte[] plaintext, SecretKey key, IvParameterSpec ivSpec, String cipherAlgorithm)
throws Exception {
Cipher cipher = Cipher.getInstance(cipherAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
}
public void complexIVFlow() {
IvParameterSpec ivSpec = new IvParameterSpec(useSecureMethod() ? secureIV(16) : insecureIV(16));
processIV(ivSpec);
// Example dynamic cipher selection with IV usage
String cipherAlgorithm = loadCipherAlgorithm();
try {
encryptWithIV("Sensitive Data".getBytes(), generateAESKey(), ivSpec, cipherAlgorithm);
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean useSecureMethod() {
return System.currentTimeMillis() % 2 == 0;
}
private void processIV(IvParameterSpec ivSpec) {
String ivBase64 = Base64.getEncoder().encodeToString(ivSpec.getIV());
}
private String loadCipherAlgorithm() {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("crypto-config.properties"));
} catch (IOException e) {
e.printStackTrace();
}
return properties.getProperty("cipher.algorithm", "AES/CBC/PKCS5Padding");
}
private SecretKey generateAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
return keyGen.generateKey();
}
private byte[] secureIV(int length) {
byte[] iv = new byte[length];
new SecureRandom().nextBytes(iv);
return iv;
}
private byte[] insecureIV(int length) {
byte[] iv = new byte[length];
new Random().nextBytes(iv);
return iv;
}
// -------------------------------
// 1. Direct Fixed IV Usage
// -------------------------------
/**
* Encrypts plaintext using AES-GCM with a fixed IV (all zeros).
* This is an insecure practice as IV reuse in AES-GCM undermines
* confidentiality and integrity.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] encryptWithFixedIV(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] fixedIV = new byte[12]; // 12-byte fixed IV (all zeros)
GCMParameterSpec spec = new GCMParameterSpec(128, fixedIV);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
return cipher.doFinal(plaintext);
}
// -------------------------------
// 2. Cached IV Usage
// -------------------------------
// Cache an IV for reuse in multiple encryptions (insecure)
private byte[] cachedIV = null;
/**
* Encrypts plaintext using AES-GCM with an IV cached from the first call.
* Reusing the same IV across multiple encryptions is insecure.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] encryptWithCachedIV(SecretKey key, byte[] plaintext) throws Exception {
if (cachedIV == null) {
cachedIV = new byte[12];
new SecureRandom().nextBytes(cachedIV);
}
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, cachedIV);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
return cipher.doFinal(plaintext);
}
// -------------------------------
// 3. Indirect IV Reuse via Deterministic Derivation
// -------------------------------
/**
* Encrypts plaintext using AES-GCM with an IV derived deterministically from a
* constant.
* This method computes a SHA-256 hash of a constant string and uses the first
* 12 bytes as the IV.
* Such derived IVs are fixed and must not be reused.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] encryptWithDerivedIV(SecretKey key, byte[] plaintext) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] constantHash = digest.digest("fixedConstant".getBytes("UTF-8"));
byte[] derivedIV = Arrays.copyOf(constantHash, 12); // Deterministically derived IV
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, derivedIV);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
return cipher.doFinal(plaintext);
}
// -------------------------------
// 4. Reusing a Single IV Across Multiple Messages
// -------------------------------
/**
* Encrypts an array of plaintext messages using AES-GCM with the same IV for
* every message.
* Reusing an IV across messages is insecure in authenticated encryption
* schemes.
*
* @param key The AES key.
* @param plaintexts An array of plaintext messages.
* @return An array of ciphertexts.
* @throws Exception if encryption fails.
*/
public byte[][] encryptMultipleMessagesWithSameIV(SecretKey key, byte[][] plaintexts) throws Exception {
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv); // Generate once and reuse
byte[][] ciphertexts = new byte[plaintexts.length][];
for (int i = 0; i < plaintexts.length; i++) {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
ciphertexts[i] = cipher.doFinal(plaintexts[i]);
}
return ciphertexts;
}
/**
* Encrypts the given plaintext using AES-GCM with the provided key and IV.
*
* @param key The AES key.
* @param ivSpec The IV specification.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext (IV is not prepended here for clarity).
* @throws Exception if encryption fails.
*/
public byte[] encrypt(SecretKey key, IvParameterSpec ivSpec, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Use 128-bit authentication tag length.
GCMParameterSpec spec = new GCMParameterSpec(128, ivSpec.getIV());
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
return cipher.doFinal(plaintext);
}
/**
* Example 1: Reuses the same IvParameterSpec object across two encryption
* calls.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return An array containing two ciphertexts generated with the same
* IvParameterSpec.
* @throws Exception if encryption fails.
*/
public byte[][] encryptUsingSameIvParameterSpec(SecretKey key, byte[] plaintext) throws Exception {
// Fixed IV (all zeros for demonstration purposes; insecure in production)
byte[] fixedIV = new byte[12];
IvParameterSpec fixedIvSpec = new IvParameterSpec(fixedIV);
// Encrypt the plaintext twice using the same IvParameterSpec.
byte[] ciphertext1 = encrypt(key, fixedIvSpec, plaintext);
byte[] ciphertext2 = encrypt(key, fixedIvSpec, plaintext);
return new byte[][] { ciphertext1, ciphertext2 };
}
/**
* Example 2: Creates two different IvParameterSpec objects that share the same
* underlying IV array.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return An array containing two ciphertexts generated with two
* IvParameterSpec objects
* constructed from the same IV array.
* @throws Exception if encryption fails.
*/
public byte[][] encryptUsingDifferentIvSpecSameIVArray(SecretKey key, byte[] plaintext) throws Exception {
// Create a fixed IV array (all zeros for demonstration; insecure in production)
byte[] fixedIV = new byte[12];
// Create two distinct IvParameterSpec objects from the same IV array reference.
IvParameterSpec ivSpec1 = new IvParameterSpec(fixedIV);
IvParameterSpec ivSpec2 = new IvParameterSpec(fixedIV);
// Encrypt the plaintext twice.
byte[] ciphertext1 = encrypt(key, ivSpec1, plaintext);
byte[] ciphertext2 = encrypt(key, ivSpec2, plaintext);
return new byte[][] { ciphertext1, ciphertext2 };
}
// -------------------------------
// Main Method for Demonstration
// -------------------------------
public static void main(String[] args) {
try {
IVArtifact test = new IVArtifact();
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256, new SecureRandom());
SecretKey key = kg.generateKey();
byte[] plaintext = "Sensitive Data".getBytes();
// Example 1: Fixed IV usage
byte[] fixedIVCipher1 = test.encryptWithFixedIV(key, plaintext);
byte[] fixedIVCipher2 = test.encryptWithFixedIV(key, plaintext);
System.out.println("Fixed IV Encryption 1: " + Base64.getEncoder().encodeToString(fixedIVCipher1));
System.out.println("Fixed IV Encryption 2: " + Base64.getEncoder().encodeToString(fixedIVCipher2));
// Example 2: Cached IV usage
byte[] cachedIVCipher1 = test.encryptWithCachedIV(key, plaintext);
byte[] cachedIVCipher2 = test.encryptWithCachedIV(key, plaintext);
System.out.println("Cached IV Encryption 1: " + Base64.getEncoder().encodeToString(cachedIVCipher1));
System.out.println("Cached IV Encryption 2: " + Base64.getEncoder().encodeToString(cachedIVCipher2));
// Example 3: Indirect IV (derived)
byte[] derivedIVCipher = test.encryptWithDerivedIV(key, plaintext);
System.out.println("Derived IV Encryption: " + Base64.getEncoder().encodeToString(derivedIVCipher));
// Example 4: Reusing the same IV across multiple messages
byte[][] messages = { "Message One".getBytes(), "Message Two".getBytes(), "Message Three".getBytes() };
byte[][] multiCiphers = test.encryptMultipleMessagesWithSameIV(key, messages);
for (int i = 0; i < multiCiphers.length; i++) {
System.out.println("Multi-message Encryption " + (i + 1) + ": "
+ Base64.getEncoder().encodeToString(multiCiphers[i]));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,292 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
// import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Base64;
/**
* KeyAgreementHybridCryptosystem demonstrates two hybrid cryptosystems:
*
* 1. ECDH + AES-GCM:
* - Secure Flow: Uses ephemeral ECDH key pairs on secp256r1, applies a simple
* KDF (SHA-256)
* to derive a 128-bit AES key, and uses AES-GCM with a random 12-byte nonce.
* - Insecure Flow: Reuses a static key pair, uses raw shared secret truncation,
* and employs
* a fixed (zero) IV.
*
* 2. X25519 + ChaCha20-Poly1305:
* - Secure Flow: Uses ephemeral X25519 key pairs, applies a KDF (SHA-256) to
* derive a 256-bit key,
* and uses ChaCha20-Poly1305 with a random nonce.
* - Insecure Flow: Reuses a static key pair, directly truncates the shared
* secret without a proper KDF,
* and uses a fixed nonce.
*
* SAST/CBOM Notes:
* - Secure flows use proper ephemeral key generation, a simple KDF, and random
* nonces.
* - Insecure flows use static keys, fixed nonces, and raw shared secret
* truncation.
*/
public class KeyAgreementHybridCryptosystem {
// static {
// Security.addProvider(new BouncyCastleProvider());
// Security.addProvider(new BouncyCastlePQCProvider());
// }
// ---------- 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.
*/
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.
*/
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. ECDH + AES-GCM Flows
// ===============================================
/**
* Secure hybrid encryption using ECDH and AES-GCM.
* Uses ephemeral key pairs, applies a simple KDF to derive a 128-bit AES key,
* and uses AES-GCM with a random 12-byte nonce.
*/
public byte[] secureECDH_AESGCMEncryption(byte[] plaintext) throws Exception {
KeyPair aliceKP = generateECDHKeyPair();
KeyPair bobKP = generateECDHKeyPair();
byte[] aliceSecret = deriveSharedSecret(aliceKP.getPrivate(), bobKP.getPublic(), "ECDH");
byte[] aesKeyBytes = simpleKDF(aliceSecret, 16);
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
return concatenate(iv, ciphertext);
}
/**
* Insecure hybrid encryption using ECDH and AES-GCM.
* Reuses a static key pair, uses raw shared secret truncation without a proper
* KDF,
* and employs a fixed IV (all zeros).
*/
public byte[] insecureECDH_AESGCMEncryption(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 (all zeros)
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, fixedIV);
cipher.init(Cipher.ENCRYPT_MODE, aesKey, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
return concatenate(fixedIV, ciphertext);
}
// ===============================================
// 2. X25519 + ChaCha20-Poly1305 Flows
// ===============================================
/**
* Secure hybrid encryption using X25519 and ChaCha20-Poly1305.
* Uses ephemeral key pairs, applies a KDF (SHA-256) to derive a 256-bit key,
* and uses ChaCha20-Poly1305 with a random 12-byte nonce.
*/
public byte[] secureX25519_Chacha20Poly1305Encryption(byte[] plaintext) throws Exception {
KeyPair aliceKP = generateX25519KeyPair();
KeyPair bobKP = generateX25519KeyPair();
byte[] sharedSecret = deriveSharedSecret(aliceKP.getPrivate(), bobKP.getPublic(), "X25519");
byte[] chachaKeyBytes = MessageDigest.getInstance("SHA-256").digest(sharedSecret);
SecretKey chachaKey = new SecretKeySpec(chachaKeyBytes, "ChaCha20");
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305", "BC");
byte[] nonce = new byte[12];
new SecureRandom().nextBytes(nonce);
cipher.init(Cipher.ENCRYPT_MODE, chachaKey, new IvParameterSpec(nonce));
byte[] ciphertext = cipher.doFinal(plaintext);
return concatenate(nonce, ciphertext);
}
/**
* Insecure hybrid encryption using X25519 and ChaCha20-Poly1305.
* Reuses a static key pair, directly truncates the shared secret without a
* proper KDF,
* and employs a fixed nonce.
*/
public byte[] insecureX25519_Chacha20Poly1305Encryption(byte[] plaintext) throws Exception {
KeyPair staticKP = generateX25519KeyPair();
byte[] sharedSecret = deriveSharedSecret(staticKP.getPrivate(), staticKP.getPublic(), "X25519");
byte[] chachaKeyBytes = Arrays.copyOf(sharedSecret, 32);
SecretKey chachaKey = new SecretKeySpec(chachaKeyBytes, "ChaCha20");
byte[] fixedNonce = new byte[12]; // fixed nonce (all zeros)
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305", "BC");
cipher.init(Cipher.ENCRYPT_MODE, chachaKey, new IvParameterSpec(fixedNonce));
byte[] ciphertext = cipher.doFinal(plaintext);
return concatenate(fixedNonce, ciphertext);
}
// ===============================================
// 3. Dynamic Hybrid Selection
// ===============================================
/**
* Dynamically selects a hybrid encryption flow based on a configuration
* property.
* If the config is unknown, defaults to an insecure flow.
*/
public String dynamicHybridEncryption(String config, byte[] plaintext) throws Exception {
byte[] result;
if ("secureECDH".equalsIgnoreCase(config)) {
result = secureECDH_AESGCMEncryption(plaintext);
} else if ("insecureECDH".equalsIgnoreCase(config)) {
result = insecureECDH_AESGCMEncryption(plaintext);
} else if ("secureX25519".equalsIgnoreCase(config)) {
result = secureX25519_Chacha20Poly1305Encryption(plaintext);
} else if ("insecureX25519".equalsIgnoreCase(config)) {
result = insecureX25519_Chacha20Poly1305Encryption(plaintext);
} else {
// Fallback to insecure ECDH flow.
result = insecureECDH_AESGCMEncryption(plaintext);
}
return Base64.getEncoder().encodeToString(result);
}
// ===============================================
// 4. Further Key Derivation from Symmetric Keys
// ===============================================
/**
* Derives two keys from a symmetric key using PBKDF2, then uses one key for
* AES-GCM encryption
* and the other for computing a MAC over the ciphertext.
*/
public byte[] furtherUseSymmetricKeyForKeyDerivation(SecretKey key, byte[] plaintext) throws Exception {
String keyAsString = Base64.getEncoder().encodeToString(key.getEncoded());
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(keyAsString.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] derived = factory.generateSecret(spec).getEncoded();
byte[] encKeyBytes = Arrays.copyOfRange(derived, 0, 16);
byte[] macKeyBytes = Arrays.copyOfRange(derived, 16, 32);
SecretKey encryptionKey = new SecretKeySpec(encKeyBytes, "AES");
SecretKey derivedMacKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
GCMParameterSpec specGcm = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, specGcm);
byte[] ciphertext = cipher.doFinal(plaintext);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(derivedMacKey);
byte[] computedMac = mac.doFinal(ciphertext);
byte[] output = new byte[ciphertext.length + computedMac.length];
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
storeOutput(output);
return output;
}
// ===============================================
// 5. Output/Storage Methods
// ===============================================
/**
* Stores the output securely.
*/
public void storeOutput(byte[] output) {
String stored = Base64.getEncoder().encodeToString(output);
// In production, this value would be stored or transmitted securely.
}
// ===============================================
// 6. Helper Methods for Key/Nonce Generation
// ===============================================
/**
* Generates a secure 256-bit AES key.
*/
public SecretKey generateAESKey() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256, new SecureRandom());
return kg.generateKey();
}
/**
* Generates a random salt.
*/
private byte[] generateSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}

View File

@@ -0,0 +1,86 @@
package com.example.crypto.artifacts;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.*;
import java.security.spec.*;
import java.util.Properties;
import java.io.FileInputStream;
public class KeyArtifact {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
public void generateSymmetricKeys() throws NoSuchAlgorithmException {
// AES Key Generation (Default Provider)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey aesKeyJDK = keyGen.generateKey();
// AES Key Generation (BouncyCastle)
keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey aesKeyBC = keyGen.generateKey();
}
public void generateAsymmetricKeys() throws NoSuchAlgorithmException {
// RSA Key Generation (JDK Default)
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair rsaPairJDK = keyPairGen.generateKeyPair();
// RSA Key Generation (BouncyCastle)
keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair rsaPairBC = keyPairGen.generateKeyPair();
// EC Key Generation
keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(256);
KeyPair ecPair = keyPairGen.generateKeyPair();
}
public void importExportRSAKeys(KeyPair keyPair) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Export Public Key
byte[] pubKeyBytes = keyPair.getPublic().getEncoded();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(pubKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey importedPubKey = keyFactory.generatePublic(pubKeySpec);
// Export Private Key
byte[] privKeyBytes = keyPair.getPrivate().getEncoded();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privKeyBytes);
PrivateKey importedPrivKey = keyFactory.generatePrivate(privKeySpec);
}
public void dynamicAlgorithmSelection() throws Exception {
// Load algorithm from configuration
Properties properties = new Properties();
properties.load(new FileInputStream("crypto-config.properties"));
String algorithm = properties.getProperty("key.algorithm", "AES");
KeyGenerator keyGen = KeyGenerator.getInstance(algorithm);
keyGen.init(256);
SecretKey dynamicKey = keyGen.generateKey();
}
public KeyPair generateKeyPair(String algorithm) throws NoSuchAlgorithmException {
// Wrapper for Key Generation
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
keyPairGen.initialize(2048);
return keyPairGen.generateKeyPair();
}
public void keySelectionFromArray() throws NoSuchAlgorithmException {
// Selecting Algorithm Dynamically from an Array
String[] algorithms = { "RSA", "EC", "Ed25519" };
KeyPair[] keyPairs = new KeyPair[algorithms.length];
for (int i = 0; i < algorithms.length; i++) {
keyPairs[i] = generateKeyPair(algorithms[i]);
}
}
}

View File

@@ -0,0 +1,394 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
// import org.bouncycastle.crypto.params.Argon2Parameters;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;
import java.util.Properties;
/**
* This class demonstrates multiple key derivation functions (KDFs) including
* PBKDF2, scrypt, Argon2, raw hash derivation,
* HKDF, and dynamic algorithm selection.
*
* SAST/CBOM Classification Notes:
*
* 1. PBKDF2 Examples:
* - Parent Classification: Password-Based Key Derivation Function (PBKDF).
* - SAST:
* * pbkdf2DerivationBasic: Uses PBKDF2WithHmacSHA256 with 10,000 iterations
* acceptable if parameters meet current standards.
* * pbkdf2LowIteration: Uses only 10 iterations flagged as insecure due to
* insufficient iteration count.
* * pbkdf2HighIteration: Uses 1,000,000 iterations secure (though performance
* may be impacted).
* * pbkdf2HmacSHA1: Uses PBKDF2WithHmacSHA1 flagged as weaker compared to
* SHA-256, though sometimes seen in legacy systems.
* * pbkdf2HmacSHA512: Uses PBKDF2WithHmacSHA512 classified as secure.
*
* 2. Scrypt Examples:
* - Parent Classification: Memory-Hard Key Derivation Function.
* - SAST:
* * scryptWeak: Uses weak parameters (n=1024, r=1, p=1) flagged as insecure.
* * scryptStrong: Uses stronger parameters (n=16384, r=8, p=1) considered
* secure.
*
* 3. Argon2 Examples:
* - Parent Classification: Memory-Hard Key Derivation Function (Argon2id).
* - SAST:
* * argon2Derivation: Uses moderate memory and iterations considered secure.
* * argon2HighMemory: Uses high memory (128MB) and more iterations secure,
* though resource intensive.
*
* 4. Insecure Raw Hash Derivation:
* - Parent Classification: Raw Hash Usage for Key Derivation.
* - SAST: Using a single SHA-256 hash as a key and then using it with insecure
* AES/ECB mode is highly discouraged.
*
* 5. HKDF Examples:
* - Parent Classification: Key Derivation Function (HKDF).
* - SAST: The provided HKDF implementation is simplistic (single-block
* expansion) and may be flagged.
*
* 6. Multi-Step Hybrid Derivation:
* - Parent Classification: Composite KDF (PBKDF2 followed by HKDF).
* - SAST: Combining two KDFs is acceptable if done carefully; however, custom
* implementations should be reviewed.
*
* 7. Dynamic KDF Selection:
* - Parent Classification: Dynamic/Configurable Key Derivation.
* - SAST: Loading KDF parameters from configuration introduces ambiguity and
* risk if misconfigured.
*/
public class KeyDerivation1 {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
//////////////////////////////////////
// 1. PBKDF2 EXAMPLES
//////////////////////////////////////
/**
* Basic PBKDF2 derivation using PBKDF2WithHmacSHA256.
*
* SAST/CBOM:
* - Parent: PBKDF2.
* - Uses 10,000 iterations with a 256-bit key; generally acceptable.
*/
public void pbkdf2DerivationBasic(String password) throws Exception {
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] key = factory.generateSecret(spec).getEncoded();
System.out.println("PBKDF2 (Basic) Key: " + Base64.getEncoder().encodeToString(key));
}
/**
* PBKDF2 derivation with a very low iteration count.
*
* SAST/CBOM:
* - Parent: PBKDF2.
* - Iteration count is only 10, which is far below acceptable security
* standards.
* - Flagged as insecure.
*/
public void pbkdf2LowIteration(String password) throws Exception {
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10, 256); // Very low iteration count.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] key = factory.generateSecret(spec).getEncoded();
System.out.println("PBKDF2 (Low Iteration) Key (Insecure): " + Base64.getEncoder().encodeToString(key));
}
/**
* PBKDF2 derivation with a high iteration count.
*
* SAST/CBOM:
* - Parent: PBKDF2.
* - Uses 1,000,000 iterations; this is secure but may impact performance.
*/
public void pbkdf2HighIteration(String password) throws Exception {
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1_000_000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] key = factory.generateSecret(spec).getEncoded();
System.out.println("PBKDF2 (High Iteration) Key: " + Base64.getEncoder().encodeToString(key));
}
/**
* PBKDF2 derivation using HmacSHA1.
*
* SAST/CBOM:
* - Parent: PBKDF2.
* - Uses HMAC-SHA1, which is considered weaker than SHA-256; may be acceptable
* only for legacy systems.
*/
public void pbkdf2HmacSHA1(String password) throws Exception {
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 80000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] key = factory.generateSecret(spec).getEncoded();
System.out.println("PBKDF2 (HmacSHA1) Key: " + Base64.getEncoder().encodeToString(key));
}
/**
* PBKDF2 derivation using HmacSHA512.
*
* SAST/CBOM:
* - Parent: PBKDF2.
* - Uses HMAC-SHA512 with 160,000 iterations; classified as secure.
*/
public void pbkdf2HmacSHA512(String password) throws Exception {
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 160000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
byte[] key = factory.generateSecret(spec).getEncoded();
System.out.println("PBKDF2 (HmacSHA512) Key: " + Base64.getEncoder().encodeToString(key));
}
//////////////////////////////////////
// 2. SCRYPT EXAMPLES
//////////////////////////////////////
/**
* Scrypt derivation with weak parameters.
*
* SAST/CBOM:
* - Parent: Scrypt.
* - Parameters (n=1024, r=1, p=1) are too weak and should be flagged as
* insecure.
*/
public void scryptWeak(String password) throws Exception {
byte[] salt = generateSalt(16);
// Weak parameters: low work factor.
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1024, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("SCRYPT");
byte[] key = factory.generateSecret(spec).getEncoded();
System.out.println("scrypt (Weak) Key: " + Base64.getEncoder().encodeToString(key));
}
/**
* Scrypt derivation with stronger parameters.
*
* SAST/CBOM:
* - Parent: Scrypt.
* - Parameters (n=16384, r=8, p=1) provide a secure work factor.
*/
public void scryptStrong(String password) throws Exception {
byte[] salt = generateSalt(16);
// Strong parameters for scrypt.
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 16384, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("SCRYPT");
byte[] key = factory.generateSecret(spec).getEncoded();
System.out.println("scrypt (Strong) Key: " + Base64.getEncoder().encodeToString(key));
}
//////////////////////////////////////
// 3. ARGON2 EXAMPLES
//////////////////////////////////////
// /**
// * Argon2 derivation using Argon2id with moderate memory and iterations.
// *
// * SAST/CBOM:
// * - Parent: Argon2 (Memory-Hard KDF).
// * - Parameters: memory=65536 KB, iterations=2, parallelism=2; considered
// * secure.
// */
// public void argon2Derivation(String password) throws Exception {
// byte[] salt = generateSalt(16);
// Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
// .withSalt(salt)
// .withParallelism(2)
// .withMemoryAsKB(65536)
// .withIterations(2);
// Argon2BytesGenerator gen = new Argon2BytesGenerator();
// gen.init(builder.build());
// byte[] hash = new byte[32];
// gen.generateBytes(password.getBytes(), hash, 0, hash.length);
// System.out.println("Argon2 Key: " + Base64.getEncoder().encodeToString(hash));
// }
// /**
// * Argon2 derivation with high memory and more iterations.
// *
// * SAST/CBOM:
// * - Parent: Argon2.
// * - Uses high memory (131072 KB = 128MB) and 5 iterations; secure but resource
// * intensive.
// */
// public void argon2HighMemory(String password) throws Exception {
// byte[] salt = generateSalt(16);
// Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
// .withSalt(salt)
// .withParallelism(4)
// .withMemoryAsKB(131072) // 128MB of memory.
// .withIterations(5);
// Argon2BytesGenerator gen = new Argon2BytesGenerator();
// gen.init(builder.build());
// byte[] hash = new byte[64];
// gen.generateBytes(password.getBytes(), hash, 0, hash.length);
// System.out.println("Argon2 (High Memory) Key: " + Base64.getEncoder().encodeToString(hash));
// }
//////////////////////////////////////
// 4. INSECURE RAW HASH EXAMPLES
//////////////////////////////////////
/**
* Derives a key by directly hashing input with SHA-256 and uses it for
* encryption.
*
* SAST/CBOM:
* - Parent: Raw Hash-Based Key Derivation.
* - This approach is insecure since it uses a raw hash as a key and then uses
* AES in ECB mode,
* which is vulnerable to pattern analysis.
*/
public void insecureRawSHA256Derivation(String input) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] derivedKey = digest.digest(input.getBytes());
System.out.println("Insecure Raw SHA-256 Key: " + Base64.getEncoder().encodeToString(derivedKey));
// Insecure usage: AES/ECB mode is used with a key derived from a raw hash.
SecretKey key = new SecretKeySpec(derivedKey, 0, 16, "AES");
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key);
byte[] ciphertext = cipher.doFinal("SampleData16Bytes".getBytes());
System.out.println("Insecurely Encrypted Data with Raw-SHA256 Key: "
+ Base64.getEncoder().encodeToString(ciphertext));
}
//////////////////////////////////////
// 5. HKDF EXAMPLES
//////////////////////////////////////
/**
* Derives a key using a simple HKDF expansion based on HMAC-SHA256.
*
* SAST/CBOM:
* - Parent: HKDF.
* - The implementation uses a single-block (simplistic) expansion and may be
* flagged.
* A full, standard HKDF implementation is recommended.
*/
public void hkdfDerivation(byte[] ikm) throws Exception {
byte[] salt = generateSalt(32);
byte[] derivedKey = hkdfExpand(ikm, salt, 32);
System.out.println("HKDF Derived Key: " + Base64.getEncoder().encodeToString(derivedKey));
}
/**
* Multi-step hybrid derivation: first using PBKDF2, then applying HKDF
* expansion.
*
* SAST/CBOM:
* - Parent: Composite KDF.
* - Combining PBKDF2 and HKDF is a non-standard approach and may be flagged;
* ensure that each step meets security requirements.
*/
public void multiStepHybridDerivation(String password, byte[] sharedSecret) throws Exception {
byte[] pbkdf2Key = derivePBKDF2Key(password);
byte[] finalKey = hkdfExpand(sharedSecret, pbkdf2Key, 32);
System.out.println("Multi-Step Hybrid Key: " + Base64.getEncoder().encodeToString(finalKey));
}
//////////////////////////////////////
// 6. DYNAMIC ALGORITHM SELECTION (AMBIGUOUS CASE)
//////////////////////////////////////
/**
* Dynamically selects a KDF algorithm based on external configuration.
*
* SAST/CBOM:
* - Parent: Dynamic/Configurable Key Derivation.
* - Loading the algorithm and parameters from a config file introduces risk if
* the configuration is compromised
* or misconfigured.
*/
public void dynamicKDFSelection(String password, String configPath) throws Exception {
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream(configPath)) {
props.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
String kdfAlg = props.getProperty("kdf.alg", "PBKDF2WithHmacSHA256");
int iterations = Integer.parseInt(props.getProperty("kdf.iterations", "10000"));
int keySize = Integer.parseInt(props.getProperty("kdf.keySize", "256"));
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keySize);
SecretKeyFactory factory = SecretKeyFactory.getInstance(kdfAlg);
byte[] derived = factory.generateSecret(spec).getEncoded();
System.out.println("Dynamically Selected KDF (" + kdfAlg + ") Key: "
+ Base64.getEncoder().encodeToString(derived));
}
//////////////////////////////////////
// HELPER METHODS
//////////////////////////////////////
/**
* Helper method to derive a PBKDF2 key with PBKDF2WithHmacSHA256.
*
* SAST/CBOM:
* - Parent: PBKDF2 helper.
*/
private byte[] derivePBKDF2Key(String password) throws Exception {
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
return factory.generateSecret(spec).getEncoded();
}
/**
* A simplistic HKDF expansion function using HMAC-SHA256.
*
* SAST/CBOM:
* - Parent: HKDF.
* - Uses a single-block expansion ("hkdf-expansion") which is non-standard and
* may be flagged.
*/
private byte[] hkdfExpand(byte[] ikm, byte[] salt, int length) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKey secretKey = new SecretKeySpec(salt, "HmacSHA256");
hmac.init(secretKey);
byte[] prk = hmac.doFinal(ikm); // Extraction step.
// Single-block expansion (non-standard; for full HKDF, multiple iterations may
// be necessary)
hmac.init(new SecretKeySpec(prk, "HmacSHA256"));
byte[] okm = hmac.doFinal("hkdf-expansion".getBytes());
return Arrays.copyOf(okm, length);
}
/**
* Generates a secure random salt of the specified length.
*
* SAST/CBOM:
* - Parent: Secure Random Salt Generation.
* - Uses SecureRandom; considered best practice.
*/
private byte[] generateSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}

View File

@@ -0,0 +1,235 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
// import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
// import org.bouncycastle.pqc.jcajce.spec.KyberParameterSpec;
// import org.bouncycastle.util.Strings;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
/**
* Demonstrates various Key Encapsulation Mechanisms (KEMs), including:
*
* 1) RSA-KEM (emulated using RSA-OAEP for ephemeral key wrapping)
* - CBOM/SAST: Classified as a Hybrid Cryptosystem (public-key based key
* encapsulation).
* While RSA-OAEP is secure, using it to emulate KEM (without a standard scheme)
* may be flagged.
*
* 2) ECIES (Elliptic Curve Integrated Encryption Scheme)
* - CBOM/SAST: Classified as a Hybrid Cryptosystem (KEM+DEM) based on ECDH and
* AES.
* Note: Directly using the raw ECDH shared secret as key material is insecure
* in production.
*
* 3) Kyber (Post-Quantum KEM using BouncyCastle PQC)
* - CBOM/SAST: Classified as a Post-Quantum Key Encapsulation mechanism.
* This is modern and secure when using standardized parameters.
*
* 4) Basic ephemeral flows that mimic KEM logic using ephemeral ECDH.
* - CBOM/SAST: Classified as a simple KEM mimic based on ephemeral ECDH.
*/
public class KeyEncapsulation {
// static {
// // Adding both classical and PQC providers.
// Security.addProvider(new BouncyCastleProvider());
// Security.addProvider(new BouncyCastlePQCProvider());
// }
//////////////////////////////////////
// 1. RSA-KEM-Like Flow
//////////////////////////////////////
/**
* Emulates RSA-KEM by using RSA-OAEP to wrap a random AES key.
*
* SAST/CBOM Classification:
* - Parent: Hybrid Cryptosystem (RSA-OAEP based key encapsulation).
* - Note: Although RSA-OAEP is secure, using it to "wrap" an ephemeral key is a
* non-standard KEM pattern.
*
* @param rsaPub The RSA public key of the recipient.
*/
public void rsaKEMEncapsulation(PublicKey rsaPub) throws Exception {
// 1) Generate an ephemeral AES key (symmetric key for data encryption)
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // 256-bit AES key.
SecretKey aesKey = keyGen.generateKey();
System.out.println("Ephemeral AES Key: " + Base64.getEncoder().encodeToString(aesKey.getEncoded()));
// 2) Encrypt (wrap) the ephemeral AES key with RSA-OAEP.
// SAST Note: This RSA-OAEP wrapping is used to encapsulate the AES key.
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, rsaPub);
byte[] wrappedKey = rsaCipher.doFinal(aesKey.getEncoded());
System.out.println("RSA-KEM Encapsulated AES Key: " + Base64.getEncoder().encodeToString(wrappedKey));
// 3) Example usage: Encrypt data with the ephemeral AES key using AES-GCM.
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // Standard IV length for GCM.
new SecureRandom().nextBytes(iv);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
byte[] ciphertext = aesCipher.doFinal("KEM-based encryption".getBytes());
System.out.println("AES-GCM ciphertext: " + Base64.getEncoder().encodeToString(ciphertext));
}
/**
* Performs RSA decapsulation by decrypting the wrapped AES key.
*
* SAST/CBOM Classification:
* - Parent: Hybrid Cryptosystem (RSA-OAEP based key decapsulation).
* - Note: Secure when used with matching RSA key pairs.
*
* @param rsaPriv The RSA private key corresponding to the public key used.
* @param wrappedKey The RSA-wrapped ephemeral AES key.
*/
public void rsaKEMDecapsulation(PrivateKey rsaPriv, byte[] wrappedKey) throws Exception {
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsaCipher.init(Cipher.DECRYPT_MODE, rsaPriv);
byte[] aesKeyBytes = rsaCipher.doFinal(wrappedKey);
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES");
System.out.println("RSA-KEM Decapsulated AES Key: " + Base64.getEncoder().encodeToString(aesKey.getEncoded()));
}
//////////////////////////////////////
// 2. ECIES Example
//////////////////////////////////////
/**
* Implements a simplified ECIES flow using ephemeral ECDH and AES-GCM.
*
* SAST/CBOM Classification:
* - Parent: Hybrid Cryptosystem (ECIES: ECDH-based key encapsulation + DEM).
* - Note: Directly using the raw ECDH shared secret as key material is
* insecure.
* In practice, a proper KDF must be applied.
*
* @param ecPub The recipient's EC public key.
*/
public void eciesEncapsulation(PublicKey ecPub) throws Exception {
// Generate an ephemeral EC key pair.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
KeyPair ephemeralEC = kpg.generateKeyPair();
// Perform ECDH key agreement to derive the shared secret.
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(ephemeralEC.getPrivate());
ka.doPhase(ecPub, true);
byte[] sharedSecret = ka.generateSecret();
System.out.println("ECIES ephemeral ECDH Secret: " + Base64.getEncoder().encodeToString(sharedSecret));
// For demonstration only: directly use part of the shared secret as an AES key.
// SAST Note: This is insecure; a proper key derivation function (KDF) must be
// used.
SecretKey aesKey = new SecretKeySpec(sharedSecret, 0, 16, "AES");
// Encrypt the message using AES-GCM.
Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, new GCMParameterSpec(128, iv));
byte[] ciphertext = aesCipher.doFinal("ECIES message".getBytes());
// The ephemeral public key (ephemeralEC.getPublic()) is transmitted as part of
// the output.
System.out.println(
"ECIES ephemeral public: " + Base64.getEncoder().encodeToString(ephemeralEC.getPublic().getEncoded()));
System.out.println("ECIES ciphertext: " + Base64.getEncoder().encodeToString(ciphertext));
}
//////////////////////////////////////
// 3. Kyber Example (Post-Quantum KEM)
//////////////////////////////////////
// /**
// * Demonstrates a Kyber-based encapsulation using BouncyCastle's PQC provider.
// *
// * SAST/CBOM Classification:
// * - Parent: Post-Quantum KEM.
// * - Note: Kyber is a modern, post-quantum secure KEM. This example uses
// * Kyber-512.
// *
// * @param kyberRecipientKP The recipient's Kyber key pair.
// */
// public void kyberEncapsulate(KeyPair kyberRecipientKP) throws Exception {
// // Use an ephemeral label for demonstration.
// byte[] ephemeralLabel = Strings.toByteArray("Kyber-KEM-Label");
// Cipher kemCipher = Cipher.getInstance("Kyber", "BCPQC");
// kemCipher.init(Cipher.ENCRYPT_MODE, kyberRecipientKP.getPublic(), new SecureRandom());
// byte[] ciphertext = kemCipher.doFinal(ephemeralLabel);
// System.out.println("Kyber ciphertext: " + Base64.getEncoder().encodeToString(ciphertext));
// }
//////////////////////////////////////
// 4. Basic Ephemeral Flows That Mimic KEM
//////////////////////////////////////
/**
* Uses ephemeral ECDH to derive a shared secret that mimics a KEM.
*
* SAST/CBOM Classification:
* - Parent: Ephemeral Key Agreement (mimicking KEM).
* - Note: This simple approach demonstrates the concept of using ephemeral keys
* to derive a secret.
* In a full scheme, the ephemeral public key would also be transmitted.
*
* @param recipientPubKey The recipient's public key.
*/
public void ephemeralECDHMimicKEM(PublicKey recipientPubKey) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair ephemeralKP = kpg.generateKeyPair();
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(ephemeralKP.getPrivate());
ka.doPhase(recipientPubKey, true);
byte[] sharedSecret = ka.generateSecret();
System.out.println(
"Ephemeral ECDH shared secret (mimics KEM): " + Base64.getEncoder().encodeToString(sharedSecret));
// In a full implementation, the ephemeral public key and the shared secret are
// used together.
}
//////////////////////////////////////
// Test / Demo Method
//////////////////////////////////////
/**
* Demonstrates each of the key encapsulation flows.
*/
public void runKeyEncapsulationDemos() throws Exception {
// 1) RSA-KEM-like Flow:
KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA");
rsaKpg.initialize(2048);
KeyPair rsaKP = rsaKpg.generateKeyPair();
rsaKEMEncapsulation(rsaKP.getPublic());
// 2) ECIES Example:
KeyPairGenerator ecKpg = KeyPairGenerator.getInstance("EC");
ecKpg.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair ecKP = ecKpg.generateKeyPair();
eciesEncapsulation(ecKP.getPublic());
// // 3) Kyber Example (Post-Quantum KEM):
// KeyPairGenerator kyberKpg = KeyPairGenerator.getInstance("Kyber", "BCPQC");
// kyberKpg.initialize(KyberParameterSpec.kyber512);
// KeyPair kyberKP = kyberKpg.generateKeyPair();
// kyberEncapsulate(kyberKP);
// 4) Ephemeral ECDH Mimic KEM:
// For demonstration, we use an EC key pair and mimic KEM by deriving a shared
// secret.
KeyPair ephemeralEC = ecKpg.generateKeyPair();
ephemeralECDHMimicKEM(ephemeralEC.getPublic());
}
}

View File

@@ -0,0 +1,341 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.KeyAgreement;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Base64;
/**
* Demonstrates various Key Exchange mechanisms using standard Java and
* BouncyCastle:
*
* 1) Classic DH (Diffie-Hellman) with multiple key sizes:
* - 512-bit: Insecure/deprecated (flagged as unsafe by SAST).
* - 2048-bit: Standard secure level.
* - 4096-bit: High-security (but can be slow).
*
* 2) ECDH (using secp256r1):
* - Classified as a secure elliptic-curve key exchange.
*
* 3) X25519:
* - A modern and efficient elliptic-curve key exchange protocol.
*
* 4) X448:
* - Provides a higher security level for key exchange.
*
* In addition, the class now includes a nuanced insecure example that
* demonstrates:
* - Reusing static key pairs instead of generating fresh ephemeral keys.
* - Using weak parameters (512-bit DH) in a key exchange.
*
* The runAllExchanges() method demonstrates generating keys for each algorithm,
* deriving shared secrets, and comparing safe vs. insecure practices.
*/
public class KeyExchange {
// static {
// // Add the BouncyCastle provider to support additional algorithms.
// Security.addProvider(new BouncyCastleProvider());
// }
//////////////////////////////////////////
// 1. Classic DH (Diffie-Hellman)
//////////////////////////////////////////
/**
* Generates a standard Diffie-Hellman key pair using a 2048-bit modulus.
*
* CBOM/SAST Classification:
* - Parent: Classic Diffie-Hellman Key Exchange.
* - 2048-bit is considered secure and is widely accepted.
*
* @return A 2048-bit DH KeyPair.
*/
public KeyPair generateDHKeyPair() throws Exception {
KeyPairGenerator dhKpg = KeyPairGenerator.getInstance("DH");
dhKpg.initialize(2048);
return dhKpg.generateKeyPair();
}
/**
* Generates a deprecated/unsafe Diffie-Hellman key pair using a 512-bit
* modulus.
*
* CBOM/SAST Classification:
* - Parent: Classic Diffie-Hellman Key Exchange.
* - 512-bit DH is considered insecure and should be flagged by SAST tools.
*
* @return A 512-bit (insecure) DH KeyPair.
*/
public KeyPair generateDHDeprecated() throws Exception {
KeyPairGenerator dhKpg = KeyPairGenerator.getInstance("DH");
// 512 bits is considered insecure/deprecated.
dhKpg.initialize(512);
return dhKpg.generateKeyPair();
}
/**
* Generates a high-security Diffie-Hellman key pair using a 4096-bit modulus.
*
* CBOM/SAST Classification:
* - Parent: Classic Diffie-Hellman Key Exchange.
* - 4096-bit DH offers high security, though it may be slower in practice.
*
* @return A 4096-bit DH KeyPair.
*/
public KeyPair generateDHHighSecurity() throws Exception {
KeyPairGenerator dhKpg = KeyPairGenerator.getInstance("DH");
dhKpg.initialize(4096);
return dhKpg.generateKeyPair();
}
/**
* Derives a shared secret from a DH key pair.
*
* CBOM/SAST Classification:
* - Parent: Classic Diffie-Hellman Key Exchange.
* - Properly deriving the shared secret is secure if using a safe key size.
*
* @param privateKey The private key of one party.
* @param publicKey The public key of the other party.
* @return The derived shared secret as a byte array.
*/
public byte[] deriveDHSecret(PrivateKey privateKey, PublicKey publicKey) throws Exception {
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(privateKey);
ka.doPhase(publicKey, true);
return ka.generateSecret();
}
//////////////////////////////////////////
// 2. ECDH (secp256r1)
//////////////////////////////////////////
/**
* Generates an Elliptic Curve Diffie-Hellman key pair using the secp256r1
* curve.
*
* CBOM/SAST Classification:
* - Parent: Elliptic Curve Diffie-Hellman (ECDH).
* - secp256r1 is widely regarded as secure and efficient.
*
* @return An ECDH KeyPair on secp256r1.
*/
public KeyPair generateECDHKeyPair() throws Exception {
KeyPairGenerator ecKpg = KeyPairGenerator.getInstance("EC", "BC");
ecKpg.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
return ecKpg.generateKeyPair();
}
/**
* Derives a shared secret using ECDH.
*
* CBOM/SAST Classification:
* - Parent: Elliptic Curve Diffie-Hellman (ECDH).
* - Secure when using appropriate curves and proper randomness.
*
* @param privateKey The ECDH private key.
* @param publicKey The corresponding public key.
* @return The derived ECDH shared secret.
*/
public byte[] deriveECDHSecret(PrivateKey privateKey, PublicKey publicKey) throws Exception {
KeyAgreement ka = KeyAgreement.getInstance("ECDH", "BC");
ka.init(privateKey);
ka.doPhase(publicKey, true);
return ka.generateSecret();
}
//////////////////////////////////////////
// 3. X25519
//////////////////////////////////////////
/**
* Generates an ephemeral X25519 key pair.
*
* CBOM/SAST Classification:
* - Parent: Modern Elliptic-Curve Key Exchange.
* - X25519 is considered secure and efficient.
*
* @return An X25519 KeyPair.
*/
public KeyPair generateX25519KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X25519", "BC");
// X25519 key size is fixed; the parameter (255) is a reference value.
kpg.initialize(255, new SecureRandom());
return kpg.generateKeyPair();
}
/**
* Derives a shared secret using the X25519 key agreement.
*
* CBOM/SAST Classification:
* - Parent: Modern Elliptic-Curve Key Exchange.
* - X25519 is highly recommended for its security and efficiency.
*
* @param privateKey The X25519 private key.
* @param publicKey The corresponding public key.
* @return The derived X25519 shared secret.
*/
public byte[] deriveX25519Secret(PrivateKey privateKey, PublicKey publicKey) throws Exception {
KeyAgreement ka = KeyAgreement.getInstance("X25519", "BC");
ka.init(privateKey);
ka.doPhase(publicKey, true);
return ka.generateSecret();
}
//////////////////////////////////////////
// 4. X448
//////////////////////////////////////////
/**
* Generates an ephemeral X448 key pair.
*
* CBOM/SAST Classification:
* - Parent: Modern Elliptic-Curve Key Exchange.
* - X448 provides a higher security margin than X25519.
*
* @return An X448 KeyPair.
*/
public KeyPair generateX448KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("X448", "BC");
// X448 key size is fixed; the parameter (448) is the curve parameter.
kpg.initialize(448, new SecureRandom());
return kpg.generateKeyPair();
}
/**
* Derives a shared secret using the X448 key agreement.
*
* CBOM/SAST Classification:
* - Parent: Modern Elliptic-Curve Key Exchange.
* - X448 is considered secure and suitable for high-security applications.
*
* @param privateKey The X448 private key.
* @param publicKey The corresponding public key.
* @return The derived X448 shared secret.
*/
public byte[] deriveX448Secret(PrivateKey privateKey, PublicKey publicKey) throws Exception {
KeyAgreement ka = KeyAgreement.getInstance("X448", "BC");
ka.init(privateKey);
ka.doPhase(publicKey, true);
return ka.generateSecret();
}
//////////////////////////////////////////
// 5. Nuanced Insecure Key Exchange Example
//////////////////////////////////////////
/**
* Demonstrates a nuanced example of insecure key exchange by:
* - Using deprecated DH parameters (512-bit).
* - Reusing static (non-ephemeral) keys.
*
* SAST/CBOM Classification:
* - Parent: Insecure Key Exchange Patterns.
* - Issues:
* * 512-bit DH is weak and vulnerable to attacks.
* * Reusing a static key pair across sessions eliminates forward secrecy.
* * Reusing an ECDH key pair for both sides results in predictable shared
* secrets.
*/
public void insecureKeyExchangeExample() throws Exception {
System.out.println("\n--- Insecure Key Exchange Example ---");
// Example 1: Using weak 512-bit DH with static key reuse.
KeyPair staticDHKeyPair = generateDHDeprecated();
// Reusing the same static DH key pair for both parties.
byte[] staticDHSecret = deriveDHSecret(staticDHKeyPair.getPrivate(), staticDHKeyPair.getPublic());
System.out.println("Static DH (512-bit) shared secret (reused): " +
Base64.getEncoder().encodeToString(staticDHSecret));
// SAST Note: 512-bit DH is considered insecure and static key reuse prevents
// forward secrecy.
// Example 2: Reusing an ECDH key pair instead of generating fresh ephemeral
// keys.
KeyPair reusedECDHKeyPair = generateECDHKeyPair();
// Using the same key pair for both sides leads to a shared secret that is
// easily derived.
byte[] reusedECDHSecret = deriveECDHSecret(reusedECDHKeyPair.getPrivate(), reusedECDHKeyPair.getPublic());
System.out.println("Reused ECDH shared secret: " +
Base64.getEncoder().encodeToString(reusedECDHSecret));
// SAST Note: Proper key exchange requires fresh ephemeral keys for each session
// to ensure forward secrecy.
}
//////////////////////////////////////////
// 6. runAllExchanges() Demo Method
//////////////////////////////////////////
/**
* Demonstrates key exchange flows for various algorithms, including both secure
* and insecure examples.
*
* CBOM/SAST Classification:
* - Exercises both safe configurations (e.g., DH with 2048/4096-bit, ECDH,
* X25519, X448)
* and insecure configurations (e.g., DH with 512-bit, static key reuse).
*/
public void runAllExchanges() throws Exception {
System.out.println("--- Running Secure Key Exchanges ---");
// ============ DEPRECATED / UNSAFE DH (512 bits) ============
KeyPair dhDep1 = generateDHDeprecated();
KeyPair dhDep2 = generateDHDeprecated();
byte[] dhDepSecret1 = deriveDHSecret(dhDep1.getPrivate(), dhDep2.getPublic());
byte[] dhDepSecret2 = deriveDHSecret(dhDep2.getPrivate(), dhDep1.getPublic());
System.out.println("DH(512) K1->K2: " + Base64.getEncoder().encodeToString(dhDepSecret1));
System.out.println("DH(512) K2->K1: " + Base64.getEncoder().encodeToString(dhDepSecret2));
System.out.println("DH(512) match? " + Arrays.equals(dhDepSecret1, dhDepSecret2));
// ============ DH (2048 bits) Standard ============
KeyPair dhKP1 = generateDHKeyPair();
KeyPair dhKP2 = generateDHKeyPair();
byte[] dhSecret1 = deriveDHSecret(dhKP1.getPrivate(), dhKP2.getPublic());
byte[] dhSecret2 = deriveDHSecret(dhKP2.getPrivate(), dhKP1.getPublic());
System.out.println("DH(2048) K1->K2: " + Base64.getEncoder().encodeToString(dhSecret1));
System.out.println("DH(2048) K2->K1: " + Base64.getEncoder().encodeToString(dhSecret2));
System.out.println("DH(2048) match? " + Arrays.equals(dhSecret1, dhSecret2));
// ============ DH (4096 bits) High-Security ============
KeyPair dhHigh1 = generateDHHighSecurity();
KeyPair dhHigh2 = generateDHHighSecurity();
byte[] dhHighSecret1 = deriveDHSecret(dhHigh1.getPrivate(), dhHigh2.getPublic());
byte[] dhHighSecret2 = deriveDHSecret(dhHigh2.getPrivate(), dhHigh1.getPublic());
System.out.println("DH(4096) K1->K2: " + Base64.getEncoder().encodeToString(dhHighSecret1));
System.out.println("DH(4096) K2->K1: " + Base64.getEncoder().encodeToString(dhHighSecret2));
System.out.println("DH(4096) match? " + Arrays.equals(dhHighSecret1, dhHighSecret2));
// ============ ECDH (secp256r1) ============
KeyPair ecKP1 = generateECDHKeyPair();
KeyPair ecKP2 = generateECDHKeyPair();
byte[] ecdhSecret1 = deriveECDHSecret(ecKP1.getPrivate(), ecKP2.getPublic());
byte[] ecdhSecret2 = deriveECDHSecret(ecKP2.getPrivate(), ecKP1.getPublic());
System.out.println("ECDH K1->K2: " + Base64.getEncoder().encodeToString(ecdhSecret1));
System.out.println("ECDH K2->K1: " + Base64.getEncoder().encodeToString(ecdhSecret2));
System.out.println("ECDH match? " + Arrays.equals(ecdhSecret1, ecdhSecret2));
// ============ X25519 ============
KeyPair x25519KP1 = generateX25519KeyPair();
KeyPair x25519KP2 = generateX25519KeyPair();
byte[] x25519Secret1 = deriveX25519Secret(x25519KP1.getPrivate(), x25519KP2.getPublic());
byte[] x25519Secret2 = deriveX25519Secret(x25519KP2.getPrivate(), x25519KP1.getPublic());
System.out.println("X25519 K1->K2: " + Base64.getEncoder().encodeToString(x25519Secret1));
System.out.println("X25519 K2->K1: " + Base64.getEncoder().encodeToString(x25519Secret2));
System.out.println("X25519 match? " + Arrays.equals(x25519Secret1, x25519Secret2));
// ============ X448 ============
KeyPair x448KP1 = generateX448KeyPair();
KeyPair x448KP2 = generateX448KeyPair();
byte[] x448Secret1 = deriveX448Secret(x448KP1.getPrivate(), x448KP2.getPublic());
byte[] x448Secret2 = deriveX448Secret(x448KP2.getPrivate(), x448KP1.getPublic());
System.out.println("X448 K1->K2: " + Base64.getEncoder().encodeToString(x448Secret1));
System.out.println("X448 K2->K1: " + Base64.getEncoder().encodeToString(x448Secret2));
System.out.println("X448 match? " + Arrays.equals(x448Secret1, x448Secret2));
// ============ Insecure Key Exchange Example ============
insecureKeyExchangeExample();
}
}

View File

@@ -0,0 +1,259 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
/**
* MACOperation demonstrates various Message Authentication Code (MAC)
* operations and further use of MAC outputs as inputs into higherlevel
* cryptosystems.
*
* Flows include:
*
* 1. Secure HMACSHA2 (HMACSHA256) a widely accepted MAC.
* 2. Secure HMACSHA3 (HMACSHA3-256) an alternative using the SHA3 family.
* 3. Secure Poly1305 MAC using BouncyCastles implementation.
* 4. Secure GMAC using AESGCMs authentication tag in a dedicated MAC mode.
* 5. Secure KMAC using KMAC128 (from the SHA3 family).
*
* Insecure examples include:
*
* 6. Insecure HMACSHA1 which is deprecated.
*
* Further flows:
*
* A. processMACOutput: Uses the MAC output directly as key material for AES
* encryption.
* (Note: This is acceptable only if the MAC is produced by a secure function.)
*
* B. alternativeMACFlow: Uses the MAC output as an identifier that is then
* encrypted.
*
* C. furtherUseMACForKeyDerivation: Uses PBKDF2 to split a MAC output into two
* keys,
* one for encryption and one for MACing ciphertext.
*
* SAST/CBOM Notes:
* - Secure MAC algorithms (HMACSHA256, HMACSHA3-256, Poly1305, GMAC, KMAC128)
* are acceptable if used correctly.
* - HMACSHA1 is flagged as insecure.
* - Using a raw MAC output directly as key material is ambiguous unless the MAC
* is produced by a secure KDF.
*/
public class MACOperation {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
// ---------- MAC Operations ----------
/**
* Secure MAC using HMAC-SHA256.
* SAST: HMAC-SHA256 is widely considered 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());
}
/**
* Secure MAC using HMAC-SHA3-256.
* SAST: HMAC-SHA3 is a modern alternative from the SHA-3 family.
*/
public byte[] secureHMACSHA3(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA3-256", "BC");
SecretKey secretKey = new SecretKeySpec(key, "HmacSHA3-256");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using Poly1305.
* SAST: Poly1305 is secure when used with a one-time key from a cipher (e.g.
* ChaCha20).
*/
public byte[] securePoly1305(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("Poly1305", "BC");
SecretKey secretKey = new SecretKeySpec(key, "Poly1305");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using GMAC.
* SAST: GMAC (the MAC part of AES-GCM) is secure when used correctly.
*/
public byte[] secureGMAC(String message, byte[] key) throws Exception {
// For GMAC, we use the GMac algorithm as provided by BC.
Mac mac = Mac.getInstance("GMac", "BC");
// Initialize the key for GMAC; key should be appropriate for the underlying
// block cipher.
SecretKey secretKey = new SecretKeySpec(key, "AES");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Secure MAC using KMAC128.
* SAST: KMAC128 is part of the SHA-3 family and is secure when used properly.
*/
public byte[] secureKMAC(String message, byte[] key) throws Exception {
Mac mac = Mac.getInstance("KMAC128", "BC");
SecretKey secretKey = new SecretKeySpec(key, "KMAC128");
mac.init(secretKey);
return mac.doFinal(message.getBytes());
}
/**
* Insecure MAC using HMAC-SHA1.
* SAST: HMAC-SHA1 is considered deprecated and weak.
*/
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());
}
// ---------- Further Use of MAC Outputs ----------
/**
* Processes the MAC output by using it as key material for AES encryption.
* SAST: Using a raw MAC output as key material is acceptable only if the MAC
* was
* produced by a secure function; otherwise, this is ambiguous.
*
* @param macOutput The computed MAC output.
* @throws Exception if encryption fails.
*/
public void processMACOutput(byte[] macOutput) throws Exception {
// Derive a 128-bit AES key from the MAC output.
SecretKey key = new SecretKeySpec(macOutput, 0, 16, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal("Sensitive Data".getBytes());
storeEncryptedMAC(encryptedData);
}
/**
* Alternative flow: Uses the MAC output as an identifier and then encrypts it.
* SAST: Using a MAC as an identifier is common; subsequent encryption must be
* secure.
*
* @param macOutput The computed MAC output.
* @throws Exception if encryption fails.
*/
public void alternativeMACFlow(byte[] macOutput) throws Exception {
byte[] identifier = Base64.getEncoder().encode(macOutput);
encryptAndSend(identifier);
}
/**
* Further use: Derives two separate keys from the MAC output using PBKDF2,
* then uses one key for encryption and one for computing an additional MAC over
* the ciphertext.
*
* SAST: This key-splitting technique is acceptable if PBKDF2 is used securely.
*
* @param macOutput The MAC output to derive keys from.
* @throws Exception if key derivation or encryption fails.
*/
public void furtherUseMACForKeyDerivation(byte[] macOutput) throws Exception {
// Use the Base64 representation of the MAC as the password input to PBKDF2.
String macAsPassword = Base64.getEncoder().encodeToString(macOutput);
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(macAsPassword.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] keyMaterial = factory.generateSecret(spec).getEncoded();
// Split into two 128-bit keys.
byte[] encryptionKeyBytes = Arrays.copyOfRange(keyMaterial, 0, 16);
byte[] macKeyBytes = Arrays.copyOfRange(keyMaterial, 16, 32);
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
SecretKey derivedMacKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
// Encrypt some sample data using the derived encryption key.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new SecureRandom());
byte[] ciphertext = cipher.doFinal("Further Use Test Data".getBytes());
// Compute HMAC over the ciphertext using the derived MAC key.
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(derivedMacKey);
byte[] computedMac = mac.doFinal(ciphertext);
// Concatenate the ciphertext and MAC for further use.
byte[] output = new byte[ciphertext.length + computedMac.length];
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
storeEncryptedMAC(output);
}
// ---------- Output/Storage Methods ----------
/**
* Simulates secure storage or transmission of an encrypted MAC output.
* SAST: In production, storage and transmission must be protected.
*
* @param encryptedMAC The encrypted MAC output.
*/
public void storeEncryptedMAC(byte[] encryptedMAC) {
String stored = Base64.getEncoder().encodeToString(encryptedMAC);
// In production, this string would be securely stored or transmitted.
}
/**
* Encrypts data using AES-GCM and simulates secure transmission.
* SAST: Uses a securely generated AES key.
*
* @param data The data to encrypt.
* @throws Exception if encryption fails.
*/
public void encryptAndSend(byte[] data) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKey key = generateAESKey();
cipher.init(Cipher.ENCRYPT_MODE, key, new SecureRandom());
byte[] encryptedData = cipher.doFinal(data);
storeEncryptedMAC(encryptedData);
}
// ---------- Helper Methods ----------
/**
* Generates a secure 256-bit AES key.
* SAST: Uses a strong RNG for key generation.
*
* @return A SecretKey for AES.
* @throws NoSuchAlgorithmException if AES is unsupported.
*/
private SecretKey generateAESKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
return keyGen.generateKey();
}
/**
* Generates a random salt of the specified length using SecureRandom.
* SAST: Salting is essential for secure key derivation.
*
* @param length The salt length.
* @return A byte array representing the salt.
*/
private byte[] generateSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}

View File

@@ -0,0 +1,115 @@
package com.example.crypto.artifacts;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;
import java.util.Random;
public class Nonce {
// static {
// Security.addProvider(new BouncyCastleProvider()); // Ensure BouncyCastle is available
// }
/**
* Simple Case: Generates a secure nonce and uses it in HMAC.
*/
public void simpleNonceUsage() throws Exception {
byte[] nonce = secureNonce(16);
SecretKey key = generateHmacKey();
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
mac.update(nonce);
byte[] macResult = mac.doFinal("Simple Test Data".getBytes());
}
/**
* Incorrect: Hardcoded, reused nonce in encryption, leading to predictable
* output.
*/
public void hardcodedNonceReuse() throws Exception {
byte[] nonce = "BADNONCEBADNONCE".getBytes(); // HARDCODED NONCE REUSED!
SecretKey key = generateHmacKey();
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
mac.update(nonce);
byte[] macResult = mac.doFinal("Sensitive Data".getBytes());
}
/**
* Incorrect: Reusing a nonce with AES-GCM, which can lead to catastrophic
* security failures.
*/
public void insecureNonceReuseGCM(SecretKey key, byte[] plaintext) throws Exception {
byte[] nonce = getReusedNonce(12); // SAME NONCE REUSED!
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
}
/**
* Secure Case: Proper unique nonce usage in AES-GCM.
*/
public void secureNonceUsageGCM(SecretKey key, byte[] plaintext) throws Exception {
byte[] nonce = secureNonce(12);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
}
public void complexNonceFlow() {
byte[] nonce = useSecureMethod() ? secureNonce(16) : insecureNonce(16);
processNonce(nonce);
try {
useNonceInMac(nonce, generateHmacKey(), "HmacSHA256");
} catch (Exception e) {
e.printStackTrace();
}
}
public void useNonceInMac(byte[] nonce, SecretKey key, String macAlgorithm) throws Exception {
Mac mac = Mac.getInstance(macAlgorithm);
mac.init(key);
mac.update(nonce);
byte[] macResult = mac.doFinal("Sensitive Data".getBytes());
}
private boolean useSecureMethod() {
return System.currentTimeMillis() % 2 == 0;
}
private void processNonce(byte[] nonce) {
String nonceBase64 = Base64.getEncoder().encodeToString(nonce);
}
private SecretKey generateHmacKey() throws NoSuchAlgorithmException {
KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA256");
return keyGen.generateKey();
}
private byte[] secureNonce(int length) {
byte[] nonce = new byte[length];
new SecureRandom().nextBytes(nonce);
return nonce;
}
private byte[] insecureNonce(int length) {
byte[] nonce = new byte[length];
new Random().nextBytes(nonce);
return nonce;
}
/**
* Incorrect: A nonce that is stored and reused across multiple encryptions.
*/
private byte[] getReusedNonce(int length) {
return "BADNONCEBADNONCE".getBytes(); // Fixed nonce reuse across calls
}
}

View File

@@ -0,0 +1,166 @@
package com.example.crypto.algorithms;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
/**
* PrngTest demonstrates various approaches for generating random data using
* PRNG/RNG APIs.
*
* It covers:
* 1) Secure random generation using SecureRandom (default and
* getInstanceStrong).
* 2) Insecure random generation using java.util.Random.
* 3) Flawed PRNG usage by setting a fixed seed.
* 4) Dynamic PRNG selection based on configuration.
* 5) Usage of random data as nonces/IVs in symmetric encryption.
*
* SAST/CBOM Notes:
* - SecureRandom (and SecureRandom.getInstanceStrong) are recommended.
* - java.util.Random is not suitable for cryptographic purposes.
* - Re-seeding or using a fixed seed with SecureRandom makes it predictable.
* - IVs and nonces must be unique for each operation; reusing fixed values is
* insecure.
*/
public class PrngTest {
// ---------- Secure Random Generation ----------
/**
* Generates random bytes using the default SecureRandom.
* SAST: SecureRandom is recommended for cryptographically secure random data.
*
* @param numBytes Number of bytes to generate.
* @return A byte array of random data.
*/
public byte[] generateSecureRandomBytes(int numBytes) {
SecureRandom secureRandom = new SecureRandom();
byte[] bytes = new byte[numBytes];
secureRandom.nextBytes(bytes);
return bytes;
}
/**
* Generates random bytes using SecureRandom.getInstanceStrong().
* SAST: getInstanceStrong() returns a strong RNG (may block in some
* environments).
*
* @param numBytes Number of bytes to generate.
* @return A byte array of random data.
* @throws NoSuchAlgorithmException if a strong RNG is not available.
*/
public byte[] generateSecureRandomBytesStrong(int numBytes) throws NoSuchAlgorithmException {
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
byte[] bytes = new byte[numBytes];
secureRandom.nextBytes(bytes);
return bytes;
}
// ---------- Insecure Random Generation ----------
/**
* Generates random bytes using java.util.Random.
* SAST: java.util.Random is predictable and insecure for cryptographic
* purposes.
*
* @param numBytes Number of bytes to generate.
* @return A byte array of random data.
*/
public byte[] generateInsecureRandomBytes(int numBytes) {
Random random = new Random();
byte[] bytes = new byte[numBytes];
random.nextBytes(bytes);
return bytes;
}
/**
* Generates random bytes using SecureRandom with a fixed seed.
* SAST: Fixed seeding makes SecureRandom predictable and insecure.
*
* @param numBytes Number of bytes to generate.
* @return A byte array of predictable random data.
*/
public byte[] generatePredictableRandomBytes(int numBytes) {
SecureRandom secureRandom = new SecureRandom();
// Fixed seed (predictable and insecure)
secureRandom.setSeed(0xDEADBEEF);
byte[] bytes = new byte[numBytes];
secureRandom.nextBytes(bytes);
return bytes;
}
// ---------- Dynamic PRNG Selection ----------
/**
* Dynamically selects a PRNG algorithm based on a configuration property.
* If the algorithm is unknown, falls back to java.util.Random (insecure).
* SAST: Dynamic selection may introduce risk if an insecure RNG is chosen.
*
* @param algorithmName The PRNG algorithm name (e.g. "SHA1PRNG",
* "NativePRNGNonBlocking", "getInstanceStrong").
* @param numBytes Number of bytes to generate.
* @return A byte array of random data.
* @throws NoSuchAlgorithmException if the algorithm is not available.
*/
public byte[] dynamicRandomGeneration(String algorithmName, int numBytes) throws NoSuchAlgorithmException {
SecureRandom secureRandom;
if ("SHA1PRNG".equalsIgnoreCase(algorithmName)) {
// SHA1PRNG is older and less preferred.
secureRandom = SecureRandom.getInstance("SHA1PRNG");
} else if ("NativePRNGNonBlocking".equalsIgnoreCase(algorithmName)) {
secureRandom = SecureRandom.getInstance("NativePRNGNonBlocking");
} else if ("getInstanceStrong".equalsIgnoreCase(algorithmName)) {
secureRandom = SecureRandom.getInstanceStrong();
} else {
// Fallback to insecure java.util.Random.
Random random = new Random();
byte[] bytes = new byte[numBytes];
random.nextBytes(bytes);
return bytes;
}
byte[] bytes = new byte[numBytes];
secureRandom.nextBytes(bytes);
return bytes;
}
// ---------- Usage Examples: Nonce/IV Generation for Symmetric Encryption
// ----------
/**
* Demonstrates secure generation of an IV for AES-GCM encryption.
* SAST: A unique, random IV is required for each encryption operation.
*
* @return A 12-byte IV.
*/
public byte[] generateRandomIVForGCM() {
return generateSecureRandomBytes(12);
}
/**
* Demonstrates insecure use of a fixed IV for AES-GCM encryption.
* SAST: Reusing a fixed IV in AES-GCM compromises security.
*
* @return A fixed 12-byte IV (all zeros).
*/
public byte[] generateFixedIVForGCM() {
return new byte[12]; // 12 bytes of zeros.
}
// ---------- Example: Using PRNG for Key Generation ----------
/**
* Generates a secure 256-bit AES key using SecureRandom.
* SAST: Strong key generation is critical for symmetric cryptography.
*
* @return A new AES SecretKey.
* @throws Exception if key generation fails.
*/
public SecretKey generateAESKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256, new SecureRandom());
return keyGen.generateKey();
}
}

View File

@@ -0,0 +1,369 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
/**
* This class demonstrates cryptographic flows combining signing, encryption,
* and MAC.
*
* It intentionally includes both safe and unsafe patterns so that a SAST tool
* can detect:
*
* 1. **Sign then Encrypt (Unsafe)**
* - Signs the plaintext and encrypts only the signature, leaving the plaintext
* in cleartext.
* - *Issue:* The message is exposed, which could lead to replay or modification
* attacks.
*
* 2. **Encrypt then Sign (Safe with caveats)**
* - Encrypts the plaintext and then signs the ciphertext.
* - *Caveat:* The signature is in the clear; metadata (e.g. ciphertext length)
* may be exposed.
*
* 3. **MAC then Encrypt (Unsafe)**
* - Computes a MAC on the plaintext and appends it before encryption.
* - *Issue:* Operating on plaintext for MAC generation can leak information and
* is discouraged.
*
* 4. **Encrypt then MAC (Safe)**
* - Encrypts the plaintext and computes a MAC over the ciphertext.
* - *Benefit:* Provides a robust authenticated encryption construction when not
* using an AEAD cipher.
*
* Note: AES/GCM already provides authentication, so adding an external MAC is
* redundant.
*/
public class SignEncryptCombinations {
private static final SecureRandom RANDOM = new SecureRandom();
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
///////////////////////////////////////////////
// Key Generation for ECDSA on secp256r1
///////////////////////////////////////////////
public KeyPair generateECDSAKeyPair() throws Exception {
KeyPairGenerator ecKpg = KeyPairGenerator.getInstance("EC", "BC");
ecKpg.initialize(new ECGenParameterSpec("secp256r1"), RANDOM);
return ecKpg.generateKeyPair();
}
///////////////////////////////////////////////
// Signing with ECDSA (SHA256withECDSA)
///////////////////////////////////////////////
public byte[] signECDSA(PrivateKey privKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initSign(privKey, RANDOM);
signature.update(data);
return signature.sign();
}
public boolean verifyECDSA(PublicKey pubKey, byte[] data, byte[] signatureBytes) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initVerify(pubKey);
signature.update(data);
return signature.verify(signatureBytes);
}
///////////////////////////////////////////////
// Symmetric Encryption with AES-GCM
///////////////////////////////////////////////
/**
* Generates a 256-bit AES key.
*/
public SecretKey generateAESKey() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256);
return kg.generateKey();
}
/**
* Encrypts data using AES-GCM with a 12-byte IV and a 128-bit tag.
* Returns the concatenation of IV and ciphertext.
*/
public byte[] encryptAESGCM(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // 12-byte IV recommended for GCM
RANDOM.nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] result = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ciphertext, 0, result, iv.length, ciphertext.length);
return result;
}
/**
* Decrypts data that was encrypted using encryptAESGCM.
*/
public 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");
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
return cipher.doFinal(ciphertext);
}
///////////////////////////////////////////////
// HMAC Usage with HMAC-SHA256
///////////////////////////////////////////////
public byte[] computeHmacSHA256(SecretKey key, byte[] data) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
return mac.doFinal(data);
}
public boolean verifyHmacSHA256(SecretKey key, byte[] data, byte[] givenMac) throws Exception {
byte[] computed = computeHmacSHA256(key, data);
return Arrays.equals(computed, givenMac);
}
///////////////////////////////////////////////
// 1) SIGN THEN ENCRYPT vs. ENCRYPT THEN SIGN
///////////////////////////////////////////////
/**
* UNSAFE FLOW: Signs the plaintext and encrypts only the signature.
*
* <p>
* **Issue:** The plaintext message is not encrypted—only the signature is.
* This exposes the original message to eavesdroppers and negates the purpose of
* encryption.
* </p>
*
* @param signingKey ECDSA private key for signing.
* @param encryptionKey AES key for encryption.
* @param data The plaintext message.
* @return The encrypted signature only.
*/
public byte[] signThenEncrypt(PrivateKey signingKey, SecretKey encryptionKey, byte[] data) throws Exception {
// Sign the plaintext message.
byte[] signature = signECDSA(signingKey, data);
// **** UNSAFE: Only the signature is encrypted. The plaintext remains in the
// clear. ****
return encryptAESGCM(encryptionKey, signature);
}
/**
* Decrypts the signature and verifies it against the original plaintext.
*/
public boolean decryptThenVerify(SecretKey encryptionKey, PublicKey verifyingKey, byte[] encryptedSig,
byte[] originalData) throws Exception {
byte[] decryptedSig = decryptAESGCM(encryptionKey, encryptedSig);
return verifyECDSA(verifyingKey, originalData, decryptedSig);
}
/**
* SAFE FLOW (with caveats): Encrypts the plaintext and then signs the
* ciphertext.
*
* <p>
* **Benefit:** The plaintext is fully encrypted and remains confidential.
* **Caveat:** The signature is transmitted in the clear. Although this does not
* compromise
* the message, it might reveal metadata (like ciphertext length).
* </p>
*
* @param encryptionKey AES key for encryption.
* @param signingKey ECDSA private key for signing.
* @param data The plaintext message.
* @return The concatenation of the ciphertext and its signature.
*/
public byte[] encryptThenSign(SecretKey encryptionKey, PrivateKey signingKey, byte[] data) throws Exception {
// Encrypt the plaintext.
byte[] ivCiphertext = encryptAESGCM(encryptionKey, data);
// Sign the ciphertext.
byte[] signature = signECDSA(signingKey, ivCiphertext);
// Combine ciphertext and signature.
byte[] combined = new byte[ivCiphertext.length + signature.length];
System.arraycopy(ivCiphertext, 0, combined, 0, ivCiphertext.length);
System.arraycopy(signature, 0, combined, ivCiphertext.length, signature.length);
return combined;
}
/**
* Extracts and verifies the signature from the combined ciphertext-signature
* bundle,
* then decrypts the ciphertext.
*
* <p>
* **Issue:** Here we assume a fixed signature length (70 bytes). In production,
* the signature length
* should be explicitly stored. Hard-coding a length is an unsafe pattern and
* may trigger SAST warnings.
* </p>
*
* @param verifyingKey ECDSA public key for signature verification.
* @param encryptionKey AES key for decryption.
* @param combined The combined ciphertext and signature.
* @return The decrypted plaintext message.
*/
public byte[] verifyThenDecrypt(PublicKey verifyingKey, SecretKey encryptionKey, byte[] combined) throws Exception {
int assumedSignatureLength = 70; // UNSAFE: Hard-coded signature length.
if (combined.length < assumedSignatureLength) {
throw new IllegalArgumentException("Combined data too short.");
}
int ctLen = combined.length - assumedSignatureLength;
byte[] ivCiphertext = Arrays.copyOfRange(combined, 0, ctLen);
byte[] signature = Arrays.copyOfRange(combined, ctLen, combined.length);
if (!verifyECDSA(verifyingKey, ivCiphertext, signature)) {
throw new SecurityException("Signature verification failed.");
}
return decryptAESGCM(encryptionKey, ivCiphertext);
}
///////////////////////////////////////////////
// 2) MAC THEN ENCRYPT vs. ENCRYPT THEN MAC
///////////////////////////////////////////////
/**
* UNSAFE FLOW: Computes a MAC on the plaintext, appends it to the plaintext,
* and then encrypts the combined data.
*
* <p>
* **Issue:** Operating on unencrypted plaintext to compute the MAC can leak
* structural
* information. Additionally, if the encryption scheme does not provide
* integrity,
* this construction is vulnerable.
* </p>
*
* @param macKey AES key used as the HMAC key (should be separate from the
* encryption key).
* @param encKey AES key for encryption.
* @param data The plaintext message.
* @return The encrypted (plaintext + MAC) bundle.
*/
public byte[] macThenEncrypt(SecretKey macKey, SecretKey encKey, byte[] data) throws Exception {
// Compute MAC over the plaintext.
byte[] mac = computeHmacSHA256(macKey, data);
// Combine plaintext and MAC.
byte[] combined = new byte[data.length + mac.length];
System.arraycopy(data, 0, combined, 0, data.length);
System.arraycopy(mac, 0, combined, data.length, mac.length);
// **** UNSAFE: The MAC is computed on plaintext, which can leak information.
// ****
return encryptAESGCM(encKey, combined);
}
/**
* Decrypts the combined data and verifies the MAC.
*
* @param macKey AES key used as the HMAC key.
* @param encKey AES key for decryption.
* @param ciphertext The encrypted bundle containing plaintext and MAC.
* @return true if the MAC is valid; false otherwise.
*/
public boolean decryptThenVerifyMac(SecretKey macKey, SecretKey encKey, byte[] ciphertext) throws Exception {
byte[] combined = decryptAESGCM(encKey, ciphertext);
if (combined.length < 32) { // HMAC-SHA256 produces a 32-byte MAC.
throw new IllegalArgumentException("Combined data too short for MAC verification.");
}
int dataLen = combined.length - 32;
byte[] originalData = Arrays.copyOfRange(combined, 0, dataLen);
byte[] extractedMac = Arrays.copyOfRange(combined, dataLen, combined.length);
return verifyHmacSHA256(macKey, originalData, extractedMac);
}
/**
* SAFE FLOW: Encrypts the plaintext and then computes a MAC over the
* ciphertext.
*
* <p>
* **Benefit:** This "encrypt-then-MAC" construction ensures that the ciphertext
* is both confidential
* and tamper-evident.
* </p>
*
* @param encKey AES key for encryption.
* @param macKey AES key used as the HMAC key.
* @param data The plaintext message.
* @return The concatenation of ciphertext and MAC.
*/
public byte[] encryptThenMac(SecretKey encKey, SecretKey macKey, byte[] data) throws Exception {
// Encrypt the plaintext.
byte[] ivCiphertext = encryptAESGCM(encKey, data);
// Compute MAC over the ciphertext.
byte[] mac = computeHmacSHA256(macKey, ivCiphertext);
// Combine ciphertext and MAC.
byte[] combined = new byte[ivCiphertext.length + mac.length];
System.arraycopy(ivCiphertext, 0, combined, 0, ivCiphertext.length);
System.arraycopy(mac, 0, combined, ivCiphertext.length, mac.length);
return combined;
}
/**
* Verifies the MAC and then decrypts the ciphertext.
*
* @param encKey AES key for decryption.
* @param macKey AES key used as the HMAC key.
* @param combined The combined ciphertext and MAC.
* @return The decrypted plaintext message.
*/
public byte[] verifyMacThenDecrypt(SecretKey encKey, SecretKey macKey, byte[] combined) throws Exception {
if (combined.length < 32) {
throw new IllegalArgumentException("Combined data too short for MAC verification.");
}
int macOffset = combined.length - 32;
byte[] ivCiphertext = Arrays.copyOfRange(combined, 0, macOffset);
byte[] extractedMac = Arrays.copyOfRange(combined, macOffset, combined.length);
if (!verifyHmacSHA256(macKey, ivCiphertext, extractedMac)) {
throw new SecurityException("MAC verification failed.");
}
return decryptAESGCM(encKey, ivCiphertext);
}
///////////////////////////////////////////////
// Demo: runAllCombinations
///////////////////////////////////////////////
public void runAllCombinations() throws Exception {
// Generate keys for signing and for encryption/MAC.
KeyPair signingKeys = generateECDSAKeyPair();
SecretKey encKey = generateAESKey();
SecretKey macKey = generateAESKey(); // Ensure a separate key for MAC operations.
byte[] message = "Hello, combinations!".getBytes();
// 1) Sign then Encrypt (Unsafe) vs. Encrypt then Sign (Safe with caveats)
System.out.println("--Sign Then Encrypt (UNSAFE)");
byte[] encryptedSig = signThenEncrypt(signingKeys.getPrivate(), encKey, message);
boolean steVerified = decryptThenVerify(encKey, signingKeys.getPublic(), encryptedSig, message);
System.out.println("signThenEncrypt -> signature verified? " + steVerified);
System.out.println("--Encrypt Then Sign (SAFE with caveats)");
byte[] combinedETS = encryptThenSign(encKey, signingKeys.getPrivate(), message);
byte[] finalPlain = verifyThenDecrypt(signingKeys.getPublic(), encKey, combinedETS);
System.out.println("encryptThenSign -> decrypted message: " + new String(finalPlain));
// 2) MAC then Encrypt (Unsafe) vs. Encrypt then MAC (Safe)
System.out.println("--MAC Then Encrypt (UNSAFE)");
byte[] mteCipher = macThenEncrypt(macKey, encKey, message);
boolean mteVerified = decryptThenVerifyMac(macKey, encKey, mteCipher);
System.out.println("macThenEncrypt -> MAC verified? " + mteVerified);
System.out.println("--Encrypt Then MAC (SAFE)");
byte[] etmCombined = encryptThenMac(encKey, macKey, message);
byte[] etmPlain = verifyMacThenDecrypt(encKey, macKey, etmCombined);
System.out.println("encryptThenMac -> decrypted message: " + new String(etmPlain));
}
}

View File

@@ -0,0 +1,380 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
import java.util.Properties;
/**
* Demonstrates various digital signature operations:
*
* 1) RSA-PSS (modern, safer)
* - CBOM/SAST: Classified as a Modern Digital Signature scheme using RSA-PSS.
* RSA-PSS with SHA-256 is recommended.
*
* 2) ECDSA with secp256r1
* - CBOM/SAST: Classified as an Elliptic Curve Digital Signature Algorithm.
* Secure when used with a strong curve and proper randomness.
*
* 3) Ed25519 (RFC 8032)
* - CBOM/SAST: Classified as a modern, high-performance signature scheme.
*
* 4) SHA1withRSA (deprecated/unsafe example)
* - CBOM/SAST: Classified as a legacy digital signature scheme.
* SHA-1 and 1024-bit RSA are deprecated.
*
* Additional nuanced examples:
*
* - Signing and verifying an empty message.
* - Signing data with non-ASCII characters.
* - Demonstrating signature tampering and its detection.
* - A dynamic (runtime-selected) signature algorithm scenario ("known
* unknown").
*
* Requirements:
* - BouncyCastle for ECDSA, Ed25519, and RSA-PSS (if needed).
* - Java 11+ for native Ed25519 support or using BC for older versions.
*/
public class SignatureOperation {
// static {
// // Register the BouncyCastle provider.
// Security.addProvider(new BouncyCastleProvider());
// }
///////////////////////////////////////
// 1. RSA-PSS (Recommended)
///////////////////////////////////////
/**
* Generate an RSA key pair for RSA-PSS.
* Uses a 2048-bit key.
*
* CBOM/SAST Notes:
* - Parent: Modern Digital Signature (RSA-PSS).
*/
public KeyPair generateRSAPSSKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
return kpg.generateKeyPair();
}
/**
* Sign data using RSA-PSS with SHA-256.
*
* CBOM/SAST Notes:
* - Parent: Modern Digital Signature (RSA-PSS).
*/
public byte[] signRSAPSS(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSAandMGF1");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
/**
* Verify data using RSA-PSS with SHA-256.
*
* CBOM/SAST Notes:
* - Parent: Modern Digital Signature (RSA-PSS).
*/
public boolean verifyRSAPSS(PublicKey publicKey, byte[] data, byte[] sigBytes) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSAandMGF1");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(sigBytes);
}
///////////////////////////////////////
// 2. ECDSA (secp256r1)
///////////////////////////////////////
/**
* Generate an ECDSA key pair on secp256r1.
*
* CBOM/SAST Notes:
* - Parent: Elliptic Curve Digital Signature.
*/
public KeyPair generateECDSAKeyPair() throws Exception {
KeyPairGenerator ecKpg = KeyPairGenerator.getInstance("EC", "BC");
ecKpg.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
return ecKpg.generateKeyPair();
}
/**
* Sign data using ECDSA with SHA-256.
*
* CBOM/SAST Notes:
* - Parent: Elliptic Curve Digital Signature.
*/
public byte[] signECDSA(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
/**
* Verify data using ECDSA with SHA-256.
*
* CBOM/SAST Notes:
* - Parent: Elliptic Curve Digital Signature.
*/
public boolean verifyECDSA(PublicKey publicKey, byte[] data, byte[] sigBytes) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(sigBytes);
}
///////////////////////////////////////
// 3. Ed25519 (RFC 8032)
///////////////////////////////////////
/**
* Generate an Ed25519 key pair.
*
* CBOM/SAST Notes:
* - Parent: Modern Digital Signature (EdDSA).
*/
public KeyPair generateEd25519KeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519", "BC");
return kpg.generateKeyPair();
}
/**
* Sign data using Ed25519.
*
* CBOM/SAST Notes:
* - Parent: Modern Digital Signature (EdDSA).
*/
public byte[] signEd25519(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("Ed25519", "BC");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
/**
* Verify data using Ed25519.
*
* CBOM/SAST Notes:
* - Parent: Modern Digital Signature (EdDSA).
*/
public boolean verifyEd25519(PublicKey publicKey, byte[] data, byte[] sigBytes) throws Exception {
Signature signature = Signature.getInstance("Ed25519", "BC");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(sigBytes);
}
///////////////////////////////////////
// 4. SHA1withRSA (Deprecated/Unsafe)
///////////////////////////////////////
/**
* Generate an RSA key pair for the deprecated/unsafe example.
* Uses a 1024-bit key.
*
* CBOM/SAST Notes:
* - Parent: Legacy Digital Signature.
* - RSA with SHA-1 and 1024-bit keys is deprecated and should be avoided.
*/
public KeyPair generateRSAUnsafeKeyPair() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
return kpg.generateKeyPair();
}
/**
* Sign data using SHA1withRSA.
*
* CBOM/SAST Notes:
* - Parent: Legacy Digital Signature.
* - SHA-1 is deprecated and RSA with 1024 bits is considered weak.
*/
public byte[] signSHA1withRSA(PrivateKey privateKey, byte[] data) throws Exception {
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
/**
* Verify data using SHA1withRSA.
*
* CBOM/SAST Notes:
* - Parent: Legacy Digital Signature.
* - Verification of SHA1withRSA is insecure.
*/
public boolean verifySHA1withRSA(PublicKey publicKey, byte[] data, byte[] sigBytes) throws Exception {
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(sigBytes);
}
///////////////////////////////////////
// Nuanced Edge-Case Examples
///////////////////////////////////////
/**
* Demonstrates signing and verifying an empty message.
*
* CBOM/SAST Notes:
* - Edge Case: Signing empty input should be handled correctly but might be
* unexpected.
*/
public void signAndVerifyEmptyMessage() throws Exception {
byte[] emptyMessage = new byte[0];
KeyPair kp = generateRSAPSSKeyPair();
byte[] sig = signRSAPSS(kp.getPrivate(), emptyMessage);
boolean verified = verifyRSAPSS(kp.getPublic(), emptyMessage, sig);
System.out.println("Empty message signature verified? " + verified);
}
/**
* Demonstrates signing and verifying data containing non-ASCII characters.
*
* CBOM/SAST Notes:
* - Edge Case: Non-ASCII (e.g., Unicode) data should be handled correctly.
*/
public void signAndVerifyNonAsciiMessage() throws Exception {
// Use a message with Unicode characters.
String nonAscii = "こんにちは世界"; // "Hello World" in Japanese.
byte[] message = nonAscii.getBytes("UTF-8");
KeyPair kp = generateEd25519KeyPair();
byte[] sig = signEd25519(kp.getPrivate(), message);
boolean verified = verifyEd25519(kp.getPublic(), message, sig);
System.out.println("Non-ASCII message signature verified? " + verified);
}
/**
* Demonstrates that even a slight tampering with the signature will cause
* verification to fail.
*
* CBOM/SAST Notes:
* - Edge Case: Signature integrity is critical. Any change—even a single
* byte—should invalidate the signature.
*/
public void tamperSignatureEdgeCase() throws Exception {
byte[] message = "Important Message".getBytes();
KeyPair kp = generateECDSAKeyPair();
byte[] originalSig = signECDSA(kp.getPrivate(), message);
// Tamper with the signature by flipping one bit.
byte[] tamperedSig = originalSig.clone();
tamperedSig[0] ^= 0x01;
boolean verifiedOriginal = verifyECDSA(kp.getPublic(), message, originalSig);
boolean verifiedTampered = verifyECDSA(kp.getPublic(), message, tamperedSig);
System.out.println("Original ECDSA signature verified? " + verifiedOriginal);
System.out.println("Tampered ECDSA signature verified? " + verifiedTampered);
}
/**
* Demonstrates dynamic signature algorithm selection.
* This is a "known unknown" scenario where the algorithm is chosen at runtime
* based on configuration. If the configuration is compromised or misconfigured,
* an insecure algorithm might be selected.
*
* CBOM/SAST Notes:
* - Known Unknown: Dynamic configuration introduces ambiguity and risk.
* - Ensure that fallback defaults are secure.
*/
public void dynamicSignatureSelectionDemo() throws Exception {
// Simulate loading a configuration.
Properties config = new Properties();
// For demonstration, let's assume the config might specify an algorithm.
// Possible values: "SHA256withRSAandMGF1", "SHA256withECDSA", "Ed25519",
// "SHA1withRSA"
// Here we simulate an unknown or insecure algorithm being selected.
config.setProperty("signature.algorithm", "SHA1withRSA"); // Insecure choice!
String algorithm = config.getProperty("signature.algorithm", "SHA256withRSAandMGF1");
KeyPair kp;
Signature signature;
if ("SHA256withRSAandMGF1".equalsIgnoreCase(algorithm)) {
kp = generateRSAPSSKeyPair();
signature = Signature.getInstance("SHA256withRSAandMGF1");
} else if ("SHA256withECDSA".equalsIgnoreCase(algorithm)) {
kp = generateECDSAKeyPair();
signature = Signature.getInstance("SHA256withECDSA", "BC");
} else if ("Ed25519".equalsIgnoreCase(algorithm)) {
kp = generateEd25519KeyPair();
signature = Signature.getInstance("Ed25519", "BC");
} else if ("SHA1withRSA".equalsIgnoreCase(algorithm)) {
kp = generateRSAUnsafeKeyPair();
signature = Signature.getInstance("SHA1withRSA");
} else {
// Fallback to a secure default.
kp = generateRSAPSSKeyPair();
signature = Signature.getInstance("SHA256withRSAandMGF1");
}
byte[] message = "Dynamic Signature Demo".getBytes();
signature.initSign(kp.getPrivate());
signature.update(message);
byte[] sigBytes = signature.sign();
// Verify using the same algorithm.
signature.initVerify(kp.getPublic());
signature.update(message);
boolean verified = signature.verify(sigBytes);
System.out.println("Dynamic algorithm (" + algorithm + ") signature verified? " + verified);
}
///////////////////////////////////////
// Demo Method: runSignatureDemos
///////////////////////////////////////
/**
* Demonstrates digital signature operations for various algorithms.
* It generates key pairs, signs a message, and verifies the signature for:
* - RSA-PSS
* - ECDSA (secp256r1)
* - Ed25519
* - SHA1withRSA (deprecated/unsafe)
* Additionally, it runs several edge-case demos.
*
* CBOM/SAST Classification:
* - Shows both modern, secure signature schemes and a deprecated example.
* - Also demonstrates handling of edge cases and dynamic selection risks.
*/
public void runSignatureDemos() throws Exception {
byte[] message = "Hello Signature World!".getBytes();
// ============ RSA-PSS ============
KeyPair rsaPssKP = generateRSAPSSKeyPair();
byte[] rsaPssSig = signRSAPSS(rsaPssKP.getPrivate(), message);
System.out.println("RSA-PSS Signature: " + Base64.getEncoder().encodeToString(rsaPssSig));
boolean rsaPssVerified = verifyRSAPSS(rsaPssKP.getPublic(), message, rsaPssSig);
System.out.println("RSA-PSS Verified? " + rsaPssVerified);
// ============ ECDSA (secp256r1) ============
KeyPair ecdsaKP = generateECDSAKeyPair();
byte[] ecdsaSig = signECDSA(ecdsaKP.getPrivate(), message);
System.out.println("ECDSA Signature: " + Base64.getEncoder().encodeToString(ecdsaSig));
boolean ecdsaVerified = verifyECDSA(ecdsaKP.getPublic(), message, ecdsaSig);
System.out.println("ECDSA Verified? " + ecdsaVerified);
// ============ Ed25519 ============
KeyPair ed25519KP = generateEd25519KeyPair();
byte[] ed25519Sig = signEd25519(ed25519KP.getPrivate(), message);
System.out.println("Ed25519 Signature: " + Base64.getEncoder().encodeToString(ed25519Sig));
boolean ed25519Verified = verifyEd25519(ed25519KP.getPublic(), message, ed25519Sig);
System.out.println("Ed25519 Verified? " + ed25519Verified);
// ============ SHA1withRSA (Deprecated/Unsafe) ============
KeyPair rsaUnsafeKP = generateRSAUnsafeKeyPair();
byte[] rsaUnsafeSig = signSHA1withRSA(rsaUnsafeKP.getPrivate(), message);
System.out.println("SHA1withRSA Signature (Insecure): " + Base64.getEncoder().encodeToString(rsaUnsafeSig));
boolean rsaUnsafeVerified = verifySHA1withRSA(rsaUnsafeKP.getPublic(), message, rsaUnsafeSig);
System.out.println("SHA1withRSA Verified? " + rsaUnsafeVerified);
// ============ Edge Cases ============
signAndVerifyEmptyMessage();
signAndVerifyNonAsciiMessage();
tamperSignatureEdgeCase();
dynamicSignatureSelectionDemo();
}
}

View File

@@ -0,0 +1,364 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
/**
* SymmetricAlgorithmTest demonstrates various symmetric encryption flows and
* key derivation
* scenarios that can be analyzed by SAST tools.
*
* It includes:
* 1) AES-GCM encryption with random nonce (secure).
* 2) AES-GCM encryption with fixed nonce (insecure).
* 3) AES-CBC encryption with random IV (secure).
* 4) AES-ECB encryption (insecure).
* 5) RC4 encryption (insecure).
* 6) DES and TripleDES encryption (insecure/weak).
* 7) ChaCha20 encryption (secure, if available).
* 8) KMAC-based key derivation used to derive a key for AES encryption.
* 9) Dynamic symmetric encryption selection based on configuration.
* 10) Further use: deriving two keys from symmetric key material via PBKDF2.
*
* SAST/CBOM notes:
* - Nonce/IV reuse (e.g., fixed nonce) must be flagged.
* - Insecure algorithms (RC4, DES, TripleDES, AES/ECB) are marked as unsafe.
* - Dynamic selection may lead to insecure fallback if misconfigured.
*/
public class SymmetricAlgorithm {
// static {
// Security.addProvider(new BouncyCastleProvider());
// }
// ---------- Secure Symmetric Encryption Flows ----------
/**
* AES-GCM encryption using a 12-byte random nonce.
* SAST: AES-GCM is secure when a unique nonce is used per encryption.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The IV prepended to the ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] aesGcmEncryptSafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // Recommended 12-byte nonce for GCM.
new SecureRandom().nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(ciphertext, 0, output, iv.length, ciphertext.length);
return output;
}
/**
* AES-GCM encryption using a fixed (constant) nonce.
* SAST: Fixed nonce reuse in AES-GCM is insecure as it destroys
* confidentiality.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The fixed IV prepended to the ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] aesGcmEncryptUnsafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12]; // Fixed IV (all zeros by default) insecure.
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(ciphertext, 0, output, iv.length, ciphertext.length);
return output;
}
/**
* AES-CBC encryption using a random IV.
* SAST: AES-CBC is secure if IVs are random and not reused.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The IV prepended to the ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] aesCbcEncryptSafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[16]; // 16-byte IV for AES block size.
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(ciphertext, 0, output, iv.length, ciphertext.length);
return output;
}
/**
* AES-ECB encryption.
* SAST: ECB mode is insecure as it does not use an IV, revealing data patterns.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] aesEcbEncryptUnsafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plaintext);
}
// ---------- Other Symmetric Algorithms ----------
/**
* RC4 encryption.
* SAST: RC4 is deprecated due to vulnerabilities.
*
* @param key The RC4 key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] rc4EncryptUnsafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("RC4");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plaintext);
}
/**
* DES encryption.
* SAST: DES is insecure due to its 56-bit effective key size.
*
* @param key The DES key.
* @param plaintext The plaintext to encrypt.
* @return The IV prepended to the ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] desEncryptUnsafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
byte[] iv = new byte[8];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(ciphertext, 0, output, iv.length, ciphertext.length);
return output;
}
/**
* TripleDES (DESede) encryption.
* SAST: TripleDES is weak by modern standards and is deprecated.
*
* @param key The TripleDES key.
* @param plaintext The plaintext to encrypt.
* @return The IV prepended to the ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] tripleDesEncryptUnsafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
byte[] iv = new byte[8];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(ciphertext, 0, output, iv.length, ciphertext.length);
return output;
}
/**
* ChaCha20 encryption.
* SAST: ChaCha20 is considered secure and is a modern alternative to AES.
*
* @param key The ChaCha20 key.
* @param plaintext The plaintext to encrypt.
* @return The nonce prepended to the ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] chacha20EncryptSafe(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("ChaCha20", "BC");
byte[] nonce = new byte[12]; // ChaCha20 typically uses a 12-byte nonce.
new SecureRandom().nextBytes(nonce);
// ChaCha20 may require an IvParameterSpec for the nonce.
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(nonce));
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[nonce.length + ciphertext.length];
System.arraycopy(nonce, 0, output, 0, nonce.length);
System.arraycopy(ciphertext, 0, output, nonce.length, ciphertext.length);
return output;
}
/**
* KMAC-based flow: Uses KMAC128 to derive key material for AES encryption.
* SAST: KMAC128 is secure as part of the SHA-3 family when used correctly.
*
* @param key The KMAC key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext (with IV) resulting from encryption with a derived
* key.
* @throws Exception if encryption fails.
*/
public byte[] kmacEncryptFlow(SecretKey key, byte[] plaintext) throws Exception {
Mac kmac = Mac.getInstance("KMAC128", "BC");
kmac.init(key);
byte[] kmacOutput = kmac.doFinal(plaintext);
// Use the first 16 bytes of KMAC output as an AES key.
SecretKey derivedKey = new SecretKeySpec(kmacOutput, 0, 16, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, derivedKey, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(ciphertext, 0, output, iv.length, ciphertext.length);
return output;
}
// ---------- Dynamic Algorithm Selection ----------
/**
* Dynamically selects a symmetric encryption algorithm based on a configuration
* property.
* If the algorithm is unknown or ambiguous, falls back to an insecure default
* (AES/ECB).
*
* SAST: Dynamic selection introduces a known unknown risk.
*
* @param algorithm The algorithm name from configuration.
* @param key The symmetric key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext.
* @throws Exception if encryption fails.
*/
public byte[] dynamicSymmetricEncryption(String algorithm, SecretKey key, byte[] plaintext) throws Exception {
if ("AES/GCM/NoPadding".equalsIgnoreCase(algorithm)) {
return aesGcmEncryptSafe(key, plaintext);
} else if ("AES/CBC/PKCS5Padding".equalsIgnoreCase(algorithm)) {
return aesCbcEncryptSafe(key, plaintext);
} else if ("AES/ECB/PKCS5Padding".equalsIgnoreCase(algorithm)) {
return aesEcbEncryptUnsafe(key, plaintext);
} else if ("RC4".equalsIgnoreCase(algorithm)) {
return rc4EncryptUnsafe(key, plaintext);
} else if ("ChaCha20".equalsIgnoreCase(algorithm)) {
return chacha20EncryptSafe(key, plaintext);
} else {
// Unknown algorithm: fallback to insecure AES/ECB.
return aesEcbEncryptUnsafe(key, plaintext);
}
}
// ---------- Further Use of Symmetric Keys ----------
/**
* Derives a key from an input key by simple truncation.
* SAST: This approach is ambiguous; a proper KDF should be used.
*
* @param key The input symmetric key.
* @return A derived 128-bit key.
*/
public byte[] deriveKeyFromKey(SecretKey key) {
byte[] keyBytes = key.getEncoded();
return Arrays.copyOf(keyBytes, 16);
}
/**
* Further use: Derives two separate keys from a symmetric key using PBKDF2,
* then uses one key for encryption and one for MACing ciphertext.
* SAST: This key-splitting approach is acceptable if PBKDF2 is used securely.
*
* @param key The input key material.
* @param plaintext The plaintext to encrypt.
* @return The concatenated ciphertext and its MAC.
* @throws Exception if key derivation or encryption fails.
*/
public byte[] furtherUseSymmetricKeyForKeyDerivation(SecretKey key, byte[] plaintext) throws Exception {
String keyAsString = Base64.getEncoder().encodeToString(key.getEncoded());
byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(keyAsString.toCharArray(), salt, 10000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] derived = factory.generateSecret(spec).getEncoded();
byte[] encKeyBytes = Arrays.copyOfRange(derived, 0, 16);
byte[] macKeyBytes = Arrays.copyOfRange(derived, 16, 32);
SecretKey encKey = new SecretKeySpec(encKeyBytes, "AES");
SecretKey derivedMacKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, encKey, new GCMParameterSpec(128, iv));
byte[] ciphertext = cipher.doFinal(plaintext);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(derivedMacKey);
byte[] computedMac = mac.doFinal(ciphertext);
byte[] output = new byte[ciphertext.length + computedMac.length];
System.arraycopy(ciphertext, 0, output, 0, ciphertext.length);
System.arraycopy(computedMac, 0, output, ciphertext.length, computedMac.length);
storeEncryptedOutput(output);
return output;
}
/**
* Stores the encrypted output.
* SAST: In production, secure storage/transmission is required.
*
* @param output The output to store.
*/
public void storeEncryptedOutput(byte[] output) {
String stored = Base64.getEncoder().encodeToString(output);
}
// ---------- Helper Methods ----------
/**
* Generates a secure 256-bit AES key.
* SAST: Uses a strong RNG for key generation.
*
* @return A new AES SecretKey.
* @throws Exception if key generation fails.
*/
public SecretKey generateAESKey() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256);
return kg.generateKey();
}
/**
* Generates a random salt of the specified length using SecureRandom.
* SAST: Salting is essential for secure key derivation.
*
* @param length The salt length.
* @return A byte array representing the salt.
*/
private byte[] generateSalt(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return salt;
}
}

View File

@@ -0,0 +1,144 @@
package com.example.crypto.algorithms;
//import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
/**
* SymmetricModesTest demonstrates the use of advanced cipher modes for
* symmetric encryption:
*
* 1. AES/KWP/NoPadding: Uses AES Key Wrap with Padding (KWP) to securely wrap
* (encrypt) a key.
* - Secure usage: Uses a randomly generated wrapping key.
*
* 2. AES/OFB8/NoPadding: Uses AES in Output Feedback mode with an 8-bit
* feedback size.
* - Secure usage: Uses a random IV for each encryption.
* - Insecure usage: Using a fixed IV (or nonce) in OFB mode compromises
* confidentiality.
*
* In production, algorithm parameters (such as mode, padding, and IV
* generation) should be
* externalized via configuration files to support crypto agility.
*/
public class SymmetricModesTest {
// static {
// // Register BouncyCastle provider for additional cipher modes.
// Security.addProvider(new BouncyCastleProvider());
// }
// ---------------------------
// AES/KWP/NoPadding Example
// ---------------------------
/**
* Securely wraps a target AES key using AES/KWP/NoPadding.
*
* Best Practice:
* - The wrapping key must be generated randomly.
* - AES/KWP provides key wrapping with padding, suitable for keys whose lengths
* are not multiples of the block size.
*
* @return The Base64-encoded wrapped key.
* @throws Exception if an error occurs during key wrapping.
*/
public String secureAESKWPWrap() throws Exception {
// Generate a random wrapping key (256-bit) for key wrapping.
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256, new SecureRandom());
SecretKey wrappingKey = kg.generateKey();
// Generate a target AES key to be wrapped (128-bit).
kg.init(128, new SecureRandom());
SecretKey targetKey = kg.generateKey();
// Use AES/KWP (Key Wrap with Padding) to wrap the target key.
Cipher cipher = Cipher.getInstance("AES/KWP/NoPadding", "BC");
cipher.init(Cipher.WRAP_MODE, wrappingKey);
byte[] wrappedKey = cipher.wrap(targetKey);
return Base64.getEncoder().encodeToString(wrappedKey);
}
// ---------------------------
// AES/OFB8/NoPadding Examples
// ---------------------------
/**
* Securely encrypts plaintext using AES in OFB mode with an 8-bit feedback size
* (AES/OFB8/NoPadding).
*
* Best Practice:
* - Use a fresh, random IV for each encryption operation.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext (Base64-encoded) with the IV prepended.
* @throws Exception if encryption fails.
*/
public String secureAesOfb8Encryption(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/OFB8/NoPadding", "BC");
byte[] iv = new byte[16]; // IV size for AES block cipher (128-bit) even if feedback is 8-bit.
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
// Prepend IV to ciphertext (as is common practice)
byte[] output = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, output, 0, iv.length);
System.arraycopy(ciphertext, 0, output, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(output);
}
/**
* Insecurely encrypts plaintext using AES in OFB mode with an 8-bit feedback
* size (AES/OFB8/NoPadding)
* by using a fixed IV.
*
* Best Practice Violation:
* - Using a fixed IV (or nonce) with any encryption mode (including OFB)
* compromises the cipher's security.
*
* @param key The AES key.
* @param plaintext The plaintext to encrypt.
* @return The ciphertext (Base64-encoded) with the fixed IV prepended.
* @throws Exception if encryption fails.
*/
public String insecureAesOfb8Encryption(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/OFB8/NoPadding", "BC");
// Fixed IV: Insecure because it causes nonce/IV reuse.
byte[] fixedIV = new byte[16]; // All zeros.
IvParameterSpec ivSpec = new IvParameterSpec(fixedIV);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] output = new byte[fixedIV.length + ciphertext.length];
System.arraycopy(fixedIV, 0, output, 0, fixedIV.length);
System.arraycopy(ciphertext, 0, output, fixedIV.length, ciphertext.length);
return Base64.getEncoder().encodeToString(output);
}
// ---------------------------
// Helper Methods
// ---------------------------
/**
* Generates a secure 256-bit AES key.
*
* @return A new AES SecretKey.
* @throws Exception if key generation fails.
*/
public SecretKey generateAESKey() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256, new SecureRandom());
return kg.generateKey();
}
}

View File

@@ -0,0 +1,48 @@
package com.example.crypto.algorithms;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;
import java.util.Base64;
import java.util.Random;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.io.IOException;
public class UniversalFlowTest {
public void simpleAESEncryption() throws Exception {
String algorithm = "AES";
String otherAlgorithm = loadAlgorithmFromDisk();
// Randomly select between the known algorithm and the one loaded from disk
String selectedAlgorithm = (new Random().nextInt(2) == 0) ? algorithm : otherAlgorithm;
KeyGenerator keyGen = KeyGenerator.getInstance(selectedAlgorithm);
keyGen.init(256); // 256-bit AES key.
SecretKey key = keyGen.generateKey();
String algorithm2 = "AES/GCM/NoPadding";
Cipher cipher = Cipher.getInstance(algorithm2);
byte[] iv = new byte[12]; // 12-byte IV recommended for GCM.
new SecureRandom().nextBytes(iv);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); // 128-bit authentication tag.
cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
byte[] encryptedData = cipher.doFinal("Sensitive Data".getBytes());
}
// Method to load algorithm from disk
private String loadAlgorithmFromDisk() {
try {
// Implementation to load algorithm name from a file
Path path = Paths.get("algorithm.txt");
return Files.readString(path).trim();
} catch (IOException e) {
// Fallback to default algorithm if loading fails
System.err.println("Failed to load algorithm from disk: " + e.getMessage());
return "AES";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
import java
import experimental.quantum.Language
from Crypto::NodeBase n, string key
select n, key, n.getChild(key)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
import java
import experimental.quantum.Language
from Crypto::NodeBase n, string key, string value, Location location
where n.properties(key, value, location)
select n, key, value, location

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
import java
import experimental.quantum.Language
from Crypto::NodeBase n
select n