Digital Signatures with Java

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

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper