Introduction to Hybrid Cryptography
Hybrid cryptography combines the efficiency of symmetric encryption with the convenience of asymmetric encryption. It uses asymmetric cryptography to securely exchange a symmetric key, which then encrypts the actual data. This approach provides the best of both worlds: secure key exchange and fast bulk encryption.
System Architecture Overview
Hybrid Cryptography System ├── Key Exchange Layer │ ├ - RSA (2048/4096 bits) │ ├ - ECC (P-256/P-384/P-521) │ ├ - DH/ECDH (Key Agreement) │ └ - KEM (Key Encapsulation) ├── Symmetric Encryption Layer │ ├ - AES (128/192/256 bits) │ ├ - ChaCha20-Poly1305 │ ├ - GCM/SIV Mode (AEAD) │ └ - Key Derivation (HKDF/PBKDF2) ├── Digital Signatures │ ├ - RSA-PSS/ PKCS#1 v1.5 │ ├ - ECDSA (NIST curves) │ ├ - EdDSA (Ed25519/Ed448) │ └ - Falcon (Post-quantum) └── Integration Patterns ├ - Hybrid Encryption (KEM + DEM) ├ - Signcryption (Combined Sign/Encrypt) ├ - Multi-Recipient Encryption └ - Hybrid KEM (Classical + PQ)
Core Implementation
1. Maven Dependencies
<properties>
<bouncycastle.version>1.78</bouncycastle.version>
<tink.version>1.12.0</tink.version>
<google.kms.version>2.34.0</google.kms.version>
</properties>
<dependencies>
<!-- Bouncy Castle for cryptographic primitives -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- Google Tink for high-level crypto -->
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
<version>${tink.version}</version>
</dependency>
<!-- Google Cloud KMS integration -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-kms</artifactId>
<version>${google.kms.version}</version>
</dependency>
<!-- JCA/JCE extensions -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- Protocol Buffers for serialization -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.1</version>
</dependency>
<!-- Apache Commons for utilities -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>
2. Hybrid Cryptography Service
package com.hybrid.crypto;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Service;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Service
public class HybridCryptographyService {
static {
Security.addProvider(new BouncyCastleProvider());
}
private final SecureRandom secureRandom = new SecureRandom();
// Supported algorithms
public enum AsymmetricAlgorithm {
RSA_2048("RSA", 2048),
RSA_4096("RSA", 4096),
EC_P256("EC", 256),
EC_P384("EC", 384),
EC_P521("EC", 521),
ED25519("Ed25519", 256),
X25519("X25519", 256);
final String algorithm;
final int keySize;
AsymmetricAlgorithm(String algorithm, int keySize) {
this.algorithm = algorithm;
this.keySize = keySize;
}
}
public enum SymmetricAlgorithm {
AES_GCM_128("AES/GCM/NoPadding", 128, 12, 16),
AES_GCM_256("AES/GCM/NoPadding", 256, 12, 16),
AES_CCM_128("AES/CCM/NoPadding", 128, 13, 8),
CHACHA20_POLY1305("ChaCha20-Poly1305/None/NoPadding", 256, 12, 16),
AES_SIV_256("AES/SIV/NoPadding", 256, 16, 16);
final String transformation;
final int keySize;
final int ivSize;
final int tagSize;
SymmetricAlgorithm(String transformation, int keySize, int ivSize, int tagSize) {
this.transformation = transformation;
this.keySize = keySize;
this.ivSize = ivSize;
this.tagSize = tagSize;
}
}
public enum KeyExchangeAlgorithm {
RSA_OAEP_SHA256,
RSA_OAEP_SHA384,
RSA_OAEP_SHA512,
ECDH_SHA256,
ECDH_SHA384,
ECDH_SHA512,
X25519
}
/**
* Generate hybrid key pair (asymmetric keys)
*/
public KeyPair generateKeyPair(AsymmetricAlgorithm algorithm)
throws HybridException {
try {
KeyPairGenerator keyGen;
if (algorithm == AsymmetricAlgorithm.ED25519) {
keyGen = KeyPairGenerator.getInstance("Ed25519", "BC");
} else if (algorithm == AsymmetricAlgorithm.X25519) {
keyGen = KeyPairGenerator.getInstance("X25519", "BC");
} else {
keyGen = KeyPairGenerator.getInstance(algorithm.algorithm, "BC");
keyGen.initialize(algorithm.keySize, secureRandom);
}
return keyGen.generateKeyPair();
} catch (Exception e) {
throw new HybridException("Key pair generation failed", e);
}
}
/**
* Hybrid encryption: RSA/EC + AES
*/
public HybridCiphertext encrypt(byte[] plaintext,
PublicKey publicKey,
AsymmetricAlgorithm asymAlg,
SymmetricAlgorithm symAlg)
throws HybridException {
try {
// Step 1: Generate ephemeral symmetric key
byte[] symmetricKey = generateSymmetricKey(symAlg.keySize / 8);
byte[] iv = generateIV(symAlg.ivSize);
// Step 2: Encrypt plaintext with symmetric key
byte[] ciphertext = symmetricEncrypt(plaintext, symmetricKey, iv, symAlg);
// Step 3: Encrypt symmetric key with public key
byte[] encryptedKey = asymmetricEncrypt(symmetricKey, publicKey, asymAlg);
// Step 4: Create hybrid ciphertext
return new HybridCiphertext(
encryptedKey,
iv,
ciphertext,
asymAlg,
symAlg
);
} catch (Exception e) {
throw new HybridException("Hybrid encryption failed", e);
}
}
/**
* Hybrid decryption
*/
public byte[] decrypt(HybridCiphertext ciphertext,
PrivateKey privateKey)
throws HybridException {
try {
// Step 1: Decrypt symmetric key with private key
byte[] symmetricKey = asymmetricDecrypt(
ciphertext.getEncryptedKey(),
privateKey,
ciphertext.getAsymAlgorithm()
);
// Step 2: Decrypt ciphertext with symmetric key
return symmetricDecrypt(
ciphertext.getCiphertext(),
symmetricKey,
ciphertext.getIv(),
ciphertext.getSymAlgorithm()
);
} catch (Exception e) {
throw new HybridException("Hybrid decryption failed", e);
}
}
/**
* Multi-recipient encryption (one message to many recipients)
*/
public MultiRecipientCiphertext encryptMultiRecipient(byte[] plaintext,
List<PublicKey> recipientKeys,
AsymmetricAlgorithm asymAlg,
SymmetricAlgorithm symAlg)
throws HybridException {
try {
// Generate single symmetric key for all recipients
byte[] symmetricKey = generateSymmetricKey(symAlg.keySize / 8);
byte[] iv = generateIV(symAlg.ivSize);
// Encrypt plaintext once
byte[] ciphertext = symmetricEncrypt(plaintext, symmetricKey, iv, symAlg);
// Encrypt symmetric key for each recipient
List<byte[]> encryptedKeys = new ArrayList<>();
for (PublicKey recipientKey : recipientKeys) {
byte[] encryptedKey = asymmetricEncrypt(symmetricKey, recipientKey, asymAlg);
encryptedKeys.add(encryptedKey);
}
return new MultiRecipientCiphertext(
encryptedKeys,
iv,
ciphertext,
asymAlg,
symAlg
);
} catch (Exception e) {
throw new HybridException("Multi-recipient encryption failed", e);
}
}
/**
* ECDH key agreement with symmetric encryption
*/
public ECDHHybridCiphertext encryptWithECDH(byte[] plaintext,
PublicKey recipientPublicKey,
PrivateKey senderPrivateKey,
SymmetricAlgorithm symAlg)
throws HybridException {
try {
// Step 1: Perform ECDH key agreement
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
keyAgreement.init(senderPrivateKey);
keyAgreement.doPhase(recipientPublicKey, true);
byte[] sharedSecret = keyAgreement.generateSecret();
// Step 2: Derive encryption key using HKDF
byte[] symmetricKey = hkdfDerive(sharedSecret, symAlg.keySize / 8, "encryption");
byte[] iv = generateIV(symAlg.ivSize);
// Step 3: Encrypt data
byte[] ciphertext = symmetricEncrypt(plaintext, symmetricKey, iv, symAlg);
// Step 4: Include sender's public key for verification
PublicKey senderPublicKey = derivePublicKey(senderPrivateKey);
return new ECDHHybridCiphertext(
senderPublicKey.getEncoded(),
iv,
ciphertext,
symAlg
);
} catch (Exception e) {
throw new HybridException("ECDH encryption failed", e);
}
}
/**
* Sign-then-Encrypt (authenticated encryption)
*/
public SignedEncryptedData signThenEncrypt(byte[] plaintext,
PrivateKey signingKey,
PublicKey encryptionKey,
SignatureAlgorithm sigAlg,
SymmetricAlgorithm symAlg)
throws HybridException {
try {
// Step 1: Sign the plaintext
Signature signature = Signature.getInstance(sigAlg.algorithm, "BC");
signature.initSign(signingKey);
signature.update(plaintext);
byte[] signatureBytes = signature.sign();
// Step 2: Create signed payload (plaintext + signature)
byte[] signedPayload = concatenate(plaintext, signatureBytes);
// Step 3: Encrypt the signed payload
HybridCiphertext encrypted = encrypt(
signedPayload,
encryptionKey,
sigAlg.asymmetricAlgorithm,
symAlg
);
return new SignedEncryptedData(encrypted, sigAlg);
} catch (Exception e) {
throw new HybridException("Sign-then-encrypt failed", e);
}
}
/**
* Decrypt-then-Verify
*/
public byte[] decryptThenVerify(SignedEncryptedData encryptedData,
PrivateKey decryptionKey,
PublicKey verificationKey)
throws HybridException {
try {
// Step 1: Decrypt the payload
byte[] signedPayload = decrypt(
encryptedData.getEncryptedData(),
decryptionKey
);
// Step 2: Split plaintext and signature
int signatureLength = getSignatureLength(encryptedData.getSignatureAlgorithm());
byte[] plaintext = Arrays.copyOfRange(
signedPayload, 0, signedPayload.length - signatureLength
);
byte[] signatureBytes = Arrays.copyOfRange(
signedPayload, signedPayload.length - signatureLength, signedPayload.length
);
// Step 3: Verify signature
Signature signature = Signature.getInstance(
encryptedData.getSignatureAlgorithm().algorithm, "BC"
);
signature.initVerify(verificationKey);
signature.update(plaintext);
if (!signature.verify(signatureBytes)) {
throw new HybridException("Signature verification failed");
}
return plaintext;
} catch (Exception e) {
throw new HybridException("Decrypt-then-verify failed", e);
}
}
/**
* Hybrid KEM (Key Encapsulation Mechanism)
*/
public KEMCiphertext kemEncapsulate(PublicKey recipientKey,
KeyExchangeAlgorithm kemAlg,
SymmetricAlgorithm symAlg)
throws HybridException {
try {
// Generate random secret
byte[] sharedSecret = new byte[32];
secureRandom.nextBytes(sharedSecret);
// Encapsulate secret with recipient's key
byte[] encapsulated = asymmetricEncrypt(sharedSecret, recipientKey,
getAsymmetricAlgFromKEM(kemAlg));
// Derive final encryption key
byte[] encryptionKey = hkdfDerive(sharedSecret, symAlg.keySize / 8, "KEM");
return new KEMCiphertext(
encapsulated,
encryptionKey,
kemAlg,
symAlg
);
} catch (Exception e) {
throw new HybridException("KEM encapsulation failed", e);
}
}
/**
* KEM decapsulation
*/
public byte[] kemDecapsulate(KEMCiphertext kemCiphertext,
PrivateKey recipientKey)
throws HybridException {
try {
// Decapsulate to get shared secret
byte[] sharedSecret = asymmetricDecrypt(
kemCiphertext.getEncapsulated(),
recipientKey,
getAsymmetricAlgFromKEM(kemCiphertext.getKemAlgorithm())
);
// Derive same encryption key
return hkdfDerive(
sharedSecret,
kemCiphertext.getSymAlgorithm().keySize / 8,
"KEM"
);
} catch (Exception e) {
throw new HybridException("KEM decapsulation failed", e);
}
}
// Helper methods
private byte[] generateSymmetricKey(int keySizeBytes) {
byte[] key = new byte[keySizeBytes];
secureRandom.nextBytes(key);
return key;
}
private byte[] generateIV(int ivSize) {
byte[] iv = new byte[ivSize];
secureRandom.nextBytes(iv);
return iv;
}
private byte[] symmetricEncrypt(byte[] plaintext, byte[] key, byte[] iv,
SymmetricAlgorithm algorithm)
throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm.name().split("_")[0]);
Cipher cipher = Cipher.getInstance(algorithm.transformation, "BC");
// Handle different IV types (GCM, CCM, etc.)
AlgorithmParameterSpec paramSpec;
if (algorithm.transformation.contains("GCM")) {
paramSpec = new GCMParameterSpec(algorithm.tagSize * 8, iv);
} else if (algorithm.transformation.contains("CCM")) {
paramSpec = new CCMParameterSpec(algorithm.tagSize * 8, iv);
} else if (algorithm.transformation.contains("SIV")) {
paramSpec = new IvParameterSpec(iv);
} else {
paramSpec = new IvParameterSpec(iv);
}
cipher.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec);
return cipher.doFinal(plaintext);
}
private byte[] symmetricDecrypt(byte[] ciphertext, byte[] key, byte[] iv,
SymmetricAlgorithm algorithm)
throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm.name().split("_")[0]);
Cipher cipher = Cipher.getInstance(algorithm.transformation, "BC");
AlgorithmParameterSpec paramSpec;
if (algorithm.transformation.contains("GCM")) {
paramSpec = new GCMParameterSpec(algorithm.tagSize * 8, iv);
} else if (algorithm.transformation.contains("CCM")) {
paramSpec = new CCMParameterSpec(algorithm.tagSize * 8, iv);
} else {
paramSpec = new IvParameterSpec(iv);
}
cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);
return cipher.doFinal(ciphertext);
}
private byte[] asymmetricEncrypt(byte[] data, PublicKey publicKey,
AsymmetricAlgorithm algorithm)
throws Exception {
Cipher cipher;
switch (algorithm) {
case RSA_2048:
case RSA_4096:
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC");
break;
default:
throw new UnsupportedOperationException("Algorithm not supported for encryption");
}
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
private byte[] asymmetricDecrypt(byte[] encryptedData, PrivateKey privateKey,
AsymmetricAlgorithm algorithm)
throws Exception {
Cipher cipher;
switch (algorithm) {
case RSA_2048:
case RSA_4096:
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC");
break;
default:
throw new UnsupportedOperationException("Algorithm not supported for decryption");
}
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encryptedData);
}
private byte[] hkdfDerive(byte[] input, int outputLength, String info)
throws Exception {
// HKDF implementation using HmacSHA256
Mac mac = Mac.getInstance("HmacSHA256");
byte[] salt = "HYBRID_CRYPTO_SALT".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(salt, "HmacSHA256");
mac.init(keySpec);
byte[] prk = mac.doFinal(input);
keySpec = new SecretKeySpec(prk, "HmacSHA256");
mac.init(keySpec);
byte[] output = new byte[outputLength];
byte[] t = new byte[0];
byte[] counter = new byte[1];
for (int i = 1; i <= (outputLength + 31) / 32; i++) {
mac.update(t);
mac.update(info.getBytes());
counter[0] = (byte) i;
mac.update(counter);
t = mac.doFinal();
System.arraycopy(t, 0, output, (i - 1) * 32,
Math.min(32, outputLength - (i - 1) * 32));
}
return output;
}
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;
}
private AsymmetricAlgorithm getAsymmetricAlgFromKEM(KeyExchangeAlgorithm kemAlg) {
switch (kemAlg) {
case RSA_OAEP_SHA256:
case RSA_OAEP_SHA384:
case RSA_OAEP_SHA512:
return AsymmetricAlgorithm.RSA_4096;
case ECDH_SHA256:
case ECDH_SHA384:
case ECDH_SHA512:
return AsymmetricAlgorithm.EC_P256;
default:
return AsymmetricAlgorithm.RSA_4096;
}
}
private int getSignatureLength(SignatureAlgorithm sigAlg) {
switch (sigAlg) {
case SHA256_WITH_RSA:
case SHA256_WITH_ECDSA:
return 256;
case SHA384_WITH_RSA:
case SHA384_WITH_ECDSA:
return 384;
case SHA512_WITH_RSA:
case SHA512_WITH_ECDSA:
return 512;
default:
return 256;
}
}
// Data classes
public static class HybridCiphertext {
private final byte[] encryptedKey;
private final byte[] iv;
private final byte[] ciphertext;
private final AsymmetricAlgorithm asymAlgorithm;
private final SymmetricAlgorithm symAlgorithm;
public HybridCiphertext(byte[] encryptedKey, byte[] iv, byte[] ciphertext,
AsymmetricAlgorithm asymAlgorithm, SymmetricAlgorithm symAlgorithm) {
this.encryptedKey = encryptedKey;
this.iv = iv;
this.ciphertext = ciphertext;
this.asymAlgorithm = asymAlgorithm;
this.symAlgorithm = symAlgorithm;
}
// getters
}
public static class MultiRecipientCiphertext {
private final List<byte[]> encryptedKeys;
private final byte[] iv;
private final byte[] ciphertext;
private final AsymmetricAlgorithm asymAlgorithm;
private final SymmetricAlgorithm symAlgorithm;
// constructor and getters
}
public static class ECDHHybridCiphertext {
private final byte[] senderPublicKey;
private final byte[] iv;
private final byte[] ciphertext;
private final SymmetricAlgorithm symAlgorithm;
// constructor and getters
}
public enum SignatureAlgorithm {
SHA256_WITH_RSA("SHA256withRSA", AsymmetricAlgorithm.RSA_4096),
SHA384_WITH_RSA("SHA384withRSA", AsymmetricAlgorithm.RSA_4096),
SHA512_WITH_RSA("SHA512withRSA", AsymmetricAlgorithm.RSA_4096),
SHA256_WITH_ECDSA("SHA256withECDSA", AsymmetricAlgorithm.EC_P256),
SHA384_WITH_ECDSA("SHA384withECDSA", AsymmetricAlgorithm.EC_P384),
SHA512_WITH_ECDSA("SHA512withECDSA", AsymmetricAlgorithm.EC_P521);
final String algorithm;
final AsymmetricAlgorithm asymmetricAlgorithm;
SignatureAlgorithm(String algorithm, AsymmetricAlgorithm asymmetricAlgorithm) {
this.algorithm = algorithm;
this.asymmetricAlgorithm = asymmetricAlgorithm;
}
}
public static class SignedEncryptedData {
private final HybridCiphertext encryptedData;
private final SignatureAlgorithm signatureAlgorithm;
// constructor and getters
}
public static class KEMCiphertext {
private final byte[] encapsulated;
private final byte[] encryptionKey;
private final KeyExchangeAlgorithm kemAlgorithm;
private final SymmetricAlgorithm symAlgorithm;
// constructor and getters
}
public static class HybridException extends Exception {
public HybridException(String message) { super(message); }
public HybridException(String message, Throwable cause) { super(message, cause); }
}
}
3. Key Derivation and Management
package com.hybrid.crypto;
import org.springframework.stereotype.Component;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.util.Arrays;
import java.util.Base64;
@Component
public class KeyDerivationService {
private final SecureRandom secureRandom = new SecureRandom();
/**
* PBKDF2 for password-based key derivation
*/
public byte[] deriveFromPassword(char[] password, byte[] salt,
int keyLength, int iterations)
throws KeyDerivationException {
try {
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength * 8);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] key = factory.generateSecret(spec).getEncoded();
// Clear sensitive data
spec.clearPassword();
return key;
} catch (Exception e) {
throw new KeyDerivationException("Password-based key derivation failed", e);
}
}
/**
* HKDF (RFC 5869) for key expansion
*/
public HKDFResult hkdf(byte[] inputKeyingMaterial, byte[] salt,
byte[] info, int outputLength)
throws KeyDerivationException {
try {
// Step 1: Extract
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec;
if (salt == null || salt.length == 0) {
// Use zero salt if none provided
salt = new byte[32];
}
keySpec = new SecretKeySpec(salt, "HmacSHA256");
mac.init(keySpec);
byte[] prk = mac.doFinal(inputKeyingMaterial);
// Step 2: Expand
keySpec = new SecretKeySpec(prk, "HmacSHA256");
mac.init(keySpec);
byte[] output = new byte[outputLength];
byte[] t = new byte[0];
byte[] counter = new byte[1];
for (int i = 1; i <= (outputLength + 31) / 32; i++) {
mac.update(t);
mac.update(info);
counter[0] = (byte) i;
mac.update(counter);
t = mac.doFinal();
System.arraycopy(t, 0, output, (i - 1) * 32,
Math.min(32, outputLength - (i - 1) * 32));
}
return new HKDFResult(prk, output);
} catch (Exception e) {
throw new KeyDerivationException("HKDF failed", e);
}
}
/**
* Argon2 for memory-hard password hashing
*/
public byte[] argon2Derive(char[] password, byte[] salt,
int parallelism, int memory, int iterations,
int keyLength)
throws KeyDerivationException {
try {
// Using Bouncy Castle's Argon2 implementation
org.bouncycastle.crypto.generators.Argon2BytesGenerator generator =
new org.bouncycastle.crypto.generators.Argon2BytesGenerator();
org.bouncycastle.crypto.params.Argon2Parameters.Builder builder =
new org.bouncycastle.crypto.params.Argon2Parameters.Builder(
org.bouncycastle.crypto.params.Argon2Parameters.ARGON2_id
)
.withSalt(salt)
.withParallelism(parallelism)
.withMemoryAsKB(memory)
.withIterations(iterations);
generator.init(builder.build());
byte[] key = new byte[keyLength];
generator.generateBytes(password, key);
return key;
} catch (Exception e) {
throw new KeyDerivationException("Argon2 derivation failed", e);
}
}
/**
* Generate key pair from seed (deterministic)
*/
public KeyPair deterministicKeyPair(byte[] seed, String algorithm, int keySize)
throws KeyDerivationException {
try {
// Create deterministic random from seed
SecureRandom deterministicRandom = SecureRandom.getInstance("SHA1PRNG");
deterministicRandom.setSeed(seed);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm, "BC");
keyGen.initialize(keySize, deterministicRandom);
return keyGen.generateKeyPair();
} catch (Exception e) {
throw new KeyDerivationException("Deterministic key pair generation failed", e);
}
}
/**
* Split key into multiple parts (Shamir's Secret Sharing)
*/
public List<byte[]> splitKey(byte[] secret, int totalParts, int threshold)
throws KeyDerivationException {
try {
// Simple XOR-based sharing for demonstration
// In production, use proper SSSS implementation
List<byte[]> shares = new ArrayList<>();
for (int i = 0; i < totalParts; i++) {
byte[] share = new byte[secret.length];
if (i < threshold - 1) {
// Generate random shares
secureRandom.nextBytes(share);
} else {
// Last share is XOR of all others and secret
for (int j = 0; j < secret.length; j++) {
byte xor = secret[j];
for (int k = 0; k < threshold - 1; k++) {
xor ^= shares.get(k)[j];
}
share[j] = xor;
}
}
shares.add(share);
}
return shares;
} catch (Exception e) {
throw new KeyDerivationException("Key splitting failed", e);
}
}
/**
* Combine key shares
*/
public byte[] combineKeys(List<byte[]> shares, int threshold)
throws KeyDerivationException {
if (shares.size() < threshold) {
throw new KeyDerivationException("Not enough shares");
}
byte[] secret = new byte[shares.get(0).length];
// XOR all shares to recover secret
for (byte[] share : shares) {
for (int i = 0; i < secret.length; i++) {
secret[i] ^= share[i];
}
}
return secret;
}
/**
* Rotate key with key derivation
*/
public byte[] rotateKey(byte[] oldKey, byte[] context, int newKeyLength)
throws KeyDerivationException {
try {
// Derive new key from old key using HKDF
HKDFResult result = hkdf(
oldKey,
"ROTATION_SALT".getBytes(),
context,
newKeyLength
);
return result.getOutputKeyingMaterial();
} catch (Exception e) {
throw new KeyDerivationException("Key rotation failed", e);
}
}
/**
* Key wrapping (encrypt one key with another)
*/
public byte[] wrapKey(byte[] keyToWrap, byte[] wrappingKey)
throws KeyDerivationException {
try {
// Use AES Key Wrap (RFC 3394)
Cipher cipher = Cipher.getInstance("AESWrap", "BC");
SecretKeySpec keySpec = new SecretKeySpec(wrappingKey, "AES");
cipher.init(Cipher.WRAP_MODE, keySpec);
SecretKey key = new SecretKeySpec(keyToWrap, "AES");
return cipher.wrap(key);
} catch (Exception e) {
throw new KeyDerivationException("Key wrapping failed", e);
}
}
/**
* Key unwrapping
*/
public byte[] unwrapKey(byte[] wrappedKey, byte[] unwrappingKey)
throws KeyDerivationException {
try {
Cipher cipher = Cipher.getInstance("AESWrap", "BC");
SecretKeySpec keySpec = new SecretKeySpec(unwrappingKey, "AES");
cipher.init(Cipher.UNWRAP_MODE, keySpec);
Key key = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
return key.getEncoded();
} catch (Exception e) {
throw new KeyDerivationException("Key unwrapping failed", e);
}
}
/**
* Calculate key fingerprint
*/
public String keyFingerprint(byte[] key)
throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(key);
// Format as hex with colons
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 8; i++) { // First 8 bytes only
if (i > 0) sb.append(':');
sb.append(String.format("%02X", hash[i] & 0xFF));
}
return sb.toString();
}
// Data classes
public static class HKDFResult {
private final byte[] pseudoRandomKey;
private final byte[] outputKeyingMaterial;
public HKDFResult(byte[] pseudoRandomKey, byte[] outputKeyingMaterial) {
this.pseudoRandomKey = pseudoRandomKey;
this.outputKeyingMaterial = outputKeyingMaterial;
}
// getters
}
public static class KeyDerivationException extends Exception {
public KeyDerivationException(String message) { super(message); }
public KeyDerivationException(String message, Throwable cause) {
super(message, cause);
}
}
}
4. Hybrid KEM (Key Encapsulation Mechanism)
package com.hybrid.crypto;
import org.springframework.stereotype.Component;
import javax.crypto.*;
import java.security.*;
import java.util.Arrays;
@Component
public class HybridKEMService {
private final SecureRandom secureRandom = new SecureRandom();
/**
* RSA-KEM (RFC 5990)
*/
public RSAKEMResult rsaKEMEncapsulate(PublicKey recipientKey, int keyLength)
throws KEMException {
try {
// Generate random secret
byte[] secret = new byte[keyLength / 8];
secureRandom.nextBytes(secret);
// Encrypt secret with RSA
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, recipientKey);
byte[] encapsulated = cipher.doFinal(secret);
// Derive key from secret using KDF
byte[] derivedKey = kdf(secret, keyLength / 8, "RSA-KEM");
return new RSAKEMResult(encapsulated, derivedKey, secret);
} catch (Exception e) {
throw new KEMException("RSA-KEM encapsulation failed", e);
}
}
/**
* RSA-KEM decapsulation
*/
public byte[] rsaKEMDecapsulate(byte[] encapsulated, PrivateKey recipientKey,
int keyLength)
throws KEMException {
try {
// Decrypt to get secret
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, recipientKey);
byte[] secret = cipher.doFinal(encapsulated);
// Derive same key
return kdf(secret, keyLength / 8, "RSA-KEM");
} catch (Exception e) {
throw new KEMException("RSA-KEM decapsulation failed", e);
}
}
/**
* ECDH-KEM with NIST curves
*/
public ECDHKEMResult ecdhKEMEncapsulate(PublicKey recipientPublicKey,
String curveName,
int keyLength)
throws KEMException {
try {
// Generate ephemeral key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
keyGen.initialize(new ECGenParameterSpec(curveName), secureRandom);
KeyPair ephemeralKeyPair = keyGen.generateKeyPair();
// Perform ECDH
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
keyAgreement.init(ephemeralKeyPair.getPrivate());
keyAgreement.doPhase(recipientPublicKey, true);
byte[] sharedSecret = keyAgreement.generateSecret();
// Derive final key
byte[] derivedKey = kdf(sharedSecret, keyLength / 8, "ECDH-KEM");
return new ECDHKEMResult(
ephemeralKeyPair.getPublic().getEncoded(),
derivedKey,
sharedSecret
);
} catch (Exception e) {
throw new KEMException("ECDH-KEM encapsulation failed", e);
}
}
/**
* ECDH-KEM decapsulation
*/
public byte[] ecdhKEMDecapsulate(byte[] ephemeralPublicKeyBytes,
PrivateKey recipientPrivateKey,
String curveName,
int keyLength)
throws KEMException {
try {
// Reconstruct ephemeral public key
KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(ephemeralPublicKeyBytes);
PublicKey ephemeralPublicKey = keyFactory.generatePublic(keySpec);
// Perform ECDH
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
keyAgreement.init(recipientPrivateKey);
keyAgreement.doPhase(ephemeralPublicKey, true);
byte[] sharedSecret = keyAgreement.generateSecret();
// Derive same key
return kdf(sharedSecret, keyLength / 8, "ECDH-KEM");
} catch (Exception e) {
throw new KEMException("ECDH-KEM decapsulation failed", e);
}
}
/**
* Hybrid KEM (Classical + Post-Quantum)
*/
public HybridKEMResult hybridKEMEncapsulate(PublicKey classicalKey,
PublicKey pqKey,
int keyLength)
throws KEMException {
try {
// Generate two independent secrets
byte[] classicalSecret = new byte[keyLength / 16];
byte[] pqSecret = new byte[keyLength / 16];
secureRandom.nextBytes(classicalSecret);
secureRandom.nextBytes(pqSecret);
// Encapsulate with both schemes
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", "BC");
rsaCipher.init(Cipher.ENCRYPT_MODE, classicalKey);
byte[] classicalEncapsulated = rsaCipher.doFinal(classicalSecret);
// For PQ, we'd use something like Kyber or FrodoKEM
// This is a placeholder
byte[] pqEncapsulated = pqKEMEncapsulate(pqSecret, pqKey);
// Combine secrets using KDF
byte[] combinedSecret = concatenate(classicalSecret, pqSecret);
byte[] finalKey = kdf(combinedSecret, keyLength / 8, "HYBRID-KEM");
return new HybridKEMResult(
classicalEncapsulated,
pqEncapsulated,
finalKey,
combinedSecret
);
} catch (Exception e) {
throw new KEMException("Hybrid KEM encapsulation failed", e);
}
}
/**
* ML-KEM (Kyber) - Post-quantum KEM
*/
public MLKEMResult mlKEMEncapsulate(PublicKey recipientKey)
throws KEMException {
try {
// Kyber implementation using BC or PQC library
// This is a placeholder for actual Kyber implementation
byte[] publicKeyBytes = recipientKey.getEncoded();
// Generate random secret
byte[] secret = new byte[32];
secureRandom.nextBytes(secret);
// Encapsulate using Kyber
byte[] ciphertext = kyberEncrypt(publicKeyBytes, secret);
// Derive final key
byte[] sharedKey = shake256(secret, 32);
return new MLKEMResult(ciphertext, sharedKey);
} catch (Exception e) {
throw new KEMException("ML-KEM encapsulation failed", e);
}
}
/**
* KDF for KEM (Key Derivation Function)
*/
private byte[] kdf(byte[] secret, int outputLength, String context)
throws NoSuchAlgorithmException {
// Use HKDF or similar
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] result = new byte[outputLength];
byte[] counter = new byte[4];
for (int i = 0; i < (outputLength + 31) / 32; i++) {
counter[0] = (byte) (i + 1);
digest.update(counter);
digest.update(context.getBytes());
digest.update(secret);
byte[] hash = digest.digest();
System.arraycopy(hash, 0, result, i * 32,
Math.min(32, outputLength - i * 32));
}
return result;
}
// Placeholder methods for PQ crypto
private byte[] pqKEMEncapsulate(byte[] secret, PublicKey pqKey) {
return secret; // Placeholder
}
private byte[] kyberEncrypt(byte[] publicKey, byte[] secret) {
return secret; // Placeholder
}
private byte[] shake256(byte[] input, int outputLength) {
return input; // Placeholder
}
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;
}
// Data classes
public static class RSAKEMResult {
private final byte[] encapsulated;
private final byte[] derivedKey;
private final byte[] secret;
// constructor and getters
}
public static class ECDHKEMResult {
private final byte[] ephemeralPublicKey;
private final byte[] derivedKey;
private final byte[] sharedSecret;
// constructor and getters
}
public static class HybridKEMResult {
private final byte[] classicalEncapsulated;
private final byte[] pqEncapsulated;
private final byte[] finalKey;
private final byte[] combinedSecret;
// constructor and getters
}
public static class MLKEMResult {
private final byte[] ciphertext;
private final byte[] sharedKey;
// constructor and getters
}
public static class KEMException extends Exception {
public KEMException(String message) { super(message); }
public KEMException(String message, Throwable cause) { super(message, cause); }
}
}
5. Hybrid Digital Signatures
package com.hybrid.crypto;
import org.springframework.stereotype.Component;
import java.security.*;
import java.security.spec.*;
import java.util.Arrays;
@Component
public class HybridSignatureService {
private final SecureRandom secureRandom = new SecureRandom();
/**
* RSA-PSS signature with salt
*/
public byte[] signRSA_PSS(byte[] message, PrivateKey privateKey,
String hashAlgorithm, int saltLength)
throws SignatureException {
try {
Signature signature = Signature.getInstance("SHA256withRSA/PSS", "BC");
// Configure PSS parameters
PSSParameterSpec pssSpec = new PSSParameterSpec(
hashAlgorithm, "MGF1",
new MGF1ParameterSpec(hashAlgorithm),
saltLength, 1
);
signature.setParameter(pssSpec);
signature.initSign(privateKey);
signature.update(message);
return signature.sign();
} catch (Exception e) {
throw new SignatureException("RSA-PSS signing failed", e);
}
}
/**
* ECDSA signature with deterministic k (RFC 6979)
*/
public byte[] signECDSA_Deterministic(byte[] message, PrivateKey privateKey)
throws SignatureException {
try {
// Use deterministic ECDSA (RFC 6979)
Signature signature = Signature.getInstance("SHA256withECDSAinP1363Format", "BC");
signature.initSign(privateKey);
signature.update(message);
return signature.sign();
} catch (Exception e) {
throw new SignatureException("Deterministic ECDSA signing failed", e);
}
}
/**
* EdDSA (Ed25519) signature
*/
public byte[] signEdDSA(byte[] message, PrivateKey privateKey)
throws SignatureException {
try {
Signature signature = Signature.getInstance("Ed25519", "BC");
signature.initSign(privateKey);
signature.update(message);
return signature.sign();
} catch (Exception e) {
throw new SignatureException("EdDSA signing failed", e);
}
}
/**
* Hybrid signature (multiple algorithms combined)
*/
public HybridSignature hybridSign(byte[] message,
PrivateKey classicalKey,
PrivateKey pqKey)
throws SignatureException {
try {
// Sign with classical algorithm (e.g., ECDSA)
Signature classicalSig = Signature.getInstance("SHA256withECDSA", "BC");
classicalSig.initSign(classicalKey);
classicalSig.update(message);
byte[] classicalSignature = classicalSig.sign();
// Sign with post-quantum algorithm (e.g., Falcon)
// Placeholder for actual PQ signature
byte[] pqSignature = pqSign(message, pqKey);
// Combine signatures
return new HybridSignature(classicalSignature, pqSignature);
} catch (Exception e) {
throw new SignatureException("Hybrid signing failed", e);
}
}
/**
* Verify hybrid signature
*/
public boolean hybridVerify(byte[] message, HybridSignature signature,
PublicKey classicalKey, PublicKey pqKey)
throws SignatureException {
try {
// Verify classical signature
Signature classicalSig = Signature.getInstance("SHA256withECDSA", "BC");
classicalSig.initVerify(classicalKey);
classicalSig.update(message);
boolean classicalValid = classicalSig.verify(signature.getClassicalSignature());
// Verify PQ signature
boolean pqValid = pqVerify(message, signature.getPqSignature(), pqKey);
// Both must be valid for composite security
return classicalValid && pqValid;
} catch (Exception e) {
throw new SignatureException("Hybrid verification failed", e);
}
}
/**
* Threshold signature (multi-party)
*/
public ThresholdSignature thresholdSign(byte[] message,
List<PrivateKey> signerKeys,
int threshold)
throws SignatureException {
try {
if (signerKeys.size() < threshold) {
throw new SignatureException("Not enough signers");
}
List<byte[]> partialSignatures = new ArrayList<>();
for (PrivateKey key : signerKeys) {
Signature sig = Signature.getInstance("SHA256withECDSA", "BC");
sig.initSign(key);
sig.update(message);
partialSignatures.add(sig.sign());
}
// Combine partial signatures
byte[] combinedSignature = combineSignatures(partialSignatures, threshold);
return new ThresholdSignature(combinedSignature, partialSignatures.size());
} catch (Exception e) {
throw new SignatureException("Threshold signing failed", e);
}
}
/**
* Blind signature
*/
public BlindSignature blindSign(byte[] blindedMessage, PrivateKey signerKey)
throws SignatureException {
try {
// RSA blind signature (simplified)
Signature sig = Signature.getInstance("NONEwithRSA", "BC");
sig.initSign(signerKey);
sig.update(blindedMessage);
byte[] blindSignature = sig.sign();
return new BlindSignature(blindSignature);
} catch (Exception e) {
throw new SignatureException("Blind signing failed", e);
}
}
/**
* Aggregate signature (BLS)
*/
public AggregateSignature aggregateSign(List<byte[]> messages,
List<PrivateKey> signerKeys)
throws SignatureException {
try {
List<byte[]> individualSignatures = new ArrayList<>();
for (int i = 0; i < messages.size(); i++) {
Signature sig = Signature.getInstance("SHA256withECDSA", "BC");
sig.initSign(signerKeys.get(i));
sig.update(messages.get(i));
individualSignatures.add(sig.sign());
}
// Aggregate signatures
byte[] aggregated = aggregateSignatures(individualSignatures);
return new AggregateSignature(aggregated, messages.size());
} catch (Exception e) {
throw new SignatureException("Aggregate signing failed", e);
}
}
// Placeholder methods for PQ signatures
private byte[] pqSign(byte[] message, PrivateKey pqKey) {
return message; // Placeholder
}
private boolean pqVerify(byte[] message, byte[] signature, PublicKey pqKey) {
return true; // Placeholder
}
private byte[] combineSignatures(List<byte[]> signatures, int threshold) {
// Simple concatenation for demo
int totalLength = signatures.stream().mapToInt(s -> s.length).sum();
byte[] combined = new byte[totalLength];
int offset = 0;
for (byte[] sig : signatures) {
System.arraycopy(sig, 0, combined, offset, sig.length);
offset += sig.length;
}
return combined;
}
private byte[] aggregateSignatures(List<byte[]> signatures) {
// BLS aggregation would go here
return signatures.get(0); // Placeholder
}
// Data classes
public static class HybridSignature {
private final byte[] classicalSignature;
private final byte[] pqSignature;
public HybridSignature(byte[] classicalSignature, byte[] pqSignature) {
this.classicalSignature = classicalSignature;
this.pqSignature = pqSignature;
}
// getters
}
public static class ThresholdSignature {
private final byte[] signature;
private final int numberOfSigners;
// constructor and getters
}
public static class BlindSignature {
private final byte[] signature;
// constructor and getter
}
public static class AggregateSignature {
private final byte[] signature;
private final int numberOfSignatures;
// constructor and getters
}
public static class SignatureException extends Exception {
public SignatureException(String message) { super(message); }
public SignatureException(String message, Throwable cause) {
super(message, cause);
}
}
}
6. Authenticated Encryption with Associated Data (AEAD)
package com.hybrid.crypto;
import org.springframework.stereotype.Component;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.util.Arrays;
@Component
public class AEADService {
private final SecureRandom secureRandom = new SecureRandom();
/**
* AES-GCM encryption with additional authenticated data
*/
public AEADCiphertext encryptAES_GCM(byte[] plaintext,
byte[] key,
byte[] aad,
int tagLength)
throws AEADException {
try {
byte[] iv = new byte[12];
secureRandom.nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(tagLength * 8, iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
if (aad != null) {
cipher.updateAAD(aad);
}
byte[] ciphertext = cipher.doFinal(plaintext);
return new AEADCiphertext(iv, ciphertext, aad, tagLength);
} catch (Exception e) {
throw new AEADException("AES-GCM encryption failed", e);
}
}
/**
* AES-GCM decryption with AAD verification
*/
public byte[] decryptAES_GCM(AEADCiphertext ciphertext, byte[] key)
throws AEADException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(
ciphertext.getTagLength() * 8,
ciphertext.getIv()
);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
if (ciphertext.getAad() != null) {
cipher.updateAAD(ciphertext.getAad());
}
return cipher.doFinal(ciphertext.getCiphertext());
} catch (Exception e) {
throw new AEADException("AES-GCM decryption failed", e);
}
}
/**
* ChaCha20-Poly1305 encryption
*/
public AEADCiphertext encryptChaCha20Poly1305(byte[] plaintext,
byte[] key,
byte[] aad)
throws AEADException {
try {
byte[] nonce = new byte[12];
secureRandom.nextBytes(nonce);
Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "ChaCha20");
IvParameterSpec ivSpec = new IvParameterSpec(nonce);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
if (aad != null) {
cipher.updateAAD(aad);
}
byte[] ciphertext = cipher.doFinal(plaintext);
return new AEADCiphertext(nonce, ciphertext, aad, 16);
} catch (Exception e) {
throw new AEADException("ChaCha20-Poly1305 encryption failed", e);
}
}
/**
* AES-SIV (RFC 5297) - deterministic authenticated encryption
*/
public byte[] encryptAES_SIV(byte[] plaintext, byte[] key, byte[]... associatedData)
throws AEADException {
try {
// AES-SIV is deterministic - no IV needed
Cipher cipher = Cipher.getInstance("AES/SIV/NoPadding", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// SIV mode requires key size double (e.g., 256-bit key for AES-128-SIV)
byte[] sivKey = Arrays.copyOf(key, 32);
keySpec = new SecretKeySpec(sivKey, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
for (byte[] aad : associatedData) {
cipher.updateAAD(aad);
}
return cipher.doFinal(plaintext);
} catch (Exception e) {
throw new AEADException("AES-SIV encryption failed", e);
}
}
/**
* OCB (Offset Codebook Mode) - parallelizable AEAD
*/
public AEADCiphertext encryptAES_OCB(byte[] plaintext,
byte[] key,
byte[] aad,
int tagLength)
throws AEADException {
try {
byte[] nonce = new byte[15]; // OCB uses 120-bit nonce
secureRandom.nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/OCB/NoPadding", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(nonce);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
if (aad != null) {
cipher.updateAAD(aad);
}
byte[] ciphertext = cipher.doFinal(plaintext);
return new AEADCiphertext(nonce, ciphertext, aad, tagLength);
} catch (Exception e) {
throw new AEADException("AES-OCB encryption failed", e);
}
}
/**
* CCM (Counter with CBC-MAC) mode
*/
public AEADCiphertext encryptAES_CCM(byte[] plaintext,
byte[] key,
byte[] aad,
int macLength)
throws AEADException {
try {
byte[] nonce = new byte[12];
secureRandom.nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/CCM/NoPadding", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// CCM requires specific parameter spec
CCMBlockCipher ccm = new CCMBlockCipher(new AESEngine());
ccm.init(true, new CCMParameters(key, macLength, nonce, aad));
byte[] ciphertext = new byte[ccm.getOutputSize(plaintext.length)];
int len = ccm.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
ccm.doFinal(ciphertext, len);
return new AEADCiphertext(nonce, ciphertext, aad, macLength);
} catch (Exception e) {
throw new AEADException("AES-CCM encryption failed", e);
}
}
/**
* EAX mode
*/
public AEADCiphertext encryptAES_EAX(byte[] plaintext,
byte[] key,
byte[] aad)
throws AEADException {
try {
byte[] nonce = new byte[16];
secureRandom.nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/EAX/NoPadding", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(nonce);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
if (aad != null) {
cipher.updateAAD(aad);
}
byte[] ciphertext = cipher.doFinal(plaintext);
return new AEADCiphertext(nonce, ciphertext, aad, 16);
} catch (Exception e) {
throw new AEADException("AES-EAX encryption failed", e);
}
}
/**
* Key commitment for AEAD
*/
public KeyCommitment commitToKey(byte[] key, byte[] context)
throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(context);
digest.update(key);
byte[] commitment = digest.digest();
return new KeyCommitment(commitment, context);
}
/**
* Verify key commitment
*/
public boolean verifyKeyCommitment(byte[] key, KeyCommitment commitment)
throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(commitment.getContext());
digest.update(key);
byte[] computed = digest.digest();
return MessageDigest.isEqual(computed, commitment.getCommitment());
}
// Data classes
public static class AEADCiphertext {
private final byte[] iv;
private final byte[] ciphertext;
private final byte[] aad;
private final int tagLength;
public AEADCiphertext(byte[] iv, byte[] ciphertext, byte[] aad, int tagLength) {
this.iv = iv;
this.ciphertext = ciphertext;
this.aad = aad;
this.tagLength = tagLength;
}
// getters
}
public static class KeyCommitment {
private final byte[] commitment;
private final byte[] context;
// constructor and getters
}
public static class AEADException extends Exception {
public AEADException(String message) { super(message); }
public AEADException(String message, Throwable cause) { super(message, cause); }
}
}
7. Hybrid Cryptography REST API
package com.hybrid.rest;
import com.hybrid.crypto.*;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.security.*;
import java.util.*;
import java.util.Base64;
@RestController
@RequestMapping("/api/hybrid")
public class HybridCryptoController {
private final HybridCryptographyService cryptoService;
private final KeyDerivationService keyDerivationService;
private final HybridKEMService kemService;
private final AEADService aeadService;
public HybridCryptoController(HybridCryptographyService cryptoService,
KeyDerivationService keyDerivationService,
HybridKEMService kemService,
AEADService aeadService) {
this.cryptoService = cryptoService;
this.keyDerivationService = keyDerivationService;
this.kemService = kemService;
this.aeadService = aeadService;
}
@PostMapping("/keys/generate")
public ResponseEntity<KeyPairResponse> generateKeyPair(
@RequestParam HybridCryptographyService.AsymmetricAlgorithm algorithm) {
try {
KeyPair keyPair = cryptoService.generateKeyPair(algorithm);
return ResponseEntity.ok(new KeyPairResponse(
Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()),
Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()),
algorithm
));
} catch (HybridCryptographyService.HybridException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new KeyPairResponse("Key generation failed: " + e.getMessage()));
}
}
@PostMapping("/encrypt")
public ResponseEntity<EncryptResponse> encrypt(
@RequestBody EncryptRequest request) {
try {
// Decode public key
PublicKey publicKey = decodePublicKey(
Base64.getDecoder().decode(request.getPublicKey()),
request.getAsymmetricAlgorithm()
);
// Encrypt data
HybridCryptographyService.HybridCiphertext ciphertext =
cryptoService.encrypt(
request.getPlaintext().getBytes(),
publicKey,
request.getAsymmetricAlgorithm(),
request.getSymmetricAlgorithm()
);
return ResponseEntity.ok(new EncryptResponse(
Base64.getEncoder().encodeToString(ciphertext.getEncryptedKey()),
Base64.getEncoder().encodeToString(ciphertext.getIv()),
Base64.getEncoder().encodeToString(ciphertext.getCiphertext()),
ciphertext.getAsymAlgorithm(),
ciphertext.getSymAlgorithm()
));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new EncryptResponse("Encryption failed: " + e.getMessage()));
}
}
@PostMapping("/decrypt")
public ResponseEntity<DecryptResponse> decrypt(
@RequestBody DecryptRequest request) {
try {
// Decode private key
PrivateKey privateKey = decodePrivateKey(
Base64.getDecoder().decode(request.getPrivateKey()),
request.getAsymmetricAlgorithm()
);
// Reconstruct ciphertext
HybridCryptographyService.HybridCiphertext ciphertext =
new HybridCryptographyService.HybridCiphertext(
Base64.getDecoder().decode(request.getEncryptedKey()),
Base64.getDecoder().decode(request.getIv()),
Base64.getDecoder().decode(request.getCiphertext()),
request.getAsymmetricAlgorithm(),
request.getSymmetricAlgorithm()
);
// Decrypt
byte[] plaintext = cryptoService.decrypt(ciphertext, privateKey);
return ResponseEntity.ok(new DecryptResponse(
new String(plaintext)
));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new DecryptResponse("Decryption failed: " + e.getMessage()));
}
}
@PostMapping("/encrypt/file")
public ResponseEntity<FileEncryptResponse> encryptFile(
@RequestParam("file") MultipartFile file,
@RequestParam("publicKey") String publicKeyBase64,
@RequestParam("asymAlgorithm")
HybridCryptographyService.AsymmetricAlgorithm asymAlgorithm,
@RequestParam("symAlgorithm")
HybridCryptographyService.SymmetricAlgorithm symAlgorithm) {
try {
byte[] fileBytes = file.getBytes();
PublicKey publicKey = decodePublicKey(
Base64.getDecoder().decode(publicKeyBase64),
asymAlgorithm
);
HybridCryptographyService.HybridCiphertext ciphertext =
cryptoService.encrypt(fileBytes, publicKey, asymAlgorithm, symAlgorithm);
// Create response with file metadata
FileEncryptResponse response = new FileEncryptResponse(
file.getOriginalFilename(),
file.getSize(),
Base64.getEncoder().encodeToString(ciphertext.getEncryptedKey()),
Base64.getEncoder().encodeToString(ciphertext.getIv()),
Base64.getEncoder().encodeToString(ciphertext.getCiphertext()),
asymAlgorithm,
symAlgorithm
);
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new FileEncryptResponse("File encryption failed: " + e.getMessage()));
}
}
@PostMapping("/kem/encapsulate")
public ResponseEntity<KEMEncapsulateResponse> kemEncapsulate(
@RequestParam("publicKey") String publicKeyBase64,
@RequestParam("kemAlgorithm")
HybridCryptographyService.KeyExchangeAlgorithm kemAlgorithm,
@RequestParam("symAlgorithm")
HybridCryptographyService.SymmetricAlgorithm symAlgorithm) {
try {
PublicKey publicKey = decodePublicKey(
Base64.getDecoder().decode(publicKeyBase64),
getAsymmetricAlgFromKEM(kemAlgorithm)
);
HybridKEMService.KEMCiphertext result = cryptoService.kemEncapsulate(
publicKey, kemAlgorithm, symAlgorithm
);
return ResponseEntity.ok(new KEMEncapsulateResponse(
Base64.getEncoder().encodeToString(result.getEncapsulated()),
Base64.getEncoder().encodeToString(result.getEncryptionKey()),
kemAlgorithm,
symAlgorithm
));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new KEMEncapsulateResponse("KEM encapsulation failed: " + e.getMessage()));
}
}
@PostMapping("/aead/encrypt")
public ResponseEntity<AEADEncryptResponse> aeadEncrypt(
@RequestBody AEADEncryptRequest request) {
try {
AEADService.AEADCiphertext ciphertext = aeadService.encryptAES_GCM(
request.getPlaintext().getBytes(),
Base64.getDecoder().decode(request.getKey()),
request.getAad() != null ? request.getAad().getBytes() : null,
request.getTagLength()
);
return ResponseEntity.ok(new AEADEncryptResponse(
Base64.getEncoder().encodeToString(ciphertext.getIv()),
Base64.getEncoder().encodeToString(ciphertext.getCiphertext())
));
} catch (AEADService.AEADException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new AEADEncryptResponse("AEAD encryption failed: " + e.getMessage()));
}
}
@PostMapping("/hybrid/sign")
public ResponseEntity<HybridSignResponse> hybridSign(
@RequestBody HybridSignRequest request) {
try {
// Decode keys
PrivateKey classicalKey = decodePrivateKey(
Base64.getDecoder().decode(request.getClassicalPrivateKey()),
AsymmetricAlgorithm.EC_P256
);
// This would need proper PQ key handling
PrivateKey pqKey = null; // Placeholder
HybridSignatureService.HybridSignature signature =
cryptoService.hybridSign(
request.getMessage().getBytes(),
classicalKey,
pqKey
);
return ResponseEntity.ok(new HybridSignResponse(
Base64.getEncoder().encodeToString(signature.getClassicalSignature()),
Base64.getEncoder().encodeToString(signature.getPqSignature())
));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new HybridSignResponse("Hybrid signing failed: " + e.getMessage()));
}
}
@PostMapping("/key/derive")
public ResponseEntity<KeyDeriveResponse> deriveKey(
@RequestBody KeyDeriveRequest request) {
try {
byte[] derivedKey = keyDerivationService.deriveFromPassword(
request.getPassword().toCharArray(),
Base64.getDecoder().decode(request.getSalt()),
request.getKeyLength(),
request.getIterations()
);
return ResponseEntity.ok(new KeyDeriveResponse(
Base64.getEncoder().encodeToString(derivedKey)
));
} catch (KeyDerivationService.KeyDerivationException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new KeyDeriveResponse("Key derivation failed: " + e.getMessage()));
}
}
// Helper methods
private PublicKey decodePublicKey(byte[] keyBytes,
HybridCryptographyService.AsymmetricAlgorithm algorithm)
throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm.algorithm, "BC");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
return keyFactory.generatePublic(keySpec);
}
private PrivateKey decodePrivateKey(byte[] keyBytes,
HybridCryptographyService.AsymmetricAlgorithm algorithm)
throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm.algorithm, "BC");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
return keyFactory.generatePrivate(keySpec);
}
private HybridCryptographyService.AsymmetricAlgorithm getAsymmetricAlgFromKEM(
HybridCryptographyService.KeyExchangeAlgorithm kemAlg) {
switch (kemAlg) {
case RSA_OAEP_SHA256:
case RSA_OAEP_SHA384:
case RSA_OAEP_SHA512:
return AsymmetricAlgorithm.RSA_4096;
case ECDH_SHA256:
case ECDH_SHA384:
case ECDH_SHA512:
return AsymmetricAlgorithm.EC_P256;
default:
return AsymmetricAlgorithm.RSA_4096;
}
}
// Request/Response classes
public static class KeyPairResponse {
private String publicKey;
private String privateKey;
private String algorithm;
private String error;
// constructors and getters
}
public static class EncryptRequest {
private String plaintext;
private String publicKey;
private HybridCryptographyService.AsymmetricAlgorithm asymmetricAlgorithm;
private HybridCryptographyService.SymmetricAlgorithm symmetricAlgorithm;
// getters and setters
}
public static class EncryptResponse {
private String encryptedKey;
private String iv;
private String ciphertext;
private HybridCryptographyService.AsymmetricAlgorithm asymAlgorithm;
private HybridCryptographyService.SymmetricAlgorithm symAlgorithm;
private String error;
// constructors and getters
}
public static class DecryptRequest {
private String privateKey;
private String encryptedKey;
private String iv;
private String ciphertext;
private HybridCryptographyService.AsymmetricAlgorithm asymmetricAlgorithm;
private HybridCryptographyService.SymmetricAlgorithm symmetricAlgorithm;
// getters and setters
}
public static class DecryptResponse {
private String plaintext;
private String error;
// constructors and getters
}
public static class FileEncryptResponse {
private String filename;
private long size;
private String encryptedKey;
private String iv;
private String ciphertext;
private HybridCryptographyService.AsymmetricAlgorithm asymAlgorithm;
private HybridCryptographyService.SymmetricAlgorithm symAlgorithm;
private String error;
// constructors and getters
}
public static class KEMEncapsulateResponse {
private String encapsulated;
private String encryptionKey;
private HybridCryptographyService.KeyExchangeAlgorithm kemAlgorithm;
private HybridCryptographyService.SymmetricAlgorithm symAlgorithm;
private String error;
// constructors and getters
}
public static class AEADEncryptRequest {
private String plaintext;
private String key;
private String aad;
private int tagLength;
// getters and setters
}
public static class AEADEncryptResponse {
private String iv;
private String ciphertext;
private String error;
// constructors and getters
}
public static class HybridSignRequest {
private String message;
private String classicalPrivateKey;
private String pqPrivateKey;
// getters and setters
}
public static class HybridSignResponse {
private String classicalSignature;
private String pqSignature;
private String error;
// constructors and getters
}
public static class KeyDeriveRequest {
private String password;
private String salt;
private int keyLength;
private int iterations;
// getters and setters
}
public static class KeyDeriveResponse {
private String derivedKey;
private String error;
// constructors and getters
}
}
8. Testing and Validation
package com.hybrid.test;
import com.hybrid.crypto.*;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.security.*;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class HybridCryptographyTests {
@Autowired
private HybridCryptographyService cryptoService;
@Autowired
private KeyDerivationService keyDerivationService;
@Autowired
private HybridKEMService kemService;
@Autowired
private AEADService aeadService;
private KeyPair rsaKeyPair;
private KeyPair ecKeyPair;
@BeforeEach
void setUp() throws Exception {
rsaKeyPair = cryptoService.generateKeyPair(
HybridCryptographyService.AsymmetricAlgorithm.RSA_4096
);
ecKeyPair = cryptoService.generateKeyPair(
HybridCryptographyService.AsymmetricAlgorithm.EC_P256
);
}
@Test
void testRSAHybridEncryption() throws Exception {
byte[] plaintext = "Hello, Hybrid World!".getBytes();
// Encrypt
HybridCryptographyService.HybridCiphertext ciphertext =
cryptoService.encrypt(
plaintext,
rsaKeyPair.getPublic(),
HybridCryptographyService.AsymmetricAlgorithm.RSA_4096,
HybridCryptographyService.SymmetricAlgorithm.AES_GCM_256
);
// Decrypt
byte[] decrypted = cryptoService.decrypt(ciphertext, rsaKeyPair.getPrivate());
assertArrayEquals(plaintext, decrypted);
}
@Test
void testMultiRecipientEncryption() throws Exception {
// Generate multiple recipients
KeyPair recipient1 = cryptoService.generateKeyPair(
HybridCryptographyService.AsymmetricAlgorithm.RSA_4096
);
KeyPair recipient2 = cryptoService.generateKeyPair(
HybridCryptographyService.AsymmetricAlgorithm.RSA_4096
);
List<PublicKey> recipients = Arrays.asList(
recipient1.getPublic(),
recipient2.getPublic()
);
byte[] plaintext = "Multi-recipient secret".getBytes();
// Multi-recipient encryption
MultiRecipientCiphertext ciphertext = cryptoService.encryptMultiRecipient(
plaintext,
recipients,
HybridCryptographyService.AsymmetricAlgorithm.RSA_4096,
HybridCryptographyService.SymmetricAlgorithm.AES_GCM_256
);
// Each recipient should be able to decrypt
for (int i = 0; i < recipients.size(); i++) {
HybridCryptographyService.HybridCiphertext singleCiphertext =
new HybridCryptographyService.HybridCiphertext(
ciphertext.getEncryptedKeys().get(i),
ciphertext.getIv(),
ciphertext.getCiphertext(),
ciphertext.getAsymAlgorithm(),
ciphertext.getSymAlgorithm()
);
byte[] decrypted = cryptoService.decrypt(
singleCiphertext,
i == 0 ? recipient1.getPrivate() : recipient2.getPrivate()
);
assertArrayEquals(plaintext, decrypted);
}
}
@Test
void testAES_GCM_AEAD() throws Exception {
byte[] key = new byte[32];
new SecureRandom().nextBytes(key);
byte[] plaintext = "AEAD test message".getBytes();
byte[] aad = "authenticated data".getBytes();
// Encrypt
AEADService.AEADCiphertext ciphertext = aeadService.encryptAES_GCM(
plaintext, key, aad, 16
);
// Decrypt
byte[] decrypted = aeadService.decryptAES_GCM(ciphertext, key);
assertArrayEquals(plaintext, decrypted);
// Tamper with ciphertext - should fail
byte[] tamperedCiphertext = ciphertext.getCiphertext().clone();
tamperedCiphertext[10] ^= 0x01;
AEADService.AEADCiphertext tampered = new AEADService.AEADCiphertext(
ciphertext.getIv(),
tamperedCiphertext,
ciphertext.getAad(),
ciphertext.getTagLength()
);
assertThrows(AEADService.AEADException.class, () -> {
aeadService.decryptAES_GCM(tampered, key);
});
}
@Test
void testHKDF() throws Exception {
byte[] inputKeyingMaterial = new byte[32];
new SecureRandom().nextBytes(inputKeyingMaterial);
byte[] salt = "test-salt".getBytes();
byte[] info = "test-info".getBytes();
KeyDerivationService.HKDFResult result = keyDerivationService.hkdf(
inputKeyingMaterial, salt, info, 64
);
assertNotNull(result.getPseudoRandomKey());
assertEquals(32, result.getPseudoRandomKey().length);
assertEquals(64, result.getOutputKeyingMaterial().length);
// Same inputs should produce same outputs
KeyDerivationService.HKDFResult result2 = keyDerivationService.hkdf(
inputKeyingMaterial, salt, info, 64
);
assertArrayEquals(result.getOutputKeyingMaterial(),
result2.getOutputKeyingMaterial());
}
@Test
void testPasswordBasedKeyDerivation() throws Exception {
char[] password = "SecurePassword123!".toCharArray();
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
byte[] derivedKey = keyDerivationService.deriveFromPassword(
password, salt, 32, 10000
);
assertEquals(32, derivedKey.length);
// Same password and salt should produce same key
byte[] derivedKey2 = keyDerivationService.deriveFromPassword(
password, salt, 32, 10000
);
assertArrayEquals(derivedKey, derivedKey2);
}
@Test
void testRSAKEM() throws Exception {
int keyLength = 256; // bits
// Encapsulate
HybridKEMService.RSAKEMResult result = kemService.rsaKEMEncapsulate(
rsaKeyPair.getPublic(), keyLength
);
// Decapsulate
byte[] derivedKey = kemService.rsaKEMDecapsulate(
result.getEncapsulated(),
rsaKeyPair.getPrivate(),
keyLength
);
assertArrayEquals(result.getDerivedKey(), derivedKey);
}
@Test
void testECDHKEM() throws Exception {
int keyLength = 256;
// Encapsulate
HybridKEMService.ECDHKEMResult result = kemService.ecdhKEMEncapsulate(
ecKeyPair.getPublic(),
"secp256r1",
keyLength
);
// Decapsulate
byte[] derivedKey = kemService.ecdhKEMDecapsulate(
result.getEphemeralPublicKey(),
ecKeyPair.getPrivate(),
"secp256r1",
keyLength
);
assertArrayEquals(result.getDerivedKey(), derivedKey);
}
@Test
void testKeySplittingAndCombining() throws Exception {
byte[] secret = "VerySecretKeyMaterial".getBytes();
// Split into 5 shares, need 3 to reconstruct
List<byte[]> shares = keyDerivationService.splitKey(secret, 5, 3);
assertEquals(5, shares.size());
// Try to reconstruct with 3 shares
List<byte[]> threeShares = shares.subList(0, 3);
byte[] recovered = keyDerivationService.combineKeys(threeShares, 3);
assertArrayEquals(secret, recovered);
// Try with 2 shares (should fail)
List<byte[]> twoShares = shares.subList(0, 2);
byte[] wrongRecovery = keyDerivationService.combineKeys(twoShares, 3);
assertFalse(Arrays.equals(secret, wrongRecovery));
}
@Test
void testSignThenEncrypt() throws Exception {
// Generate signing and encryption keys
KeyPair signingKeys = cryptoService.generateKeyPair(
HybridCryptographyService.AsymmetricAlgorithm.EC_P256
);
byte[] message = "Message to sign and encrypt".getBytes();
// Sign then encrypt
SignedEncryptedData encrypted = cryptoService.signThenEncrypt(
message,
signingKeys.getPrivate(),
rsaKeyPair.getPublic(),
SignatureAlgorithm.SHA256_WITH_ECDSA,
HybridCryptographyService.SymmetricAlgorithm.AES_GCM_256
);
// Decrypt then verify
byte[] decrypted = cryptoService.decryptThenVerify(
encrypted,
rsaKeyPair.getPrivate(),
signingKeys.getPublic()
);
assertArrayEquals(message, decrypted);
}
@Test
void testChaCha20Poly1305() throws Exception {
byte[] key = new byte[32];
new SecureRandom().nextBytes(key);
byte[] plaintext = "ChaCha20-Poly1305 test".getBytes();
byte[] aad = "additional data".getBytes();
AEADService.AEADCiphertext ciphertext = aeadService.encryptChaCha20Poly1305(
plaintext, key, aad
);
byte[] decrypted = aeadService.decryptAES_GCM(ciphertext, key);
assertArrayEquals(plaintext, decrypted);
}
@Test
void testKeyCommitment() throws Exception {
byte[] key = new byte[32];
new SecureRandom().nextBytes(key);
byte[] context = "test-context".getBytes();
AEADService.KeyCommitment commitment = aeadService.commitToKey(key, context);
boolean verified = aeadService.verifyKeyCommitment(key, commitment);
assertTrue(verified);
// Tamper with key
byte[] tamperedKey = key.clone();
tamperedKey[0] ^= 0x01;
boolean tamperedVerification = aeadService.verifyKeyCommitment(
tamperedKey, commitment
);
assertFalse(tamperedVerification);
}
@Test
void testPerformance() throws Exception {
int iterations = 100;
byte[] message = "Performance test message".getBytes();
// RSA-4096 encryption/decryption
long start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
HybridCryptographyService.HybridCiphertext ciphertext =
cryptoService.encrypt(
message,
rsaKeyPair.getPublic(),
HybridCryptographyService.AsymmetricAlgorithm.RSA_4096,
HybridCryptographyService.SymmetricAlgorithm.AES_GCM_256
);
cryptoService.decrypt(ciphertext, rsaKeyPair.getPrivate());
}
long rsaTime = System.nanoTime() - start;
// ECC encryption/decryption (for key exchange)
start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
HybridKEMService.ECDHKEMResult result = kemService.ecdhKEMEncapsulate(
ecKeyPair.getPublic(), "secp256r1", 256
);
kemService.ecdhKEMDecapsulate(
result.getEphemeralPublicKey(),
ecKeyPair.getPrivate(),
"secp256r1",
256
);
}
long eccTime = System.nanoTime() - start;
System.out.printf("RSA hybrid: %d ns/op%n", rsaTime / iterations);
System.out.printf("ECC hybrid: %d ns/op%n", eccTime / iterations);
}
}
9. Spring Boot Configuration
package com.hybrid.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.security.Security;
import java.util.concurrent.*;
@Configuration
@EnableScheduling
@ConfigurationProperties(prefix = "hybrid.crypto")
public class HybridCryptoConfig {
static {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
private String defaultAsymmetricAlgorithm = "RSA_4096";
private String defaultSymmetricAlgorithm = "AES_GCM_256";
private int keyCacheSize = 100;
private int threadPoolSize = Runtime.getRuntime().availableProcessors();
private long operationTimeoutMs = 30000;
@Bean
public HybridCryptographyService hybridCryptographyService() {
return new HybridCryptographyService();
}
@Bean
public KeyDerivationService keyDerivationService() {
return new KeyDerivationService();
}
@Bean
public HybridKEMService hybridKEMService() {
return new HybridKEMService();
}
@Bean
public AEADService aeadService() {
return new AEADService();
}
@Bean
public ExecutorService cryptoExecutor() {
return Executors.newFixedThreadPool(threadPoolSize);
}
@Bean
public CacheManager keyCacheManager() {
return new CacheManager(keyCacheSize);
}
// Scheduled task for key rotation
@Scheduled(cron = "0 0 2 * * *") // Daily at 2 AM
public void rotateKeys() {
// Implement key rotation logic
}
// Cache manager for keys
public static class CacheManager {
private final ConcurrentHashMap<String, CacheEntry> cache;
private final int maxSize;
public CacheManager(int maxSize) {
this.cache = new ConcurrentHashMap<>();
this.maxSize = maxSize;
}
public void put(String key, Object value) {
if (cache.size() >= maxSize) {
evictOldest();
}
cache.put(key, new CacheEntry(value, System.currentTimeMillis()));
}
public Object get(String key) {
CacheEntry entry = cache.get(key);
if (entry != null) {
entry.setLastAccessed(System.currentTimeMillis());
return entry.getValue();
}
return null;
}
private void evictOldest() {
cache.entrySet().stream()
.min(Comparator.comparingLong(e -> e.getValue().getLastAccessed()))
.ifPresent(entry -> cache.remove(entry.getKey()));
}
private static class CacheEntry {
private final Object value;
private long lastAccessed;
CacheEntry(Object value, long lastAccessed) {
this.value = value;
this.lastAccessed = lastAccessed;
}
// getters and setters
}
}
}
10. Security Audit and Monitoring
package com.hybrid.audit;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class SecurityAuditService {
private final Map<String, AuditEntry> auditLog = new ConcurrentHashMap<>();
private final List<SecurityAlert> alerts = new ArrayList<>();
public enum AuditEventType {
KEY_GENERATION,
ENCRYPTION,
DECRYPTION,
SIGNATURE_CREATION,
SIGNATURE_VERIFICATION,
KEY_ROTATION,
KEY_REVOCATION,
SECURITY_VIOLATION
}
public enum SecurityLevel {
INFO, WARNING, ERROR, CRITICAL
}
/**
* Log cryptographic operation
*/
public void logOperation(AuditEventType type,
String keyId,
SecurityLevel level,
String details) {
AuditEntry entry = new AuditEntry(
UUID.randomUUID().toString(),
type,
keyId,
level,
details,
Instant.now(),
Thread.currentThread().getName()
);
auditLog.put(entry.getId(), entry);
if (level == SecurityLevel.CRITICAL || level == SecurityLevel.ERROR) {
createAlert(entry);
}
// Log to standard logger as well
logToStandardLogger(entry);
}
/**
* Monitor for cryptographic weaknesses
*/
public void checkCryptographicStrength(byte[] key, String algorithm) {
// Check key strength
if (key.length < 32 && algorithm.contains("AES")) {
logOperation(
AuditEventType.SECURITY_VIOLATION,
null,
SecurityLevel.WARNING,
"Weak key length: " + (key.length * 8) + " bits"
);
}
// Check for weak algorithms
if (algorithm.contains("DES") || algorithm.contains("MD5")) {
logOperation(
AuditEventType.SECURITY_VIOLATION,
null,
SecurityLevel.ERROR,
"Weak algorithm detected: " + algorithm
);
}
}
/**
* Generate audit report
*/
public AuditReport generateReport(Instant from, Instant to) {
AuditReport report = new AuditReport();
auditLog.values().stream()
.filter(entry -> !entry.getTimestamp().isBefore(from) &&
!entry.getTimestamp().isAfter(to))
.forEach(entry -> {
report.addEntry(entry);
incrementCounts(report, entry);
});
report.setAlerts(alerts.stream()
.filter(alert -> !alert.getTimestamp().isBefore(from) &&
!alert.getTimestamp().isAfter(to))
.toList());
return report;
}
/**
* Detect anomalies in cryptographic operations
*/
public void detectAnomalies() {
// Check for unusual operation patterns
Map<String, Long> operationCounts = new HashMap<>();
Instant oneHourAgo = Instant.now().minusSeconds(3600);
auditLog.values().stream()
.filter(entry -> entry.getTimestamp().isAfter(oneHourAgo))
.forEach(entry -> {
operationCounts.merge(
entry.getType().name(),
1L,
Long::sum
);
});
// Detect if too many decryption failures
long failures = operationCounts.getOrDefault(
AuditEventType.DECRYPTION.name(), 0L
);
if (failures > 100) {
createAlert(new SecurityAlert(
"High decryption failure rate",
SecurityLevel.WARNING,
"Possible attack or configuration issue"
));
}
}
/**
* Verify operation integrity
*/
public boolean verifyOperationIntegrity(String operationId) {
AuditEntry entry = auditLog.get(operationId);
if (entry == null) {
return false;
}
// Calculate and verify hash of log entry
byte[] computedHash = calculateEntryHash(entry);
return Arrays.equals(computedHash, entry.getHash());
}
private void createAlert(AuditEntry entry) {
SecurityAlert alert = new SecurityAlert(
entry.getId(),
entry.getType().name() + ": " + entry.getDetails(),
entry.getLevel(),
entry.getTimestamp()
);
alerts.add(alert);
// Send to monitoring system
notifyMonitoringSystem(alert);
}
private void createAlert(SecurityAlert alert) {
alerts.add(alert);
notifyMonitoringSystem(alert);
}
private void logToStandardLogger(AuditEntry entry) {
// Use SLF4J or similar
System.out.printf("[%s] %s: %s - %s%n",
entry.getLevel(),
entry.getType(),
entry.getKeyId(),
entry.getDetails()
);
}
private byte[] calculateEntryHash(AuditEntry entry) {
// Calculate hash for integrity verification
return new byte[32]; // Placeholder
}
private void incrementCounts(AuditReport report, AuditEntry entry) {
switch (entry.getType()) {
case ENCRYPTION:
report.incrementEncryptionCount();
break;
case DECRYPTION:
report.incrementDecryptionCount();
break;
case KEY_GENERATION:
report.incrementKeyGenerationCount();
break;
case SIGNATURE_CREATION:
report.incrementSignatureCount();
break;
}
}
private void notifyMonitoringSystem(SecurityAlert alert) {
// Integrate with monitoring system (e.g., CloudWatch, Prometheus)
}
// Data classes
public static class AuditEntry {
private final String id;
private final AuditEventType type;
private final String keyId;
private final SecurityLevel level;
private final String details;
private final Instant timestamp;
private final String thread;
private byte[] hash;
public AuditEntry(String id, AuditEventType type, String keyId,
SecurityLevel level, String details,
Instant timestamp, String thread) {
this.id = id;
this.type = type;
this.keyId = keyId;
this.level = level;
this.details = details;
this.timestamp = timestamp;
this.thread = thread;
calculateHash();
}
private void calculateHash() {
// Calculate integrity hash
}
// getters
}
public static class SecurityAlert {
private final String id;
private final String message;
private final SecurityLevel level;
private final Instant timestamp;
private boolean acknowledged;
public SecurityAlert(String id, String message, SecurityLevel level, Instant timestamp) {
this.id = id;
this.message = message;
this.level = level;
this.timestamp = timestamp;
}
public SecurityAlert(String message, SecurityLevel level, String details) {
this(UUID.randomUUID().toString(), message + ": " + details, level, Instant.now());
}
// getters and setters
}
public static class AuditReport {
private final List<AuditEntry> entries = new ArrayList<>();
private final List<SecurityAlert> alerts = new ArrayList<>();
private int encryptionCount;
private int decryptionCount;
private int keyGenerationCount;
private int signatureCount;
private int warningCount;
private int errorCount;
private int criticalCount;
public void addEntry(AuditEntry entry) {
entries.add(entry);
switch (entry.getLevel()) {
case WARNING:
warningCount++;
break;
case ERROR:
errorCount++;
break;
case CRITICAL:
criticalCount++;
break;
}
}
// increment methods and getters
}
}
Best Practices
1. Key Management
// Always use secure key storage
public class SecureKeyStore {
public void storeKey(String keyId, byte[] keyMaterial) {
// Encrypt key material before storage
byte[] encrypted = encryptWithMasterKey(keyMaterial);
// Store in HSM or secure vault
vault.store(keyId, encrypted);
// Zeroize memory
Arrays.fill(keyMaterial, (byte) 0);
}
}
2. Constant-Time Operations
// Implement constant-time comparison for security
public boolean constantTimeEquals(byte[] a, byte[] b) {
if (a.length != b.length) {
return false;
}
int result = 0;
for (int i = 0; i < a.length; i++) {
result |= a[i] ^ b[i];
}
return result == 0;
}
3. Secure Memory Management
// Use SecureMemory for sensitive data
public class SecureMemory implements AutoCloseable {
private final byte[] data;
private final Cleaner cleaner;
public SecureMemory(int size) {
this.data = new byte[size];
this.cleaner = Cleaner.create(this, new MemoryCleaner(data));
}
@Override
public void close() {
cleaner.clean();
}
private static class MemoryCleaner implements Runnable {
private final byte[] data;
MemoryCleaner(byte[] data) {
this.data = data;
}
@Override
public void run() {
Arrays.fill(data, (byte) 0);
}
}
}
4. Algorithm Agility
// Support multiple algorithms for future-proofing
public class AlgorithmAgility {
public HybridCiphertext encryptWithBestAvailable(byte[] plaintext) {
// Try PQ first if available
if (isPostQuantumAvailable()) {
return pqEncrypt(plaintext);
}
// Fall back to classical
return classicalEncrypt(plaintext);
}
}
5. Rate Limiting
// Prevent brute force attacks
public class RateLimiter {
private final Map<String, AtomicInteger> attempts = new ConcurrentHashMap<>();
public boolean allowOperation(String keyId) {
return attempts.compute(keyId, (k, v) -> {
if (v == null) return new AtomicInteger(1);
if (v.get() > 100) return null; // Block after 100 attempts
v.incrementAndGet();
return v;
}) != null;
}
}
Conclusion
This comprehensive hybrid cryptography implementation provides:
- Multiple asymmetric algorithms: RSA (2048/4096), ECC (P-256/P-384/P-521), Ed25519
- Multiple symmetric algorithms: AES-GCM, ChaCha20-Poly1305, AES-SIV
- Key encapsulation mechanisms: RSA-KEM, ECDH-KEM, hybrid PQ-KEM
- Authenticated encryption: AEAD with AAD support
- Key derivation: PBKDF2, HKDF, Argon2
- Digital signatures: RSA-PSS, ECDSA, EdDSA, hybrid signatures
- Multi-recipient encryption: One message to many recipients
- Signcryption: Combined sign and encrypt
- Threshold cryptography: Multi-party signing
- Comprehensive security auditing: Logging, monitoring, anomaly detection
Key benefits:
- Post-quantum readiness through hybrid KEM
- High performance with optimized symmetric encryption
- Strong security with multiple layers of protection
- Flexibility to choose algorithms based on requirements
- Compliance with NIST standards and recommendations
This setup provides enterprise-grade hybrid cryptography suitable for protecting sensitive data in transit and at rest, with future-proofing against quantum computing advances.
Advanced Java Programming Concepts and Projects (Related to Java Programming)
Number Guessing Game in Java:
This project teaches how to build a simple number guessing game using Java. It combines random number generation, loops, and conditional statements to create an interactive program where users guess a number until they find the correct answer.
Read more: https://macronepal.com/blog/number-guessing-game-in-java-a-complete-guide/
HashMap Basics in Java:
HashMap is a collection class used to store data in key-value pairs. It allows fast retrieval of values using keys and is widely used when working with structured data that requires quick searching and updating.
Read more: https://macronepal.com/blog/hashmap-basics-in-java-a-complete-guide/
Date and Time in Java:
This topic explains how to work with dates and times in Java using built-in classes. It helps developers manage time-related data such as current date, formatting time, and calculating time differences.
Read more: https://macronepal.com/blog/date-and-time-in-java-a-complete-guide/
StringBuilder in Java:
StringBuilder is used to create and modify strings efficiently. Unlike regular strings, it allows changes without creating new objects, making programs faster when handling large or frequently changing text.
Read more: https://macronepal.com/blog/stringbuilder-in-java-a-complete-guide/
Packages in Java:
Packages help organize Java classes into groups, making programs easier to manage and maintain. They also help prevent naming conflicts and improve code structure in large applications.
Read more: https://macronepal.com/blog/packages-in-java-a-complete-guide/
Interfaces in Java:
Interfaces define a set of methods that classes must implement. They help achieve abstraction and support multiple inheritance in Java, making programs more flexible and organized.
Read more: https://macronepal.com/blog/interfaces-in-java-a-complete-guide/
Abstract Classes in Java:
Abstract classes are classes that cannot be instantiated directly and may contain both abstract and non-abstract methods. They are used as base classes to define common features for other classes.
Read more: https://macronepal.com/blog/abstract-classes-in-java-a-complete-guide/
Method Overriding in Java:
Method overriding occurs when a subclass provides its own version of a method already defined in its parent class. It supports runtime polymorphism and allows customized behavior in child classes.
Read more: https://macronepal.com/blog/method-overriding-in-java-a-complete-guide/
The This Keyword in Java:
The this keyword refers to the current object in a class. It is used to access instance variables, call constructors, and differentiate between class variables and parameters.
Read more: https://macronepal.com/blog/the-this-keyword-in-java-a-complete-guide/
Encapsulation in Java:
Encapsulation is an object-oriented concept that involves bundling data and methods into a single unit and restricting direct access to some components. It improves data security and program organization.
Read more: https://macronepal.com/blog/encapsulation-in-java-a-complete-guide/