mirror of
https://github.com/github/codeql.git
synced 2026-02-15 22:43:43 +01:00
347 lines
14 KiB
Java
347 lines
14 KiB
Java
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 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();
|
|
tamperSignatureEdgeCase();
|
|
dynamicSignatureSelectionDemo();
|
|
}
|
|
}
|