This comprehensive guide covers secure key pair generation, storage, and management practices in Java for various cryptographic algorithms.
Project Setup and Dependencies
Maven Configuration
<!-- pom.xml --> <properties> <bouncycastle.version>1.76</bouncycastle.version> </properties> <dependencies> <!-- Bouncy Castle Provider for additional algorithms --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>${bouncycastle.version}</version> </dependency> <!-- Bouncy Castle PKIX for X.509 certificate handling --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> <version>${bouncycastle.version}</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.9</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.9</version> </dependency> </dependencies> Core KeyPair Generation Service
Secure KeyPair Generator
package com.security.crypto; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.KeyGenerator; import java.security.*; import java.security.spec.ECGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; import java.util.HashMap; import java.util.Map; public class SecureKeyPairGenerator { private static final Logger logger = LoggerFactory.getLogger(SecureKeyPairGenerator.class); // Secure random instance - reuse for better performance private static final SecureRandom SECURE_RANDOM = new SecureRandom(); static { // Initialize security providers Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // Seed the secure random seedSecureRandom(); } /** * Seed the secure random with additional entropy */ private static void seedSecureRandom() { try { // Add system entropy byte[] seed = new byte[32]; new SecureRandom().nextBytes(seed); // System random for initial seed SECURE_RANDOM.setSeed(seed); // Add thread timing entropy SECURE_RANDOM.setSeed(System.nanoTime()); SECURE_RANDOM.setSeed(Thread.currentThread().getId()); logger.info("SecureRandom seeded successfully"); } catch (Exception e) { logger.warn("Failed to seed SecureRandom: {}", e.getMessage()); } } /** * Generate RSA key pair with secure parameters */ public KeyPair generateRSAKeyPair(int keySize) throws GeneralSecurityException { validateRSAKeySize(keySize); try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "BC"); // Use secure parameters for RSA key generation RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec( keySize, RSAKeyGenParameterSpec.F4 // 65537 - standard public exponent ); keyGen.initialize(spec, SECURE_RANDOM); KeyPair keyPair = keyGen.generateKeyPair(); logger.info("Generated RSA-{} key pair successfully", keySize); logKeyStrength(keyPair.getPublic()); return keyPair; } catch (GeneralSecurityException e) { logger.error("Failed to generate RSA-{} key pair: {}", keySize, e.getMessage()); throw e; } } /** * Generate Elliptic Curve key pair */ public KeyPair generateECKeyPair(String curveName) throws GeneralSecurityException { validateECCurveName(curveName); try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC"); // Use named curve for better interoperability ECGenParameterSpec ecSpec = new ECGenParameterSpec(curveName); keyGen.initialize(ecSpec, SECURE_RANDOM); KeyPair keyPair = keyGen.generateKeyPair(); logger.info("Generated EC key pair with curve {} successfully", curveName); logKeyStrength(keyPair.getPublic()); return keyPair; } catch (GeneralSecurityException e) { logger.error("Failed to generate EC key pair with curve {}: {}", curveName, e.getMessage()); throw e; } } /** * Generate DSA key pair */ public KeyPair generateDSAKeyPair(int keySize) throws GeneralSecurityException { validateDSAKeySize(keySize); try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "BC"); keyGen.initialize(keySize, SECURE_RANDOM); KeyPair keyPair = keyGen.generateKeyPair(); logger.info("Generated DSA-{} key pair successfully", keySize); logKeyStrength(keyPair.getPublic()); return keyPair; } catch (GeneralSecurityException e) { logger.error("Failed to generate DSA-{} key pair: {}", keySize, e.getMessage()); throw e; } } /** * Generate EdDSA key pair (Ed25519/Ed448) */ public KeyPair generateEdDSAKeyPair(String algorithm) throws GeneralSecurityException { if (!algorithm.equalsIgnoreCase("Ed25519") && !algorithm.equalsIgnoreCase("Ed448")) { throw new IllegalArgumentException("Unsupported EdDSA algorithm: " + algorithm); } try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm, "BC"); keyGen.initialize(null, SECURE_RANDOM); // EdDSA doesn't need explicit parameters KeyPair keyPair = keyGen.generateKeyPair(); logger.info("Generated {} key pair successfully", algorithm); logKeyStrength(keyPair.getPublic()); return keyPair; } catch (GeneralSecurityException e) { logger.error("Failed to generate {} key pair: {}", algorithm, e.getMessage()); throw e; } } /** * Validate RSA key size */ private void validateRSAKeySize(int keySize) { if (keySize < 2048) { throw new IllegalArgumentException( "RSA key size must be at least 2048 bits for security. Provided: " + keySize); } // Check if key size is a power of two for optimal performance if ((keySize & (keySize - 1)) != 0) { logger.warn("RSA key size {} is not a power of two, which may impact performance", keySize); } // NIST recommendations if (keySize < 3072) { logger.warn("RSA key size {} is below NIST recommended 3072 bits for new systems", keySize); } } /** * Validate EC curve name */ private void validateECCurveName(String curveName) { Map<String, Integer> standardCurves = new HashMap<>(); standardCurves.put("secp256r1", 256); // NIST P-256 standardCurves.put("secp384r1", 384); // NIST P-384 standardCurves.put("secp521r1", 521); // NIST P-521 standardCurves.put("brainpoolP256r1", 256); standardCurves.put("brainpoolP384r1", 384); standardCurves.put("brainpoolP512r1", 512); if (!standardCurves.containsKey(curveName)) { throw new IllegalArgumentException( "Unsupported EC curve: " + curveName + ". Supported: " + standardCurves.keySet()); } logger.debug("Using EC curve: {} ({} bits security)", curveName, standardCurves.get(curveName)); } /** * Validate DSA key size */ private void validateDSAKeySize(int keySize) { if (keySize < 2048) { throw new IllegalArgumentException( "DSA key size must be at least 2048 bits for security. Provided: " + keySize); } // DSA key sizes should be multiples of 64 if (keySize % 64 != 0) { throw new IllegalArgumentException("DSA key size must be a multiple of 64. Provided: " + keySize); } } /** * Log key strength information */ private void logKeyStrength(PublicKey publicKey) { String algorithm = publicKey.getAlgorithm(); int keySize = getKeySize(publicKey); String strength; if (keySize >= 3072 || (algorithm.equals("EC") && keySize >= 256)) { strength = "STRONG"; } else if (keySize >= 2048) { strength = "ADEQUATE"; } else { strength = "WEAK"; } logger.info("Key strength: {} - {}-bit {}", strength, keySize, algorithm); } /** * Estimate key size from public key */ private int getKeySize(PublicKey publicKey) { String algorithm = publicKey.getAlgorithm(); if (algorithm.equals("RSA") || algorithm.equals("DSA")) { // For RSA and DSA, key size is explicit in the encoding return publicKey.getEncoded().length * 8; // Rough estimate } else if (algorithm.equals("EC")) { // For EC, we need to parse the curve parameters java.security.interfaces.ECPublicKey ecKey = (java.security.interfaces.ECPublicKey) publicKey; return ecKey.getParams().getCurve().getField().getFieldSize(); } else if (algorithm.equals("Ed25519")) { return 255; } else if (algorithm.equals("Ed448")) { return 448; } return -1; // Unknown } /** * Get secure random instance */ public SecureRandom getSecureRandom() { return SECURE_RANDOM; } } Key Storage and Management
Secure Key Storage Service
package com.security.crypto; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.*; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.nio.file.*; import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class SecureKeyStorage { private static final Logger logger = LoggerFactory.getLogger(SecureKeyStorage.class); // Encryption parameters private static final String KEY_ENCRYPTION_ALGORITHM = "AES/GCM/NoPadding"; private static final int GCM_TAG_LENGTH = 128; // bits private static final int GCM_IV_LENGTH = 12; // bytes private static final int SALT_LENGTH = 32; // bytes private static final int ITERATION_COUNT = 210000; // OWASP recommended /** * Store key pair securely to files */ public void storeKeyPair(KeyPair keyPair, String basePath, char[] password) throws GeneralSecurityException, IOException { validateStoragePath(basePath); // Create directory if it doesn't exist Path directory = Paths.get(basePath).getParent(); if (directory != null) { Files.createDirectories(directory); } // Store private key encrypted String privateKeyPath = basePath + ".private"; storePrivateKey(keyPair.getPrivate(), privateKeyPath, password); // Store public key in plain text (it's public) String publicKeyPath = basePath + ".public"; storePublicKey(keyPair.getPublic(), publicKeyPath); logger.info("Key pair stored successfully: {}", basePath); } /** * Store encrypted private key */ private void storePrivateKey(PrivateKey privateKey, String filePath, char[] password) throws GeneralSecurityException, IOException { // Encrypt private key byte[] encryptedKey = encryptKey(privateKey.getEncoded(), password); // Write to file with secure permissions Path path = Paths.get(filePath); Files.write(path, encryptedKey, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); // Set secure file permissions (Unix-like systems) try { path.toFile().setReadable(false, false); // No global read path.toFile().setReadable(true, true); // Owner read only path.toFile().setWritable(true, true); // Owner write only } catch (SecurityException e) { logger.warn("Could not set secure file permissions: {}", e.getMessage()); } logger.debug("Private key stored encrypted: {}", filePath); } /** * Store public key */ private void storePublicKey(PublicKey publicKey, String filePath) throws IOException { // Public key doesn't need encryption byte[] publicKeyBytes = publicKey.getEncoded(); // Write to file Files.write(Paths.get(filePath), publicKeyBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); logger.debug("Public key stored: {}", filePath); } /** * Load key pair from storage */ public KeyPair loadKeyPair(String basePath, char[] password, String keyAlgorithm) throws GeneralSecurityException, IOException { validateStoragePath(basePath); // Load private key String privateKeyPath = basePath + ".private"; PrivateKey privateKey = loadPrivateKey(privateKeyPath, password, keyAlgorithm); // Load public key String publicKeyPath = basePath + ".public"; PublicKey publicKey = loadPublicKey(publicKeyPath, keyAlgorithm); KeyPair keyPair = new KeyPair(publicKey, privateKey); logger.info("Key pair loaded successfully: {}", basePath); return keyPair; } /** * Load and decrypt private key */ private PrivateKey loadPrivateKey(String filePath, char[] password, String keyAlgorithm) throws GeneralSecurityException, IOException { // Read encrypted key byte[] encryptedKey = Files.readAllBytes(Paths.get(filePath)); // Decrypt private key byte[] privateKeyBytes = decryptKey(encryptedKey, password); // Reconstruct private key KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm, "BC"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); return keyFactory.generatePrivate(keySpec); } /** * Load public key */ private PublicKey loadPublicKey(String filePath, String keyAlgorithm) throws GeneralSecurityException, IOException { byte[] publicKeyBytes = Files.readAllBytes(Paths.get(filePath)); KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm, "BC"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); return keyFactory.generatePublic(keySpec); } /** * Encrypt key using password-based encryption */ private byte[] encryptKey(byte[] keyData, char[] password) throws GeneralSecurityException { // Generate salt byte[] salt = new byte[SALT_LENGTH]; new SecureRandom().nextBytes(salt); // Generate IV for GCM byte[] iv = new byte[GCM_IV_LENGTH]; new SecureRandom().nextBytes(iv); // Derive encryption key from password SecretKey secretKey = deriveKey(password, salt); // Initialize cipher Cipher cipher = Cipher.getInstance(KEY_ENCRYPTION_ALGORITHM, "BC"); GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv); cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmSpec); // Encrypt key data byte[] encryptedKey = cipher.doFinal(keyData); // Combine salt + iv + encrypted data byte[] result = new byte[salt.length + iv.length + encryptedKey.length]; System.arraycopy(salt, 0, result, 0, salt.length); System.arraycopy(iv, 0, result, salt.length, iv.length); System.arraycopy(encryptedKey, 0, result, salt.length + iv.length, encryptedKey.length); // Clear sensitive data from memory Arrays.fill(salt, (byte) 0); Arrays.fill(iv, (byte) 0); return result; } /** * Decrypt key using password-based encryption */ private byte[] decryptKey(byte[] encryptedData, char[] password) throws GeneralSecurityException { // Extract components byte[] salt = new byte[SALT_LENGTH]; byte[] iv = new byte[GCM_IV_LENGTH]; byte[] encryptedKey = new byte[encryptedData.length - SALT_LENGTH - GCM_IV_LENGTH]; System.arraycopy(encryptedData, 0, salt, 0, SALT_LENGTH); System.arraycopy(encryptedData, SALT_LENGTH, iv, 0, GCM_IV_LENGTH); System.arraycopy(encryptedData, SALT_LENGTH + GCM_IV_LENGTH, encryptedKey, 0, encryptedKey.length); // Derive decryption key SecretKey secretKey = deriveKey(password, salt); // Initialize cipher Cipher cipher = Cipher.getInstance(KEY_ENCRYPTION_ALGORITHM, "BC"); GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv); cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmSpec); // Decrypt key data byte[] decryptedKey = cipher.doFinal(encryptedKey); // Clear sensitive data from memory Arrays.fill(salt, (byte) 0); Arrays.fill(iv, (byte) 0); return decryptedKey; } /** * Derive encryption key from password using PBKDF2 */ private SecretKey deriveKey(char[] password, byte[] salt) throws GeneralSecurityException { PBEKeySpec keySpec = new PBEKeySpec(password, salt, ITERATION_COUNT, 256); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", "BC"); byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); // Clear sensitive data keySpec.clearPassword(); Arrays.fill(keyBytes, (byte) 0); return secretKey; } /** * Validate storage path */ private void validateStoragePath(String basePath) { if (basePath == null || basePath.trim().isEmpty()) { throw new IllegalArgumentException("Storage path cannot be null or empty"); } // Check for path traversal attempts if (basePath.contains("..") || basePath.contains("~")) { throw new IllegalArgumentException("Invalid storage path: " + basePath); } } /** * Export public key to PEM format */ public String exportPublicKeyToPEM(PublicKey publicKey) { byte[] encoded = publicKey.getEncoded(); String base64 = Base64.getEncoder().encodeToString(encoded); StringBuilder pem = new StringBuilder(); pem.append("-----BEGIN PUBLIC KEY-----\n"); // Split into 64-character lines for (int i = 0; i < base64.length(); i += 64) { int end = Math.min(i + 64, base64.length()); pem.append(base64, i, end).append("\n"); } pem.append("-----END PUBLIC KEY-----\n"); return pem.toString(); } /** * Import public key from PEM format */ public PublicKey importPublicKeyFromPEM(String pem, String algorithm) throws GeneralSecurityException { // Remove PEM headers and whitespace String base64 = pem.replaceAll("-----BEGIN PUBLIC KEY-----", "") .replaceAll("-----END PUBLIC KEY-----", "") .replaceAll("\\s", ""); byte[] encoded = Base64.getDecoder().decode(base64); KeyFactory keyFactory = KeyFactory.getInstance(algorithm, "BC"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded); return keyFactory.generatePublic(keySpec); } } Key Rotation and Lifecycle Management
Key Rotation Service
package com.security.crypto; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.KeyPair; import java.security.PublicKey; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class KeyRotationService { private static final Logger logger = LoggerFactory.getLogger(KeyRotationService.class); private final SecureKeyPairGenerator keyGenerator; private final SecureKeyStorage keyStorage; private final ScheduledExecutorService scheduler; // Key rotation policies private final Map<String, RotationPolicy> rotationPolicies; private final Map<String, KeyVersion> activeKeys; public KeyRotationService() { this.keyGenerator = new SecureKeyPairGenerator(); this.keyStorage = new SecureKeyStorage(); this.scheduler = Executors.newScheduledThreadPool(2); this.rotationPolicies = new ConcurrentHashMap<>(); this.activeKeys = new ConcurrentHashMap<>(); // Start background rotation tasks startRotationMonitor(); } /** * Key rotation policy */ public static class RotationPolicy { private final String keyId; private final int maxLifetimeDays; private final int warningPeriodDays; private final String algorithm; private final int keySize; private final String curveName; public RotationPolicy(String keyId, int maxLifetimeDays, int warningPeriodDays, String algorithm, int keySize, String curveName) { this.keyId = keyId; this.maxLifetimeDays = maxLifetimeDays; this.warningPeriodDays = warningPeriodDays; this.algorithm = algorithm; this.keySize = keySize; this.curveName = curveName; } // Getters public String getKeyId() { return keyId; } public int getMaxLifetimeDays() { return maxLifetimeDays; } public int getWarningPeriodDays() { return warningPeriodDays; } public String getAlgorithm() { return algorithm; } public int getKeySize() { return keySize; } public String getCurveName() { return curveName; } } /** * Key version information */ public static class KeyVersion { private final String keyId; private final String version; private final LocalDateTime creationTime; private final LocalDateTime expirationTime; private final KeyPair keyPair; private boolean active; public KeyVersion(String keyId, String version, LocalDateTime creationTime, LocalDateTime expirationTime, KeyPair keyPair) { this.keyId = keyId; this.version = version; this.creationTime = creationTime; this.expirationTime = expirationTime; this.keyPair = keyPair; this.active = true; } // Getters public String getKeyId() { return keyId; } public String getVersion() { return version; } public LocalDateTime getCreationTime() { return creationTime; } public LocalDateTime getExpirationTime() { return expirationTime; } public KeyPair getKeyPair() { return keyPair; } public boolean isActive() { return active; } public boolean isExpired() { return LocalDateTime.now().isAfter(expirationTime); } public boolean isExpiringSoon() { return LocalDateTime.now().isAfter(expirationTime.minusDays(7)); } public void deactivate() { this.active = false; } } /** * Register a key rotation policy */ public void registerRotationPolicy(RotationPolicy policy) { rotationPolicies.put(policy.getKeyId(), policy); logger.info("Registered rotation policy for key: {}", policy.getKeyId()); } /** * Generate initial key pair with rotation policy */ public KeyVersion generateInitialKey(String keyId, char[] storagePassword) throws GeneralSecurityException { RotationPolicy policy = rotationPolicies.get(keyId); if (policy == null) { throw new IllegalArgumentException("No rotation policy found for key: " + keyId); } // Generate key pair KeyPair keyPair = generateKeyPair(policy); // Create key version String version = generateVersionId(); LocalDateTime now = LocalDateTime.now(); LocalDateTime expiration = now.plusDays(policy.getMaxLifetimeDays()); KeyVersion keyVersion = new KeyVersion(keyId, version, now, expiration, keyPair); // Store key String storagePath = buildStoragePath(keyId, version); keyStorage.storeKeyPair(keyPair, storagePath, storagePassword); // Set as active activeKeys.put(keyId, keyVersion); logger.info("Generated initial key version {} for key: {}", version, keyId); return keyVersion; } /** * Rotate key to new version */ public KeyVersion rotateKey(String keyId, char[] storagePassword) throws GeneralSecurityException { RotationPolicy policy = rotationPolicies.get(keyId); if (policy == null) { throw new IllegalArgumentException("No rotation policy found for key: " + keyId); } // Generate new key pair KeyPair newKeyPair = generateKeyPair(policy); // Create new version String newVersion = generateVersionId(); LocalDateTime now = LocalDateTime.now(); LocalDateTime expiration = now.plusDays(policy.getMaxLifetimeDays()); KeyVersion newKeyVersion = new KeyVersion(keyId, newVersion, now, expiration, newKeyPair); // Store new key String storagePath = buildStoragePath(keyId, newVersion); keyStorage.storeKeyPair(newKeyPair, storagePath, storagePassword); // Deactivate old key KeyVersion oldKeyVersion = activeKeys.get(keyId); if (oldKeyVersion != null) { oldKeyVersion.deactivate(); logger.info("Deactivated old key version {} for key: {}", oldKeyVersion.getVersion(), keyId); } // Activate new key activeKeys.put(keyId, newKeyVersion); logger.info("Rotated key {} to new version: {}", keyId, newVersion); return newKeyVersion; } /** * Get active key for a key ID */ public KeyVersion getActiveKey(String keyId) { KeyVersion keyVersion = activeKeys.get(keyId); if (keyVersion == null || !keyVersion.isActive()) { throw new IllegalArgumentException("No active key found for: " + keyId); } return keyVersion; } /** * Check if key needs rotation */ public boolean needsRotation(String keyId) { KeyVersion keyVersion = activeKeys.get(keyId); if (keyVersion == null) return true; return keyVersion.isExpiringSoon() || keyVersion.isExpired(); } /** * Generate key pair based on policy */ private KeyPair generateKeyPair(RotationPolicy policy) throws GeneralSecurityException { switch (policy.getAlgorithm().toUpperCase()) { case "RSA": return keyGenerator.generateRSAKeyPair(policy.getKeySize()); case "EC": return keyGenerator.generateECKeyPair(policy.getCurveName()); case "DSA": return keyGenerator.generateDSAKeyPair(policy.getKeySize()); case "ED25519": case "ED448": return keyGenerator.generateEdDSAKeyPair(policy.getAlgorithm()); default: throw new IllegalArgumentException("Unsupported algorithm: " + policy.getAlgorithm()); } } /** * Generate version ID based on timestamp */ private String generateVersionId() { return DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss").format(LocalDateTime.now()); } /** * Build storage path for key version */ private String buildStoragePath(String keyId, String version) { return String.format("keys/%s/%s", keyId, version); } /** * Start background rotation monitoring */ private void startRotationMonitor() { // Check for expiring keys every hour scheduler.scheduleAtFixedRate(() -> { try { monitorKeyExpiration(); } catch (Exception e) { logger.error("Error in key rotation monitor", e); } }, 1, 1, TimeUnit.HOURS); logger.info("Key rotation monitor started"); } /** * Monitor key expiration and send alerts */ private void monitorKeyExpiration() { for (Map.Entry<String, KeyVersion> entry : activeKeys.entrySet()) { String keyId = entry.getKey(); KeyVersion keyVersion = entry.getValue(); if (keyVersion.isExpired()) { logger.error("KEY EXPIRED: Key {} version {} has expired!", keyId, keyVersion.getVersion()); sendExpirationAlert(keyId, keyVersion, "EXPIRED"); } else if (keyVersion.isExpiringSoon()) { logger.warn("KEY EXPIRING: Key {} version {} expires in less than 7 days", keyId, keyVersion.getVersion()); sendExpirationAlert(keyId, keyVersion, "EXPIRING_SOON"); } } } /** * Send expiration alert */ private void sendExpirationAlert(String keyId, KeyVersion keyVersion, String severity) { // In a real implementation, this would send email, Slack message, etc. String message = String.format( "Key Rotation Alert [%s] - Key: %s, Version: %s, Expires: %s", severity, keyId, keyVersion.getVersion(), keyVersion.getExpirationTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) ); logger.warn("KEY ROTATION ALERT: {}", message); // Example: Integrate with monitoring system // monitoringService.alert("KeyRotation", severity, message); } /** * Shutdown the rotation service */ public void shutdown() { scheduler.shutdown(); try { if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { scheduler.shutdownNow(); Thread.currentThread().interrupt(); } logger.info("Key rotation service shutdown complete"); } } Key Validation and Testing
Key Validation Service
```java
package com.security.crypto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.; import java.security.interfaces.;
import java.util.ArrayList;
import java.util.List;
public class KeyValidationService {
private static final Logger logger = LoggerFactory.getLogger(KeyValidationService.class);
/** * Validate key pair for correctness and security */ public ValidationResult validateKeyPair(KeyPair keyPair) { List<String> errors = new ArrayList<>(); List<String> warnings = new ArrayList<>(); try { // Basic validation validateNotNull(keyPair, errors); validateKeyComponents(keyPair, errors); // Algorithm-specific validation String algorithm = keyPair.getPublic().getAlgorithm(); switch (algorithm.toUpperCase()) { case "RSA": validateRSAKeyPair((RSAPublicKey) keyPair.getPublic(), (RSAPrivateKey) keyPair.getPrivate(), errors, warnings); break; case "EC": validateECKeyPair((ECPublicKey) keyPair.getPublic(), (ECPrivateKey) keyPair.getPrivate(), errors, warnings); break; case "DSA": validateDSAKeyPair((DSAPublicKey) keyPair.getPublic(), (DSAPrivateKey) keyPair.getPrivate(), errors, warnings); break; default: warnings.add("Unsupported algorithm for detailed validation: " + algorithm); } // Perform cryptographic test if (errors.isEmpty()) { testKeyPairFunctionality(keyPair, errors); } } catch (Exception e) { errors.add("Validation failed with exception: " + e.getMessage()); } return new ValidationResult(errors, warnings); } /** * Validate RSA key pair */ private void validateRSAKeyPair(RSAPublicKey publicKey, RSAPrivateKey privateKey, List<String> errors, List<String> warnings) { // Check key sizes int keySize = publicKey.getModulus().bitLength(); if (keySize < 2048) { errors.add("RSA key size too small: " + keySize + " bits (minimum 2048)"); } else if (keySize < 3072) { warnings.add("RSA key size " + keySize + " bits is below NIST recommended 3072 bits"); } // Check public exponent if (!publicKey.getPublicExponent().equals(RSAPublicKeySpec.F4)) { warnings.add("RSA public exponent is not 65537 (F4)"); } // Check modulus properties if (!publicKey.getModulus().isProbablePrime(100)) { errors.add("RSA modulus is not prime"); } // Verify public/private key relationship try { if (!publicKey.getModulus().equals(privateKey.getModulus())) { errors.add("RSA public and private key moduli do not match"); } } catch (Exception e) { errors.add("Failed to verify RSA key relationship: " + e.getMessage()); } } /** * Validate EC key pair */ private void validateECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey, List<String> errors, List<String> warnings) { // Check curve strength int fieldSize = publicKey.getParams().getCurve().getField().getFieldSize(); if (fieldSize < 256) { errors.add("EC key curve too weak: " + fieldSize + " bits (minimum 256)"); } // Verify public key is on curve try { if (!isPointOnCurve(publicKey)) { errors.add("EC public key point is not on the curve"); } } catch (Exception e) { errors.add("Failed to validate EC point: " + e.getMessage()); } // Check private key range if (privateKey.getS().signum() <= 0) { errors.add("EC private key is not positive"); } } /** * Validate DSA key pair */ private void validateDSAKeyPair(DSAPublicKey publicKey, DSAPrivateKey privateKey, List<String> errors, List<String> warnings) { int keySize = publicKey.getParams().getP().bitLength(); if (keySize < 2048) { errors.add("DSA key size too small: " + keySize + " bits (minimum 2048)"); } // Verify parameter consistency if (!publicKey.getParams().equals(privateKey.getParams())) { errors.add("DSA public and private key parameters do not match"); } } /** * Basic validation checks */ private void validateNotNull(KeyPair keyPair, List<String> errors) { if (keyPair == null) { errors.add("Key pair is null"); return; } if (keyPair.getPublic() == null) { errors.add("Public key is null"); } if (keyPair.getPrivate() == null) { errors.add("Private key is null"); }