PQC Key Lifecycle Management
The PQC component provides comprehensive key lifecycle management capabilities for Post-Quantum Cryptography keys. This includes key generation, storage, rotation, expiration, revocation, and format conversion.
Key lifecycle management is critical for maintaining security in production environments, especially with stateful hash-based signature algorithms (XMSS, XMSSMT, LMS, HSS) that have limited signature capacity.
Key Lifecycle Manager Interface
The KeyLifecycleManager interface provides the following operations:
-
Key Generation - Generate new key pairs with algorithm-specific parameters
-
Key Storage - Store and retrieve keys with metadata
-
Key Rotation - Rotate keys and deprecate old ones
-
Key Expiration - Mark keys as expired based on age or policy
-
Key Revocation - Revoke compromised keys with reason tracking
-
Key Export/Import - Convert keys between formats (PEM, DER, PKCS8, X.509)
-
Key Listing - List all keys with their metadata
-
Rotation Detection - Check if keys need rotation based on age or usage
Available Implementations
The component provides four implementations of KeyLifecycleManager:
FileBasedKeyLifecycleManager
A production-ready implementation that persists keys and metadata to disk.
Features:
-
Persistent storage - keys survive application restarts
-
File-based storage with
.keyand.metadatafiles -
In-memory caching for performance
-
Automatic loading of existing keys on startup
-
Secure file permissions
Use Cases:
-
Production environments
-
Single-instance deployments
-
When persistence is required
-
Audit and compliance requirements
Example:
KeyLifecycleManager keyManager = new FileBasedKeyLifecycleManager("/secure/keys");
// Generate a new Dilithium key
KeyPair keyPair = keyManager.generateKeyPair("DILITHIUM", "app-signing-key",
DilithiumParameterSpec.dilithium2);
// Use the key
KeyMetadata metadata = keyManager.getKeyMetadata("app-signing-key");
logger.info("Key created: {}", metadata); InMemoryKeyLifecycleManager
A lightweight implementation that stores keys in memory only.
Features:
-
Non-persistent storage - keys lost on restart
-
ConcurrentHashMap-based storage
-
No I/O overhead
-
Thread-safe operations
-
Additional utility methods:
clear()andsize()
Use Cases:
-
Testing and development
-
Ephemeral workloads
-
Short-lived applications
-
Unit and integration tests
Example:
InMemoryKeyLifecycleManager keyManager = new InMemoryKeyLifecycleManager();
// Generate a test key
KeyPair keyPair = keyManager.generateKeyPair("FALCON", "test-key");
// Clean up after testing
keyManager.clear(); HashicorpVaultKeyLifecycleManager
An enterprise-grade implementation that integrates with HashiCorp Vault for centralized secret management.
Security Implementation:
This implementation uses industry-standard cryptographic key formats and separation of concerns:
-
Private keys: Stored in PKCS#8 format (RFC 5208) - the standard for private key encoding
-
Public keys: Stored in X.509/SubjectPublicKeyInfo format (RFC 5280) - the standard for public key encoding
-
Separate storage: Private keys, public keys, and metadata are stored in distinct Vault paths
-
Fine-grained ACLs: Enables different access policies for private keys (restricted) vs public keys (read-only)
Security Note: This implementation stores PQC keys in Vault’s KV secrets engine. While this approach uses industry-standard formats and enables fine-grained access control, organizations with stringent security requirements should consider:
-
Using Hardware Security Modules (HSMs) via PKCS#11 for keys that must never be exportable
-
Implementing additional encryption layers using Vault’s Transit engine to encrypt key material before storage
-
Applying strict Vault policies limiting private key access to specific services or key IDs only
-
Regular key rotation and audit log monitoring
-
For production use, review and customize the Vault ACL policies shown in the setup section
Features:
-
Centralized secret management via HashiCorp Vault
-
Industry-standard key formats (PKCS#8 for private keys, X.509 for public keys)
-
Separate storage for private and public keys (enables different ACLs)
-
Automatic audit logging of all key operations
-
Fine-grained access control with Vault policies
-
Encryption at rest
-
High availability support (Vault HA clusters)
-
In-memory caching for performance
-
Uses Spring Vault (spring-vault-core) consistent with camel-hashicorp-vault
-
HashiCorp Cloud Platform (HCP) Vault support
Dependencies:
To use HashicorpVaultKeyLifecycleManager, add the following optional dependency:
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>${spring-vault-core-version}</version>
</dependency> Example with VaultTemplate:
// Using existing VaultTemplate (recommended when using camel-hashicorp-vault)
@BindToRegistry("vaultTemplate")
public VaultTemplate createVaultTemplate() {
VaultEndpoint vaultEndpoint = new VaultEndpoint();
vaultEndpoint.setHost("localhost");
vaultEndpoint.setPort(8200);
vaultEndpoint.setScheme("https");
return new VaultTemplate(vaultEndpoint, new TokenAuthentication("s.token"));
}
@BindToRegistry("keyLifecycleManager")
public HashicorpVaultKeyLifecycleManager createKeyManager() {
return new HashicorpVaultKeyLifecycleManager(
vaultTemplate, // Reuse existing VaultTemplate
"secret", // Secrets engine name
"pqc/keys" // Key prefix in Vault
);
}
// Generate a Dilithium key stored in Vault
KeyPair keyPair = keyManager.generateKeyPair("DILITHIUM", "app-signing-key",
DilithiumParameterSpec.dilithium2);
// Key is stored in Vault at: secret/data/pqc/keys/app-signing-key Example with Direct Configuration:
// Direct configuration with connection parameters
HashicorpVaultKeyLifecycleManager keyManager =
new HashicorpVaultKeyLifecycleManager(
"vault.example.com", // host
8200, // port
"https", // scheme
"s.your-token", // Vault token
"secret", // secrets engine (optional, defaults to "secret")
"pqc/keys" // key prefix (optional, defaults to "pqc/keys")
); Example with HashiCorp Cloud Platform (HCP) Vault:
// Configuration for HCP Vault with namespace
HashicorpVaultKeyLifecycleManager keyManager =
new HashicorpVaultKeyLifecycleManager(
"your-cluster.vault.hashicorp.cloud", // HCP Vault host
8200, // port
"https", // scheme
"s.your-hcp-token", // HCP Vault token
"secret", // secrets engine
"pqc/keys", // key prefix
true, // cloud=true for HCP Vault
"admin" // namespace (required for HCP)
);
// Key is stored in HCP Vault at: admin/secret/data/pqc/keys/hcp-key YAML Configuration:
camel:
beans:
vaultEndpoint:
type: org.springframework.vault.client.VaultEndpoint
properties:
host: "vault.example.com"
port: 8200
scheme: "https"
tokenAuthentication:
type: org.springframework.vault.authentication.TokenAuthentication
constructorArgs:
- "${VAULT_TOKEN}"
vaultTemplate:
type: org.springframework.vault.core.VaultTemplate
constructorArgs:
- "#bean:vaultEndpoint"
- "#bean:tokenAuthentication"
keyLifecycleManager:
type: org.apache.camel.component.pqc.lifecycle.HashicorpVaultKeyLifecycleManager
constructorArgs:
- "#bean:vaultTemplate"
- "secret"
- "pqc/keys"
- false # cloud
- null # namespace Vault Storage Structure:
Keys are stored in Vault’s KV v2 secrets engine with separate paths for private keys, public keys, and metadata. This separation enables fine-grained access control where applications can access public keys without having access to private keys.
secret/ # Secrets engine
├── data/
│ └── pqc/
│ └── keys/
│ ├── app-signing-key/
│ │ ├── private # Private key path (STRICT ACL)
│ │ │ ├── key # Base64-encoded PKCS#8 private key
│ │ │ ├── format # "PKCS8"
│ │ │ └── algorithm # "DILITHIUM"
│ │ ├── public # Public key path (READ-ONLY ACL)
│ │ │ ├── key # Base64-encoded X.509 public key
│ │ │ ├── format # "X509"
│ │ │ └── algorithm # "DILITHIUM"
│ │ └── metadata # Metadata path
│ │ ├── metadata # Serialized KeyMetadata
│ │ ├── keyId # "app-signing-key"
│ │ └── algorithm # "DILITHIUM"
│ └── app-signing-key-v2/
│ ├── private/
│ ├── public/
│ └── metadata/
└── metadata/
└── pqc/
└── keys/
├── app-signing-key/ # Vault metadata entry
└── app-signing-key-v2/ Vault Setup and Security Policies:
To use HashicorpVaultKeyLifecycleManager, configure Vault with appropriate policies. The implementation stores private keys, public keys, and metadata separately to enable fine-grained access control.
Basic Policy (Full Access - Development/Testing):
# Enable KV v2 secrets engine (usually enabled by default)
vault secrets enable -path=secret kv-v2
# Create basic policy with full access to all key paths
cat > pqc-policy-full.hcl <<EOF
# Full access to private keys
path "secret/data/pqc/keys/*/private" {
capabilities = ["create", "read", "update", "delete"]
}
# Full access to public keys
path "secret/data/pqc/keys/*/public" {
capabilities = ["create", "read", "update", "delete"]
}
# Full access to metadata
path "secret/data/pqc/keys/*/metadata" {
capabilities = ["create", "read", "update", "delete"]
}
# List access to key paths
path "secret/metadata/pqc/keys/*" {
capabilities = ["list", "read", "delete"]
}
EOF
# Apply policy
vault policy write pqc-keys-full pqc-policy-full.hcl
# Create token with policy
vault token create -policy=pqc-keys-full Production Policies (Fine-Grained Access Control):
For production, create separate policies for different access levels. The following example shows four policies with increasing restriction:
# POLICY 1: Admin Policy (Key Management Service)
# Full access to generate, rotate, and manage keys
cat > pqc-policy-admin.hcl <<EOF
path "secret/data/pqc/keys/*/private" {
capabilities = ["create", "read", "update", "delete"]
}
path "secret/data/pqc/keys/*/public" {
capabilities = ["create", "read", "update", "delete"]
}
path "secret/data/pqc/keys/*/metadata" {
capabilities = ["create", "read", "update", "delete"]
}
path "secret/metadata/pqc/keys/*" {
capabilities = ["list", "read", "delete"]
}
EOF
vault policy write pqc-admin pqc-policy-admin.hcl
# POLICY 2: Signing Service (Read Private Keys)
cat > pqc-policy-signing.hcl <<EOF
path "secret/data/pqc/keys/*/private" {
capabilities = ["read"]
}
path "secret/data/pqc/keys/*/public" {
capabilities = ["read"]
}
path "secret/data/pqc/keys/*/metadata" {
capabilities = ["read"]
}
path "secret/metadata/pqc/keys/*" {
capabilities = ["list", "read"]
}
EOF
vault policy write pqc-signing pqc-policy-signing.hcl
# POLICY 3: Application Policy (Public Keys Only - Verification)
cat > pqc-policy-app.hcl <<EOF
path "secret/data/pqc/keys/*/public" {
capabilities = ["read"]
}
path "secret/data/pqc/keys/*/metadata" {
capabilities = ["read"]
}
path "secret/metadata/pqc/keys/*" {
capabilities = ["list", "read"]
}
EOF
vault policy write pqc-app pqc-policy-app.hcl
# POLICY 4: Specific Key Access (Production Best Practice)
cat > pqc-policy-specific.hcl <<EOF
path "secret/data/pqc/keys/app-signing-key/private" {
capabilities = ["read"]
}
path "secret/data/pqc/keys/app-signing-key/public" {
capabilities = ["read"]
}
path "secret/data/pqc/keys/app-signing-key/metadata" {
capabilities = ["read"]
}
EOF
vault policy write pqc-app-signing-key pqc-policy-specific.hcl
# Create tokens with different policies
vault token create -policy=pqc-admin # For key management service
vault token create -policy=pqc-signing # For signing service
vault token create -policy=pqc-app # For applications (verification only)
vault token create -policy=pqc-app-signing-key # For specific key access Integration with camel-hashicorp-vault:
HashicorpVaultKeyLifecycleManager can share the same VaultTemplate with the camel-hashicorp-vault component:
@BindToRegistry("keyLifecycleManager")
public HashicorpVaultKeyLifecycleManager createKeyManager() {
VaultTemplate vaultTemplate = context.getRegistry()
.lookupByNameAndType("vaultTemplate", VaultTemplate.class);
return new HashicorpVaultKeyLifecycleManager(
vaultTemplate,
"secret",
"pqc/keys"
);
} AwsSecretsManagerKeyLifecycleManager
An enterprise-grade implementation that integrates with AWS Secrets Manager for centralized secret management.
Security Implementation:
This implementation uses industry-standard cryptographic key formats and separation of concerns:
-
Private keys: Stored in PKCS#8 format (RFC 5208) - the standard for private key encoding
-
Public keys: Stored in X.509/SubjectPublicKeyInfo format (RFC 5280) - the standard for public key encoding
-
Separate storage: Private keys, public keys, and metadata are stored as distinct AWS secrets
-
Fine-grained IAM policies: Enables different access policies for private keys (restricted) vs public keys (read-only)
Security Note: This implementation stores PQC keys in AWS Secrets Manager. While this approach uses industry-standard formats and enables fine-grained access control, organizations with stringent security requirements should consider:
-
Using AWS CloudHSM for keys that must never be exportable
-
Implementing additional encryption layers using AWS KMS Customer Master Keys (CMKs)
-
Applying strict IAM policies limiting private key access to specific services or roles only
-
Regular key rotation and CloudTrail audit log monitoring
-
For production use, review and customize the IAM policies shown in the setup section
Dependencies:
To use AwsSecretsManagerKeyLifecycleManager, add the following optional dependency:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>secretsmanager</artifactId>
<version>${aws-java-sdk2-version}</version>
</dependency> Example with SecretsManagerClient:
// Using existing SecretsManagerClient (recommended when using camel-aws-secrets-manager)
@BindToRegistry("secretsManagerClient")
public SecretsManagerClient createSecretsManagerClient() {
return SecretsManagerClient.builder()
.region(Region.US_EAST_1)
.build(); // Uses default AWS credentials chain
}
@BindToRegistry("keyLifecycleManager")
public AwsSecretsManagerKeyLifecycleManager createKeyManager() {
return new AwsSecretsManagerKeyLifecycleManager(
secretsManagerClient, // Reuse existing client
"pqc/keys" // Key prefix
);
}
// Generate a Dilithium key stored in AWS Secrets Manager
KeyPair keyPair = keyManager.generateKeyPair("DILITHIUM", "app-signing-key",
DilithiumParameterSpec.dilithium2);
// Keys are stored as: pqc/keys/app-signing-key/private, /public, /metadata Example with Direct Configuration:
AwsSecretsManagerKeyLifecycleManager keyManager =
new AwsSecretsManagerKeyLifecycleManager(
"us-east-1", // AWS region
"AKIAIOSFODNN7EXAMPLE", // access key (optional, uses default credentials if null)
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", // secret key (optional)
"pqc/keys" // key prefix (optional, defaults to "pqc/keys")
); Example with LocalStack (Testing):
AwsSecretsManagerKeyLifecycleManager keyManager =
new AwsSecretsManagerKeyLifecycleManager(
"us-east-1", // region
"test", // access key for LocalStack
"test", // secret key for LocalStack
"pqc/test-keys", // key prefix
"http://localhost:4566" // LocalStack endpoint
); YAML Configuration:
camel:
beans:
keyLifecycleManager:
type: org.apache.camel.component.pqc.lifecycle.AwsSecretsManagerKeyLifecycleManager
constructorArgs:
- "us-east-1" # region
- "${AWS_ACCESS_KEY}" # access key from environment
- "${AWS_SECRET_KEY}" # secret key from environment
- "pqc/keys" # key prefix
- null # endpoint override (null for production) AWS Secrets Storage Structure:
Each key creates three secrets in AWS Secrets Manager:
pqc/keys/app-signing-key/private # PKCS#8 private key (STRICT IAM POLICY)
{
"key": "MIIEvQIBADANBg...", # Base64-encoded PKCS#8 private key
"format": "PKCS8",
"algorithm": "DILITHIUM"
}
Tags: ManagedBy=camel-pqc
pqc/keys/app-signing-key/public # X.509 public key (READ-ONLY IAM POLICY)
{
"key": "MIIBIjANBgkqhk...", # Base64-encoded X.509 public key
"format": "X509",
"algorithm": "DILITHIUM"
}
Tags: ManagedBy=camel-pqc
pqc/keys/app-signing-key/metadata # Key metadata
{
"metadata": "rO0ABXNyAC9vcm...", # Serialized KeyMetadata object
"keyId": "app-signing-key",
"algorithm": "DILITHIUM"
}
Tags: ManagedBy=camel-pqc AWS IAM Policies:
To use AwsSecretsManagerKeyLifecycleManager, configure IAM policies for appropriate access control. The pattern follows the same four-level model as Vault (Admin, Signing Service, Application, Specific Key). See the full policy examples below.
Basic Policy (Full Access - Development/Testing):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PQCKeysFullAccess",
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:DeleteSecret",
"secretsmanager:ListSecrets",
"secretsmanager:DescribeSecret",
"secretsmanager:TagResource"
],
"Resource": "arn:aws:secretsmanager:*:*:secret:pqc/keys/*"
},
{
"Sid": "KMSAccess",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:ViaService": "secretsmanager.*.amazonaws.com"
}
}
}
]
} Production Policies (Fine-Grained Access Control):
The production setup follows the same four-tier model as Vault:
-
Admin Policy - Full access for key management service (create, read, update, delete all key paths)
-
Signing Service Policy - Read-only access to private keys, public keys, and metadata for signing operations
-
Application Policy - Read-only access to public keys and metadata (explicitly denies private key access)
-
Specific Key Access - Limits access to a specific key ID only (production best practice)
Application Policy (Public Keys Only) example:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadPublicKeysOnly",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": [
"arn:aws:secretsmanager:*:*:secret:pqc/keys/*/public-*",
"arn:aws:secretsmanager:*:*:secret:pqc/keys/*/metadata-*"
]
},
{
"Sid": "DenyPrivateKeyAccess",
"Effect": "Deny",
"Action": "secretsmanager:GetSecretValue",
"Resource": "arn:aws:secretsmanager:*:*:secret:pqc/keys/*/private-*"
},
{
"Sid": "ListKeys",
"Effect": "Allow",
"Action": "secretsmanager:ListSecrets",
"Resource": "*"
},
{
"Sid": "KMSDecrypt",
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "*"
}
]
} Integration with camel-aws-secrets-manager:
AwsSecretsManagerKeyLifecycleManager can share the same SecretsManagerClient with the camel-aws-secrets-manager component:
@BindToRegistry("keyLifecycleManager")
public AwsSecretsManagerKeyLifecycleManager createKeyManager() {
SecretsManagerClient secretsManagerClient = context.getRegistry()
.lookupByNameAndType("secretsManagerClient", SecretsManagerClient.class);
return new AwsSecretsManagerKeyLifecycleManager(
secretsManagerClient,
"pqc/keys"
);
} Comparison of Implementations
| Feature | FileBasedKeyLifecycleManager | InMemoryKeyLifecycleManager | HashicorpVaultKeyLifecycleManager | AwsSecretsManagerKeyLifecycleManager |
|---|---|---|---|---|
Persistence | File system | Memory only | Vault backend | AWS Secrets Manager |
Distributed | Single node | Single node | Multi-node | Multi-region |
Audit Logging | Manual | None | Automatic (Vault) | Automatic (CloudTrail) |
Access Control | File permissions | None | Vault policies | IAM policies |
Encryption at Rest | OS-dependent | N/A | Always (Vault) | Always (AWS KMS) |
High Availability | No | No | Yes (Vault HA) | Yes (AWS Multi-AZ) |
External Dependencies | None | None | Vault + spring-vault | AWS SDK v2 |
Use Case | Single server | Testing/Dev | Production/Enterprise (Vault) | Production/Enterprise (AWS) |
Key Generation
The key lifecycle manager supports all PQC algorithms with sensible default parameter specifications.
Signature Algorithms
// ML-DSA (uses default ML-DSA-65)
KeyPair mldsaKey = keyManager.generateKeyPair("MLDSA", "mldsa-key");
// Dilithium with specific parameter
KeyPair dilithiumKey = keyManager.generateKeyPair("DILITHIUM", "dilithium-key",
DilithiumParameterSpec.dilithium3);
// Falcon (uses default Falcon-512)
KeyPair falconKey = keyManager.generateKeyPair("FALCON", "falcon-key");
// XMSS (uses default 10-tree height with SHA-256)
KeyPair xmssKey = keyManager.generateKeyPair("XMSS", "xmss-key");
// XMSSMT (uses default XMSSMT-SHA2-20d2-256)
KeyPair xmssmtKey = keyManager.generateKeyPair("XMSSMT", "xmssmt-key");
// LMS/HSS (uses default LMS-SHA256-N32-H10)
KeyPair lmsKey = keyManager.generateKeyPair("LMS", "lms-key"); Key Encapsulation Algorithms
// ML-KEM (uses default)
KeyPair mlkemKey = keyManager.generateKeyPair("MLKEM", "mlkem-key");
// NTRU (uses default ntruhps2048509)
KeyPair ntruKey = keyManager.generateKeyPair("NTRU", "ntru-key");
// SNTRUPrime (uses default sntrup761)
KeyPair sntrupKey = keyManager.generateKeyPair("SNTRUPrime", "sntrup-key");
// BIKE (uses default bike128)
KeyPair bikeKey = keyManager.generateKeyPair("BIKE", "bike-key"); Key Metadata
Each key is associated with metadata that tracks its lifecycle:
KeyMetadata metadata = keyManager.getKeyMetadata("my-key");
System.out.println("Key ID: " + metadata.getKeyId());
System.out.println("Algorithm: " + metadata.getAlgorithm());
System.out.println("Status: " + metadata.getStatus());
System.out.println("Created: " + metadata.getCreatedAt());
System.out.println("Age (days): " + metadata.getAgeInDays());
System.out.println("Usage count: " + metadata.getUsageCount());
System.out.println("Expires at: " + metadata.getExpiresAt());
System.out.println("Next rotation: " + metadata.getNextRotationAt()); Key Status Values:
-
ACTIVE- Key is active and can be used -
EXPIRED- Key has expired and should not be used -
REVOKED- Key has been revoked due to compromise -
PENDING_ROTATION- Key should be rotated soon -
DEPRECATED- Key has been rotated and replaced
Key Rotation
Key rotation is essential for maintaining security. The lifecycle manager supports automated rotation:
// Check if key needs rotation
boolean needsRotation = keyManager.needsRotation("old-key",
Duration.ofDays(90), // Max age: 90 days
10000); // Max usage: 10,000 operations
if (needsRotation) {
// Rotate the key
KeyPair newKey = keyManager.rotateKey("old-key", "new-key", "DILITHIUM");
// Old key is now DEPRECATED
// New key is ACTIVE
logger.info("Key rotated successfully");
} Key Export and Import
Keys can be exported and imported in multiple formats:
Supported Formats:
-
PEM- Privacy-Enhanced Mail format (Base64 encoded) -
DER- Distinguished Encoding Rules (binary) -
PKCS8- PKCS#8 format for private keys -
X509- X.509 format for public keys
Export Example:
KeyPair keyPair = keyManager.getKey("my-key");
// Export public key as PEM
byte[] publicPem = keyManager.exportPublicKey(keyPair, KeyFormat.PEM);
String pemString = new String(publicPem);
System.out.println(pemString);
// Output:
// -----BEGIN PUBLIC KEY-----
// MIIBIjAN...
// -----END PUBLIC KEY-----
// Export entire key pair (public only for safety)
byte[] keyPairPem = keyManager.exportKey(keyPair, KeyFormat.PEM, false);
// Export with private key (USE WITH CAUTION)
byte[] fullExport = keyManager.exportKey(keyPair, KeyFormat.DER, true); Import Example:
// Import from PEM format
byte[] pemData = Files.readAllBytes(Paths.get("public-key.pem"));
KeyPair imported = keyManager.importKey(pemData, KeyFormat.PEM, "DILITHIUM");
// Store the imported key
KeyMetadata metadata = new KeyMetadata("imported-key", "DILITHIUM");
keyManager.storeKey("imported-key", imported, metadata); Key Expiration and Revocation
Expire a Key:
// Set expiration time
KeyMetadata metadata = keyManager.getKeyMetadata("my-key");
metadata.setExpiresAt(Instant.now().plus(Duration.ofDays(365)));
keyManager.updateKeyMetadata("my-key", metadata);
// Manually expire immediately
keyManager.expireKey("my-key");
// Check if expired
if (metadata.isExpired()) {
logger.warn("Key {} has expired", metadata.getKeyId());
} Revoke a Key:
// Revoke a compromised key
keyManager.revokeKey("compromised-key", "Private key exposed in log file");
KeyMetadata metadata = keyManager.getKeyMetadata("compromised-key");
assert metadata.getStatus() == KeyMetadata.KeyStatus.REVOKED;
assert metadata.getDescription().contains("Revoked: Private key exposed"); Listing and Managing Keys
// List all keys
List<KeyMetadata> allKeys = keyManager.listKeys();
// Filter active keys
List<KeyMetadata> activeKeys = allKeys.stream()
.filter(m -> m.getStatus() == KeyMetadata.KeyStatus.ACTIVE)
.collect(Collectors.toList());
// Find keys needing rotation
for (KeyMetadata metadata : allKeys) {
if (keyManager.needsRotation(metadata.getKeyId(), Duration.ofDays(90), 5000)) {
logger.warn("Key {} needs rotation", metadata.getKeyId());
}
}
// Delete old keys
for (KeyMetadata metadata : allKeys) {
if (metadata.getStatus() == KeyMetadata.KeyStatus.DEPRECATED
&& metadata.getAgeInDays() > 365) {
keyManager.deleteKey(metadata.getKeyId());
logger.info("Deleted old deprecated key: {}", metadata.getKeyId());
}
} Integration with Camel Routes
The key lifecycle manager can be registered in the Camel registry and used in routes:
-
Java
-
YAML
@BindToRegistry("keyLifecycleManager")
public KeyLifecycleManager createKeyManager() throws IOException {
return new FileBasedKeyLifecycleManager("/secure/keys");
}
@BindToRegistry("signingKey")
public KeyPair createSigningKey() throws Exception {
KeyLifecycleManager manager = createKeyManager();
return manager.generateKeyPair("DILITHIUM", "route-signing-key",
DilithiumParameterSpec.dilithium2);
} camel:
beans:
keyLifecycleManager:
type: org.apache.camel.component.pqc.lifecycle.FileBasedKeyLifecycleManager
properties:
keyDirectoryPath: "/secure/keys"
signingKey:
type: org.apache.camel.component.pqc.lifecycle.FileBasedKeyLifecycleManager
scriptLanguage: groovy
script: |
def manager = new org.apache.camel.component.pqc.lifecycle.FileBasedKeyLifecycleManager('/secure/keys')
def spec = org.bouncycastle.pqc.jcajce.spec.DilithiumParameterSpec.dilithium2
manager.generateKeyPair('DILITHIUM', 'route-signing-key', spec) Using in Routes:
-
Java
-
YAML
from("timer:check-rotation?period=86400000") // Check daily
.bean("keyLifecycleManager", "needsRotation('route-signing-key', 90, 10000)")
.choice()
.when(body().isEqualTo(true))
.log("Rotating signing key")
.bean("keyLifecycleManager", "rotateKey('route-signing-key', 'route-signing-key-new', 'DILITHIUM')")
.to("log:rotation-complete")
.otherwise()
.log("Key rotation not needed")
.end(); - route:
id: check-key-rotation
from:
uri: timer:check-rotation
parameters:
period: 86400000 # Check daily
steps:
- bean:
ref: keyLifecycleManager
method: needsRotation('route-signing-key', 90, 10000)
- choice:
when:
- expression:
simple:
expression: "${body} == true"
steps:
- log:
message: "Rotating signing key"
- bean:
ref: keyLifecycleManager
method: rotateKey('route-signing-key', 'route-signing-key-new', 'DILITHIUM')
- to:
uri: log:rotation-complete
otherwise:
steps:
- log:
message: "Key rotation not needed" Default Parameter Specifications
The lifecycle manager provides sensible defaults for all algorithms:
| Algorithm | Default Parameter Spec |
|---|---|
DILITHIUM | dilithium2 |
FALCON | falcon_512 |
SPHINCSPLUS | sha2_128s |
XMSS | 10-tree height with SHA-256 |
XMSSMT | XMSSMT-SHA2-20d2-256 |
LMS/HSS | LMS-SHA256-N32-H10 with SHA256-N32-W4 |
NTRU | ntruhps2048509 |
NTRULPRime | ntrulpr653 |
SNTRUPrime | sntrup761 |
SABER | lightsaberkem128r3 |
FRODO | frodokem640aes |
BIKE | bike128 |
HQC | hqc128 |
CMCE | mceliece348864 |
Best Practices
1. Regular Key Rotation
Implement automated key rotation policies:
// Rotate keys every 90 days or after 10,000 uses
Duration maxAge = Duration.ofDays(90);
long maxUsage = 10000;
if (keyManager.needsRotation(keyId, maxAge, maxUsage)) {
keyManager.rotateKey(keyId, keyId + "-new", algorithm);
} 2. Secure Storage
For FileBasedKeyLifecycleManager:
-
Store keys in a secure directory with restricted permissions
-
Use encrypted file systems where possible
-
Implement backup strategies for key files
-
Monitor access to key directories
3. Key Metadata Tracking
Always update metadata when using keys:
KeyMetadata metadata = keyManager.getKeyMetadata(keyId);
metadata.updateLastUsed(); // Increments usage count
keyManager.updateKeyMetadata(keyId, metadata); 4. Handle Stateful Signatures Carefully
For XMSS, XMSSMT, LMS, and HSS algorithms:
-
Track signature usage to prevent exhaustion
-
Never reuse a stateful signature state (critical security requirement)
-
Monitor usage percentage and rotate before exhaustion
-
Set appropriate
maxSignaturesbased on tree height
5. Secure Key Export
When exporting keys:
-
Export public keys only when possible
-
Encrypt private key exports
-
Use secure channels for key transfer
-
Audit all key export operations
6. Testing Strategy
Use InMemoryKeyLifecycleManager for tests:
@BeforeEach
public void setup() {
keyManager = new InMemoryKeyLifecycleManager();
}
@AfterEach
public void cleanup() {
keyManager.clear();
} Thread Safety
Both implementations are thread-safe:
-
FileBasedKeyLifecycleManager: Uses ConcurrentHashMap for caching and atomic file operations
-
InMemoryKeyLifecycleManager: Uses ConcurrentHashMap for all storage
Concurrent key operations are safe:
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
final int index = i;
executor.submit(() -> {
try {
keyManager.generateKeyPair("DILITHIUM", "key-" + index,
DilithiumParameterSpec.dilithium2);
} catch (Exception e) {
logger.error("Key generation failed", e);
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES); Migration Between Implementations
To migrate from InMemoryKeyLifecycleManager to FileBasedKeyLifecycleManager:
InMemoryKeyLifecycleManager memoryManager = new InMemoryKeyLifecycleManager();
FileBasedKeyLifecycleManager fileManager = new FileBasedKeyLifecycleManager("/secure/keys");
// Export all keys
List<KeyMetadata> keys = memoryManager.listKeys();
for (KeyMetadata metadata : keys) {
KeyPair keyPair = memoryManager.getKey(metadata.getKeyId());
fileManager.storeKey(metadata.getKeyId(), keyPair, metadata);
}
logger.info("Migrated {} keys to file-based storage", keys.size());