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

257 lines
9.9 KiB
Java

package com.example.crypto.artifacts;
// import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
* 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;
}
}