Introduction to Digital Signatures
Digital signatures provide authentication, integrity, and non-repudiation for digital messages or documents. They use public-key cryptography to verify that a message was created by a known sender and wasn't altered in transit.
Core Java Cryptography Architecture (JCA)
Key Classes and Interfaces
- KeyPairGenerator: Generates public/private key pairs
- Signature: Creates and verifies digital signatures
- KeyStore: Manages cryptographic keys and certificates
- MessageDigest: Creates hash values for data
- SecureRandom: Generates cryptographically strong random numbers
Basic Digital Signature Operations
Key Pair Generation
import java.security.*; import java.security.spec.ECGenParameterSpec; import java.util.Base64; public class KeyPairGeneratorExample { public static KeyPair generateRSAKeyPair(int keySize) throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(keySize); return keyGen.generateKeyPair(); } public static KeyPair generateDSAKeyPair(int keySize) throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); keyGen.initialize(keySize); return keyGen.generateKeyPair(); } public static KeyPair generateECKeyPair(String curveName) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); ECGenParameterSpec ecSpec = new ECGenParameterSpec(curveName); keyGen.initialize(ecSpec); return keyGen.generateKeyPair(); } public static void main(String[] args) throws Exception { // Generate RSA key pair KeyPair rsaKeyPair = generateRSAKeyPair(2048); System.out.println("RSA Public Key: " + Base64.getEncoder().encodeToString(rsaKeyPair.getPublic().getEncoded())); System.out.println("RSA Private Key: " + Base64.getEncoder().encodeToString(rsaKeyPair.getPrivate().getEncoded())); // Generate DSA key pair KeyPair dsaKeyPair = generateDSAKeyPair(2048); System.out.println("DSA Public Key: " + Base64.getEncoder().encodeToString(dsaKeyPair.getPublic().getEncoded())); // Generate EC key pair KeyPair ecKeyPair = generateECKeyPair("secp256r1"); System.out.println("EC Public Key: " + Base64.getEncoder().encodeToString(ecKeyPair.getPublic().getEncoded())); } } Basic Signature Creation and Verification
import java.security.*; import java.util.Base64; public class BasicDigitalSignature { /** * Creates a digital signature for the given data */ public static byte[] createSignature(byte[] data, PrivateKey privateKey, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { Signature signature = Signature.getInstance(algorithm); signature.initSign(privateKey); signature.update(data); return signature.sign(); } /** * Verifies a digital signature */ public static boolean verifySignature(byte[] data, byte[] signatureBytes, PublicKey publicKey, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { Signature signature = Signature.getInstance(algorithm); signature.initVerify(publicKey); signature.update(data); return signature.verify(signatureBytes); } public static void main(String[] args) throws Exception { String message = "This is a important message that needs signing"; byte[] data = message.getBytes(); // Generate key pair KeyPair keyPair = KeyPairGeneratorExample.generateRSAKeyPair(2048); // Create signature String algorithm = "SHA256withRSA"; byte[] signature = createSignature(data, keyPair.getPrivate(), algorithm); System.out.println("Original Message: " + message); System.out.println("Signature: " + Base64.getEncoder().encodeToString(signature)); // Verify signature boolean isValid = verifySignature(data, signature, keyPair.getPublic(), algorithm); System.out.println("Signature valid: " + isValid); // Test with tampered data String tamperedMessage = "This is a tampered message that needs signing"; byte[] tamperedData = tamperedMessage.getBytes(); boolean isTamperedValid = verifySignature(tamperedData, signature, keyPair.getPublic(), algorithm); System.out.println("Tampered signature valid: " + isTamperedValid); } } Advanced Digital Signature Implementation
Comprehensive Signature Service
import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class DigitalSignatureService { public enum SignatureAlgorithm { RSA_SHA256("SHA256withRSA"), RSA_SHA512("SHA512withRSA"), DSA_SHA256("SHA256withDSA"), ECDSA_SHA256("SHA256withECDSA"), ECDSA_SHA512("SHA512withECDSA"); private final String algorithm; SignatureAlgorithm(String algorithm) { this.algorithm = algorithm; } public String getAlgorithm() { return algorithm; } } private final SecureRandom secureRandom; public DigitalSignatureService() { this.secureRandom = new SecureRandom(); } /** * Generates a key pair for the specified algorithm */ public KeyPair generateKeyPair(SignatureAlgorithm algorithm, int keySize) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { KeyPairGenerator keyGen; switch (algorithm) { case RSA_SHA256: case RSA_SHA512: keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(keySize, secureRandom); break; case DSA_SHA256: keyGen = KeyPairGenerator.getInstance("DSA"); keyGen.initialize(keySize, secureRandom); break; case ECDSA_SHA256: case ECDSA_SHA512: keyGen = KeyPairGenerator.getInstance("EC"); ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1"); keyGen.initialize(ecSpec, secureRandom); break; default: throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); } return keyGen.generateKeyPair(); } /** * Creates a digital signature with metadata */ public SignatureResult signData(byte[] data, PrivateKey privateKey, SignatureAlgorithm algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { long startTime = System.currentTimeMillis(); Signature signature = Signature.getInstance(algorithm.getAlgorithm()); signature.initSign(privateKey, secureRandom); signature.update(data); byte[] signatureBytes = signature.sign(); long endTime = System.currentTimeMillis(); return new SignatureResult( data, signatureBytes, algorithm, endTime - startTime ); } /** * Verifies a digital signature with detailed results */ public VerificationResult verifySignature(SignatureResult signatureResult, PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { long startTime = System.currentTimeMillis(); Signature signature = Signature.getInstance(signatureResult.getAlgorithm().getAlgorithm()); signature.initVerify(publicKey); signature.update(signatureResult.getOriginalData()); boolean isValid = signature.verify(signatureResult.getSignature()); long endTime = System.currentTimeMillis(); return new VerificationResult( isValid, signatureResult.getAlgorithm(), endTime - startTime, signatureResult.getOriginalData().length, signatureResult.getSignature().length ); } /** * Signs large data in chunks */ public byte[] signLargeData(java.io.InputStream dataStream, PrivateKey privateKey, SignatureAlgorithm algorithm, int bufferSize) throws Exception { Signature signature = Signature.getInstance(algorithm.getAlgorithm()); signature.initSign(privateKey, secureRandom); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = dataStream.read(buffer)) != -1) { signature.update(buffer, 0, bytesRead); } return signature.sign(); } /** * Verifies large data in chunks */ public boolean verifyLargeData(java.io.InputStream dataStream, byte[] signatureBytes, PublicKey publicKey, SignatureAlgorithm algorithm, int bufferSize) throws Exception { Signature signature = Signature.getInstance(algorithm.getAlgorithm()); signature.initVerify(publicKey); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = dataStream.read(buffer)) != -1) { signature.update(buffer, 0, bytesRead); } return signature.verify(signatureBytes); } // Utility methods for key serialization public String publicKeyToString(PublicKey publicKey) { return Base64.getEncoder().encodeToString(publicKey.getEncoded()); } public String privateKeyToString(PrivateKey privateKey) { return Base64.getEncoder().encodeToString(privateKey.getEncoded()); } public PublicKey publicKeyFromString(String keyStr, String algorithm) throws Exception { byte[] keyBytes = Base64.getDecoder().decode(keyStr); KeyFactory keyFactory = KeyFactory.getInstance(algorithm); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); return keyFactory.generatePublic(keySpec); } public PrivateKey privateKeyFromString(String keyStr, String algorithm) throws Exception { byte[] keyBytes = Base64.getDecoder().decode(keyStr); KeyFactory keyFactory = KeyFactory.getInstance(algorithm); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); return keyFactory.generatePrivate(keySpec); } // Result classes public static class SignatureResult { private final byte[] originalData; private final byte[] signature; private final SignatureAlgorithm algorithm; private final long signingTimeMs; public SignatureResult(byte[] originalData, byte[] signature, SignatureAlgorithm algorithm, long signingTimeMs) { this.originalData = originalData; this.signature = signature; this.algorithm = algorithm; this.signingTimeMs = signingTimeMs; } // Getters public byte[] getOriginalData() { return originalData; } public byte[] getSignature() { return signature; } public SignatureAlgorithm getAlgorithm() { return algorithm; } public long getSigningTimeMs() { return signingTimeMs; } } public static class VerificationResult { private final boolean valid; private final SignatureAlgorithm algorithm; private final long verificationTimeMs; private final int dataSize; private final int signatureSize; public VerificationResult(boolean valid, SignatureAlgorithm algorithm, long verificationTimeMs, int dataSize, int signatureSize) { this.valid = valid; this.algorithm = algorithm; this.verificationTimeMs = verificationTimeMs; this.dataSize = dataSize; this.signatureSize = signatureSize; } // Getters public boolean isValid() { return valid; } public SignatureAlgorithm getAlgorithm() { return algorithm; } public long getVerificationTimeMs() { return verificationTimeMs; } public int getDataSize() { return dataSize; } public int getSignatureSize() { return signatureSize; } } } Example Usage
public class DigitalSignatureDemo { public static void main(String[] args) throws Exception { DigitalSignatureService service = new DigitalSignatureService(); // Generate key pair KeyPair keyPair = service.generateKeyPair( DigitalSignatureService.SignatureAlgorithm.RSA_SHA256, 2048); String message = "This is a confidential business document"; byte[] data = message.getBytes(); // Sign the data DigitalSignatureService.SignatureResult signatureResult = service.signData(data, keyPair.getPrivate(), DigitalSignatureService.SignatureAlgorithm.RSA_SHA256); System.out.println("=== Digital Signature Demo ==="); System.out.println("Original message: " + message); System.out.println("Signature algorithm: " + signatureResult.getAlgorithm()); System.out.println("Signing time: " + signatureResult.getSigningTimeMs() + " ms"); System.out.println("Signature (Base64): " + Base64.getEncoder().encodeToString(signatureResult.getSignature())); // Verify the signature DigitalSignatureService.VerificationResult verificationResult = service.verifySignature(signatureResult, keyPair.getPublic()); System.out.println("\n=== Verification Results ==="); System.out.println("Signature valid: " + verificationResult.isValid()); System.out.println("Verification time: " + verificationResult.getVerificationTimeMs() + " ms"); System.out.println("Data size: " + verificationResult.getDataSize() + " bytes"); System.out.println("Signature size: " + verificationResult.getSignatureSize() + " bytes"); // Test with different algorithms testDifferentAlgorithms(service); // Test key serialization testKeySerialization(service); } private static void testDifferentAlgorithms(DigitalSignatureService service) throws Exception { System.out.println("\n=== Algorithm Comparison ==="); String testData = "Test data for algorithm comparison"; byte[] data = testData.getBytes(); for (DigitalSignatureService.SignatureAlgorithm algorithm : DigitalSignatureService.SignatureAlgorithm.values()) { try { KeyPair keyPair = service.generateKeyPair(algorithm, 2048); long startTime = System.currentTimeMillis(); DigitalSignatureService.SignatureResult result = service.signData(data, keyPair.getPrivate(), algorithm); long signTime = System.currentTimeMillis() - startTime; startTime = System.currentTimeMillis(); boolean valid = service.verifySignature(result, keyPair.getPublic()).isValid(); long verifyTime = System.currentTimeMillis() - startTime; System.out.printf("%-15s | Sign: %4d ms | Verify: %4d ms | Valid: %b%n", algorithm, signTime, verifyTime, valid); } catch (Exception e) { System.out.printf("%-15s | Failed: %s%n", algorithm, e.getMessage()); } } } private static void testKeySerialization(DigitalSignatureService service) throws Exception { System.out.println("\n=== Key Serialization Test ==="); KeyPair keyPair = service.generateKeyPair( DigitalSignatureService.SignatureAlgorithm.RSA_SHA256, 2048); // Serialize keys String publicKeyStr = service.publicKeyToString(keyPair.getPublic()); String privateKeyStr = service.privateKeyToString(keyPair.getPrivate()); System.out.println("Public key length: " + publicKeyStr.length() + " chars"); System.out.println("Private key length: " + privateKeyStr.length() + " chars"); // Deserialize keys PublicKey restoredPublicKey = service.publicKeyFromString(publicKeyStr, "RSA"); PrivateKey restoredPrivateKey = service.privateKeyFromString(privateKeyStr, "RSA"); // Test with restored keys String testMessage = "Test message for serialization"; byte[] testData = testMessage.getBytes(); DigitalSignatureService.SignatureResult result = service.signData(testData, restoredPrivateKey, DigitalSignatureService.SignatureAlgorithm.RSA_SHA256); boolean valid = service.verifySignature(result, restoredPublicKey).isValid(); System.out.println("Signature with restored keys valid: " + valid); } } File Signing and Verification
import java.io.*; import java.nio.file.*; import java.security.*; import java.util.Base64; public class FileSignatureService { private final DigitalSignatureService signatureService; public FileSignatureService() { this.signatureService = new DigitalSignatureService(); } /** * Signs a file and creates a signature file */ public void signFile(Path filePath, PrivateKey privateKey, DigitalSignatureService.SignatureAlgorithm algorithm, Path signatureFilePath) throws Exception { try (InputStream fileStream = Files.newInputStream(filePath)) { byte[] signature = signatureService.signLargeData( fileStream, privateKey, algorithm, 8192); // Write signature to file String signatureBase64 = Base64.getEncoder().encodeToString(signature); Files.write(signatureFilePath, signatureBase64.getBytes()); System.out.println("File signed successfully: " + filePath); System.out.println("Signature saved to: " + signatureFilePath); } } /** * Verifies a file against its signature */ public boolean verifyFile(Path filePath, PublicKey publicKey, DigitalSignatureService.SignatureAlgorithm algorithm, Path signatureFilePath) throws Exception { // Read signature from file byte[] signatureBytes = Files.readAllBytes(signatureFilePath); byte[] signature = Base64.getDecoder().decode(signatureBytes); try (InputStream fileStream = Files.newInputStream(filePath)) { boolean isValid = signatureService.verifyLargeData( fileStream, signature, publicKey, algorithm, 8192); System.out.println("File verification result: " + (isValid ? "VALID" : "INVALID")); return isValid; } } /** * Creates a signed package with metadata */ public void createSignedPackage(Path filePath, PrivateKey privateKey, DigitalSignatureService.SignatureAlgorithm algorithm, Path packagePath) throws Exception { // Read file data byte[] fileData = Files.readAllBytes(filePath); String fileName = filePath.getFileName().toString(); long fileSize = Files.size(filePath); long timestamp = System.currentTimeMillis(); // Create package metadata SignedPackage signedPackage = new SignedPackage( fileName, fileData, fileSize, timestamp, algorithm ); // Sign the package signedPackage.sign(privateKey, signatureService); // Serialize and save package byte[] packageData = signedPackage.serialize(); Files.write(packagePath, packageData); System.out.println("Signed package created: " + packagePath); } /** * Verifies and extracts a signed package */ public boolean verifyAndExtractPackage(Path packagePath, PublicKey publicKey, Path outputDir) throws Exception { // Read package data byte[] packageData = Files.readAllBytes(packagePath); SignedPackage signedPackage = SignedPackage.deserialize(packageData); // Verify signature boolean isValid = signedPackage.verify(publicKey, signatureService); if (isValid) { // Extract file Path outputPath = outputDir.resolve(signedPackage.getFileName()); Files.write(outputPath, signedPackage.getFileData()); System.out.println("Package verified and extracted to: " + outputPath); } else { System.out.println("Package verification FAILED"); } return isValid; } /** * Signed package class */ public static class SignedPackage implements Serializable { private static final long serialVersionUID = 1L; private final String fileName; private final byte[] fileData; private final long fileSize; private final long timestamp; private final DigitalSignatureService.SignatureAlgorithm algorithm; private byte[] signature; public SignedPackage(String fileName, byte[] fileData, long fileSize, long timestamp, DigitalSignatureService.SignatureAlgorithm algorithm) { this.fileName = fileName; this.fileData = fileData; this.fileSize = fileSize; this.timestamp = timestamp; this.algorithm = algorithm; } public void sign(PrivateKey privateKey, DigitalSignatureService signatureService) throws Exception { byte[] dataToSign = getDataForSigning(); DigitalSignatureService.SignatureResult result = signatureService.signData(dataToSign, privateKey, algorithm); this.signature = result.getSignature(); } public boolean verify(PublicKey publicKey, DigitalSignatureService signatureService) throws Exception { byte[] dataToVerify = getDataForSigning(); return signatureService.verifySignature( new DigitalSignatureService.SignatureResult( dataToVerify, signature, algorithm, 0), publicKey).isValid(); } private byte[] getDataForSigning() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF(fileName); dos.writeLong(fileSize); dos.writeLong(timestamp); dos.writeUTF(algorithm.name()); dos.write(fileData); return baos.toByteArray(); } public byte[] serialize() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); return baos.toByteArray(); } public static SignedPackage deserialize(byte[] data) throws IOException, ClassNotFoundException { ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais); return (SignedPackage) ois.readObject(); } // Getters public String getFileName() { return fileName; } public byte[] getFileData() { return fileData; } public long getFileSize() { return fileSize; } public long getTimestamp() { return timestamp; } public DigitalSignatureService.SignatureAlgorithm getAlgorithm() { return algorithm; } public byte[] getSignature() { return signature; } } } Certificate-Based Signatures
import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.Base64; public class CertificateBasedSignature { /** * Loads a KeyStore and retrieves keys */ public static KeyStore loadKeyStore(Path keystorePath, String password, String type) throws Exception { KeyStore keyStore = KeyStore.getInstance(type); try (InputStream is = Files.newInputStream(keystorePath)) { keyStore.load(is, password.toCharArray()); } return keyStore; } /** * Signs data using a certificate from a KeyStore */ public static byte[] signWithCertificate(byte[] data, KeyStore keyStore, String alias, String keyPassword) throws Exception { PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, keyPassword.toCharArray()); Certificate cert = keyStore.getCertificate(alias); if (privateKey == null) { throw new IllegalArgumentException("Private key not found for alias: " + alias); } // Use the algorithm from the certificate String algorithm = getSignatureAlgorithm(cert); Signature signature = Signature.getInstance(algorithm); signature.initSign(privateKey); signature.update(data); return signature.sign(); } /** * Verifies signature using a certificate */ public static boolean verifyWithCertificate(byte[] data, byte[] signatureBytes, Certificate certificate) throws Exception { String algorithm = getSignatureAlgorithm(certificate); Signature signature = Signature.getInstance(algorithm); signature.initVerify(certificate); signature.update(data); return signature.verify(signatureBytes); } /** * Determines the appropriate signature algorithm from certificate */ private static String getSignatureAlgorithm(Certificate cert) { if (cert instanceof X509Certificate) { X509Certificate x509Cert = (X509Certificate) cert; String sigAlg = x509Cert.getSigAlgName(); // Map common algorithm names to standard ones if (sigAlg.contains("SHA256") && sigAlg.contains("RSA")) { return "SHA256withRSA"; } else if (sigAlg.contains("SHA512") && sigAlg.contains("RSA")) { return "SHA512withRSA"; } else if (sigAlg.contains("SHA256") && sigAlg.contains("ECDSA")) { return "SHA256withECDSA"; } } // Default fallback return "SHA256withRSA"; } /** * Creates a self-signed certificate (for testing) */ public static void createSelfSignedCertificate(Path keystorePath, String password, String alias, String dn) throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); // For production, use proper certificate generation // This is a simplified version for testing java.security.cert.Certificate[] chain = new java.security.cert.Certificate[1]; chain[0] = generateSelfSignedCertificate(keyPair, dn); KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(null, null); keyStore.setKeyEntry(alias, keyPair.getPrivate(), password.toCharArray(), chain); try (OutputStream os = Files.newOutputStream(keystorePath)) { keyStore.store(os, password.toCharArray()); } } /** * Generates a simple self-signed certificate (for testing only) */ private static java.security.cert.Certificate generateSelfSignedCertificate( KeyPair keyPair, String dn) throws Exception { // In production, use proper certificate generation libraries // This is a placeholder for demonstration return null; // Implementation would go here } /** * Demonstrates certificate-based signing */ public static void main(String[] args) throws Exception { // Create a test keystore (in real scenario, use properly issued certificates) Path keystorePath = Path.of("test-keystore.p12"); String password = "password"; String alias = "test-cert"; createSelfSignedCertificate(keystorePath, password, alias, "CN=Test"); // Load keystore KeyStore keyStore = loadKeyStore(keystorePath, password, "PKCS12"); // Sign data String message = "Important document content"; byte[] data = message.getBytes(); byte[] signature = signWithCertificate(data, keyStore, alias, password); System.out.println("Certificate-based signature created"); System.out.println("Signature: " + Base64.getEncoder().encodeToString(signature)); // Verify signature Certificate cert = keyStore.getCertificate(alias); boolean isValid = verifyWithCertificate(data, signature, cert); System.out.println("Signature valid: " + isValid); // Clean up Files.deleteIfExists(keystorePath); } } Advanced Features and Best Practices
Timestamping Service
import java.security.*; import java.util.Base64; public class TimestampingService { /** * Creates a timestamped signature */ public static TimestampedSignature createTimestampedSignature(byte[] data, PrivateKey privateKey, String algorithm) throws Exception { long timestamp = System.currentTimeMillis(); // Create data with timestamp byte[] dataWithTimestamp = addTimestampToData(data, timestamp); // Sign the timestamped data Signature signature = Signature.getInstance(algorithm); signature.initSign(privateKey); signature.update(dataWithTimestamp); byte[] signatureBytes = signature.sign(); return new TimestampedSignature(data, signatureBytes, timestamp, algorithm); } /** * Verifies a timestamped signature */ public static boolean verifyTimestampedSignature(TimestampedSignature tsSignature, PublicKey publicKey) throws Exception { // Recreate the timestamped data byte[] dataWithTimestamp = addTimestampToData( tsSignature.getOriginalData(), tsSignature.getTimestamp()); // Verify signature Signature signature = Signature.getInstance(tsSignature.getAlgorithm()); signature.initVerify(publicKey); signature.update(dataWithTimestamp); return signature.verify(tsSignature.getSignature()); } private static byte[] addTimestampToData(byte[] data, long timestamp) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); try { dos.writeLong(timestamp); dos.write(data); } catch (IOException e) { throw new RuntimeException("Failed to add timestamp to data", e); } return baos.toByteArray(); } public static class TimestampedSignature { private final byte[] originalData; private final byte[] signature; private final long timestamp; private final String algorithm; public TimestampedSignature(byte[] originalData, byte[] signature, long timestamp, String algorithm) { this.originalData = originalData; this.signature = signature; this.timestamp = timestamp; this.algorithm = algorithm; } // Getters public byte[] getOriginalData() { return originalData; } public byte[] getSignature() { return signature; } public long getTimestamp() { return timestamp; } public String getAlgorithm() { return algorithm; } public String getSignatureBase64() { return Base64.getEncoder().encodeToString(signature); } } } Performance Optimization and Caching
import java.security.*; import java.util.concurrent.*; public class OptimizedSignatureService { private final ConcurrentHashMap<String, KeyPair> keyPairCache; private final ExecutorService executorService; private final DigitalSignatureService signatureService; public OptimizedSignatureService() { this.keyPairCache = new ConcurrentHashMap<>(); this.executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); this.signatureService = new DigitalSignatureService(); } /** * Asynchronous signing */ public CompletableFuture<byte[]> signAsync(byte[] data, PrivateKey privateKey, DigitalSignatureService.SignatureAlgorithm algorithm) { return CompletableFuture.supplyAsync(() -> { try { return signatureService.signData(data, privateKey, algorithm).getSignature(); } catch (Exception e) { throw new CompletionException(e); } }, executorService); } /** * Asynchronous verification */ public CompletableFuture<Boolean> verifyAsync(byte[] data, byte[] signature, PublicKey publicKey, DigitalSignatureService.SignatureAlgorithm algorithm) { return CompletableFuture.supplyAsync(() -> { try { DigitalSignatureService.SignatureResult result = new DigitalSignatureService.SignatureResult(data, signature, algorithm, 0); return signatureService.verifySignature(result, publicKey).isValid(); } catch (Exception e) { throw new CompletionException(e); } }, executorService); } /** * Batch signing */ public CompletableFuture<Void> signBatch(List<byte[]> dataList, PrivateKey privateKey, DigitalSignatureService.SignatureAlgorithm algorithm) { List<CompletableFuture<byte[]>> futures = dataList.stream() .map(data -> signAsync(data, privateKey, algorithm)) .collect(Collectors.toList()); return CompletableFuture.allOf( futures.toArray(new CompletableFuture[0]) ); } /** * Cached key pair generation */ public KeyPair getCachedKeyPair(String cacheKey, DigitalSignatureService.SignatureAlgorithm algorithm, int keySize) throws Exception { return keyPairCache.computeIfAbsent(cacheKey, k -> { try { return signatureService.generateKeyPair(algorithm, keySize); } catch (Exception e) { throw new RuntimeException("Failed to generate key pair", e); } }); } /** * Shutdown the service */ public void shutdown() { executorService.shutdown(); try { if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } } } Security Best Practices
import java.security.*; import javax.crypto.spec.PBEKeySpec; import javax.crypto.SecretKeyFactory; public class SecurityBestPractices { /** * Secure key generation with proper entropy */ public static KeyPair generateSecureKeyPair(String algorithm, int keySize) throws NoSuchAlgorithmException { SecureRandom secureRandom = new SecureRandom(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm); keyGen.initialize(keySize, secureRandom); return keyGen.generateKeyPair(); } /** * Derives encryption key from password for key protection */ public static byte[] deriveKeyFromPassword(String password, byte[] salt, int iterations, int keyLength) throws Exception { PBEKeySpec spec = new PBEKeySpec( password.toCharArray(), salt, iterations, keyLength); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); return skf.generateSecret(spec).getEncoded(); } /** * Securely wipe sensitive data from memory */ public static void secureWipe(byte[] sensitiveData) { if (sensitiveData != null) { SecureRandom random = new SecureRandom(); random.nextBytes(sensitiveData); // Overwrite with random data } } /** * Secure key storage recommendations */ public static class SecureKeyStorage { // Use Hardware Security Modules (HSM) for production // Use KeyStore with strong passwords // Consider key rotation policies // Implement proper access controls public static void storeKeySecurely(Key key, String alias, Path keystorePath, char[] password) throws Exception { KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(null, null); // Use protection parameter for extra security KeyStore.ProtectionParameter protectionParam = new KeyStore.PasswordProtection(password); KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry( (javax.crypto.SecretKey) key); keyStore.setEntry(alias, secretKeyEntry, protectionParam); try (OutputStream os = Files.newOutputStream(keystorePath)) { keyStore.store(os, password); } } } /** * Input validation for signature operations */ public static void validateSignatureInputs(byte[] data, Key key, String algorithm) { if (data == null || data.length == 0) { throw new IllegalArgumentException("Data cannot be null or empty"); } if (key == null) { throw new IllegalArgumentException("Key cannot be null"); } if (algorithm == null || algorithm.trim().isEmpty()) { throw new IllegalArgumentException("Algorithm cannot be null or empty"); } // Check for weak algorithms if (isWeakAlgorithm(algorithm)) { throw new IllegalArgumentException("Weak algorithm not allowed: " + algorithm); } } private static boolean isWeakAlgorithm(String algorithm) { String[] weakAlgorithms = { "MD2withRSA", "MD5withRSA", "SHA1withRSA", "SHA1withDSA", "SHA1withECDSA" }; for (String weak : weakAlgorithms) { if (weak.equalsIgnoreCase(algorithm)) { return true; } } return false; } } Complete Example: Document Signing Application
```java
import java.security.*;
import java.util.Base64;
public class DocumentSigningApplication {
private final DigitalSignatureService signatureService; private final FileSignatureService fileService; private KeyPair keyPair; public DocumentSigningApplication() { this.signatureService = new DigitalSignatureService(); this.fileService = new FileSignatureService(); } public void initialize() throws Exception { // Generate or load keys this.keyPair = signatureService.generateKeyPair( DigitalSignatureService.SignatureAlgorithm.RSA_SHA256, 2048); System.out.println("Document Signing Application Initialized"); System.out.println("Public Key: " + Base64.getEncoder().encodeToString