Learn how to implement and use homomorphic encryption in Java to perform computations on encrypted data without decryption.
Table of Contents
- Homomorphic Encryption Overview
- Libraries & Dependencies
- Basic Homomorphic Operations
- Microsoft SEAL Integration
- Practical Use Cases
- Performance Optimization
- Security Considerations
Homomorphic Encryption Overview
What is Homomorphic Encryption?
Homomorphic encryption allows computations to be performed on ciphertexts, generating an encrypted result that, when decrypted, matches the result of operations performed on the plaintext.
Types of Homomorphic Encryption:
- Partially Homomorphic Encryption (PHE): Supports one operation (addition OR multiplication)
- Somewhat Homomorphic Encryption (SHE): Supports limited operations
- Fully Homomorphic Encryption (FHE): Supports unlimited operations
Libraries & Dependencies
Maven Configuration
<properties> <jna.version>5.13.0</jna.version> <commons-math3.version>3.6.1</commons-math3.version> <bouncycastle.version>1.75</bouncycastle.version> </properties> <dependencies> <!-- Microsoft SEAL Java Bindings --> <dependency> <groupId>com.microsoft.seal</groupId> <artifactId>seal-java</artifactId> <version>4.1.1</version> </dependency> <!-- JNA for native integration --> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>${jna.version}</version> </dependency> <!-- Math utilities --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-math3</artifactId> <version>${commons-math3.version}</version> </dependency> <!-- Cryptography utilities --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>${bouncycastle.version}</version> </dependency> <!-- JSON serialization --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> </dependencies> Gradle Configuration
dependencies { implementation 'com.microsoft.seal:seal-java:4.1.1' implementation 'net.java.dev.jna:jna:5.13.0' implementation 'org.apache.commons:commons-math3:3.6.1' implementation 'org.bouncycastle:bcprov-jdk18on:1.75' implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' } Basic Homomorphic Operations
1. Homomorphic Encryption Service Interface
public interface HomomorphicEncryptionService { // Key generation KeyPair generateKeyPair(); // Encryption byte[] encrypt(int value); byte[] encrypt(long value); byte[] encrypt(double value); // Decryption int decryptInt(byte[] ciphertext); long decryptLong(byte[] ciphertext); double decryptDouble(byte[] ciphertext); // Homomorphic operations byte[] add(byte[] ciphertext1, byte[] ciphertext2); byte[] add(byte[] ciphertext, int plaintext); byte[] multiply(byte[] ciphertext1, byte[] ciphertext2); byte[] multiply(byte[] ciphertext, int plaintext); // Serialization byte[] serializeCiphertext(byte[] ciphertext); byte[] deserializeCiphertext(byte[] data); // Key management byte[] serializePublicKey(); byte[] serializePrivateKey(); void loadPublicKey(byte[] publicKeyData); void loadPrivateKey(byte[] privateKeyData); } 2. Paillier Homomorphic Encryption Implementation
@Service public class PaillierHomomorphicEncryption implements HomomorphicEncryptionService { private static final Logger logger = LoggerFactory.getLogger(PaillierHomomorphicEncryption.class); private static final int DEFAULT_KEY_SIZE = 2048; private final SecureRandom random; private BigInteger n; // modulus n = p * q private BigInteger nsquare; // n^2 private BigInteger g; // generator private BigInteger lambda; // λ = lcm(p-1, q-1) private BigInteger mu; // μ = (L(g^λ mod n^2))^{-1} mod n private boolean keysGenerated = false; public PaillierHomomorphicEncryption() { this.random = new SecureRandom(); } @Override public KeyPair generateKeyPair() { return generateKeyPair(DEFAULT_KEY_SIZE); } public KeyPair generateKeyPair(int keySize) { try { // Generate two large prime numbers BigInteger p = generatePrime(keySize / 2); BigInteger q = generatePrime(keySize / 2); // Ensure p and q are different while (p.equals(q)) { q = generatePrime(keySize / 2); } // Calculate n = p * q this.n = p.multiply(q); this.nsquare = n.multiply(n); // Calculate λ = lcm(p-1, q-1) BigInteger pMinusOne = p.subtract(BigInteger.ONE); BigInteger qMinusOne = q.subtract(BigInteger.ONE); this.lambda = lcm(pMinusOne, qMinusOne); // Choose generator g = n + 1 this.g = n.add(BigInteger.ONE); // Calculate μ = (L(g^λ mod n^2))^{-1} mod n BigInteger gLambda = g.modPow(lambda, nsquare); BigInteger l = lFunction(gLambda); this.mu = l.modInverse(n); this.keysGenerated = true; logger.info("Paillier key pair generated with key size: {}", keySize); return new KeyPair( new PublicKey(n.toByteArray(), g.toByteArray()), new PrivateKey(lambda.toByteArray(), mu.toByteArray()) ); } catch (Exception e) { logger.error("Failed to generate Paillier key pair", e); throw new KeyGenerationException("Paillier key generation failed", e); } } @Override public byte[] encrypt(int value) { return encrypt(BigInteger.valueOf(value)); } @Override public byte[] encrypt(long value) { return encrypt(BigInteger.valueOf(value)); } @Override public byte[] encrypt(double value) { // For floating-point numbers, use fixed-point arithmetic long scaledValue = (long) (value * 1000); // Scale by 1000 for 3 decimal places return encrypt(scaledValue); } private byte[] encrypt(BigInteger plaintext) { validateKeys(); try { // Ensure plaintext is in range [0, n-1] if (plaintext.compareTo(n) >= 0) { throw new IllegalArgumentException("Plaintext too large for encryption"); } // Choose random r where 0 < r < n and gcd(r, n) = 1 BigInteger r; do { r = new BigInteger(n.bitLength(), random); } while (r.compareTo(BigInteger.ZERO) <= 0 || r.compareTo(n) >= 0 || !r.gcd(n).equals(BigInteger.ONE)); // Encryption: c = g^m * r^n mod n^2 BigInteger gm = g.modPow(plaintext, nsquare); BigInteger rn = r.modPow(n, nsquare); BigInteger ciphertext = gm.multiply(rn).mod(nsquare); return ciphertext.toByteArray(); } catch (Exception e) { logger.error("Encryption failed", e); throw new EncryptionException("Paillier encryption failed", e); } } @Override public int decryptInt(byte[] ciphertext) { BigInteger plaintext = decrypt(ciphertext); return plaintext.intValue(); } @Override public long decryptLong(byte[] ciphertext) { BigInteger plaintext = decrypt(ciphertext); return plaintext.longValue(); } @Override public double decryptDouble(byte[] ciphertext) { long scaledValue = decryptLong(ciphertext); return scaledValue / 1000.0; // Reverse scaling } private BigInteger decrypt(byte[] ciphertext) { validateKeys(); try { BigInteger c = new BigInteger(ciphertext); // Decryption: m = L(c^λ mod n^2) * μ mod n BigInteger cLambda = c.modPow(lambda, nsquare); BigInteger l = lFunction(cLambda); BigInteger plaintext = l.multiply(mu).mod(n); return plaintext; } catch (Exception e) { logger.error("Decryption failed", e); throw new DecryptionException("Paillier decryption failed", e); } } @Override public byte[] add(byte[] ciphertext1, byte[] ciphertext2) { validateKeys(); try { BigInteger c1 = new BigInteger(ciphertext1); BigInteger c2 = new BigInteger(ciphertext2); // Homomorphic addition: c1 * c2 mod n^2 BigInteger result = c1.multiply(c2).mod(nsquare); return result.toByteArray(); } catch (Exception e) { logger.error("Homomorphic addition failed", e); throw new HomomorphicOperationException("Addition operation failed", e); } } @Override public byte[] add(byte[] ciphertext, int plaintext) { validateKeys(); try { BigInteger c = new BigInteger(ciphertext); BigInteger m = BigInteger.valueOf(plaintext); // Homomorphic addition with plaintext: c * g^m mod n^2 BigInteger gm = g.modPow(m, nsquare); BigInteger result = c.multiply(gm).mod(nsquare); return result.toByteArray(); } catch (Exception e) { logger.error("Homomorphic addition with plaintext failed", e); throw new HomomorphicOperationException("Addition with plaintext failed", e); } } @Override public byte[] multiply(byte[] ciphertext, int plaintext) { validateKeys(); try { BigInteger c = new BigInteger(ciphertext); BigInteger k = BigInteger.valueOf(plaintext); // Homomorphic multiplication: c^k mod n^2 BigInteger result = c.modPow(k, nsquare); return result.toByteArray(); } catch (Exception e) { logger.error("Homomorphic multiplication failed", e); throw new HomomorphicOperationException("Multiplication operation failed", e); } } // Paillier doesn't support ciphertext-ciphertext multiplication directly @Override public byte[] multiply(byte[] ciphertext1, byte[] ciphertext2) { throw new UnsupportedOperationException( "Paillier encryption does not support ciphertext-ciphertext multiplication"); } // Helper methods private BigInteger generatePrime(int bitLength) { return BigInteger.probablePrime(bitLength, random); } private BigInteger lcm(BigInteger a, BigInteger b) { return a.multiply(b).divide(a.gcd(b)); } private BigInteger lFunction(BigInteger x) { // L(x) = (x - 1) / n return x.subtract(BigInteger.ONE).divide(n); } private void validateKeys() { if (!keysGenerated) { throw new IllegalStateException("Encryption keys not generated"); } } // Key serialization/deserialization @Override public byte[] serializePublicKey() { validateKeys(); Map<String, String> keyData = new HashMap<>(); keyData.put("n", n.toString()); keyData.put("g", g.toString()); return serializeKeyData(keyData); } @Override public byte[] serializePrivateKey() { validateKeys(); Map<String, String> keyData = new HashMap<>(); keyData.put("lambda", lambda.toString()); keyData.put("mu", mu.toString()); return serializeKeyData(keyData); } @Override public void loadPublicKey(byte[] publicKeyData) { try { Map<String, String> keyData = deserializeKeyData(publicKeyData); this.n = new BigInteger(keyData.get("n")); this.g = new BigInteger(keyData.get("g")); this.nsquare = n.multiply(n); this.keysGenerated = true; } catch (Exception e) { throw new KeyLoadException("Failed to load public key", e); } } @Override public void loadPrivateKey(byte[] privateKeyData) { try { Map<String, String> keyData = deserializeKeyData(privateKeyData); this.lambda = new BigInteger(keyData.get("lambda")); this.mu = new BigInteger(keyData.get("mu")); } catch (Exception e) { throw new KeyLoadException("Failed to load private key", e); } } private byte[] serializeKeyData(Map<String, String> keyData) { try { ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsBytes(keyData); } catch (Exception e) { throw new SerializationException("Key serialization failed", e); } } private Map<String, String> deserializeKeyData(byte[] data) { try { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(data, new TypeReference<Map<String, String>>() {}); } catch (Exception e) { throw new SerializationException("Key deserialization failed", e); } } @Override public byte[] serializeCiphertext(byte[] ciphertext) { return ciphertext; // Simple implementation } @Override public byte[] deserializeCiphertext(byte[] data) { return data; // Simple implementation } // Key pair classes public static class KeyPair { private final PublicKey publicKey; private final PrivateKey privateKey; public KeyPair(PublicKey publicKey, PrivateKey privateKey) { this.publicKey = publicKey; this.privateKey = privateKey; } public PublicKey getPublicKey() { return publicKey; } public PrivateKey getPrivateKey() { return privateKey; } } public static class PublicKey { private final byte[] n; private final byte[] g; public PublicKey(byte[] n, byte[] g) { this.n = n; this.g = g; } public byte[] getN() { return n; } public byte[] getG() { return g; } } public static class PrivateKey { private final byte[] lambda; private final byte[] mu; public PrivateKey(byte[] lambda, byte[] mu) { this.lambda = lambda; this.mu = mu; } public byte[] getLambda() { return lambda; } public byte[] getMu() { return mu; } } } Microsoft SEAL Integration
3. SEAL Homomorphic Encryption Service
@Service @ConditionalOnClass(name = "com.microsoft.seal.SEALContext") public class SealHomomorphicEncryption implements HomomorphicEncryptionService { private static final Logger logger = LoggerFactory.getLogger(SealHomomorphicEncryption.class); private SEALContext context; private KeyGenerator keyGenerator; private Encryptor encryptor; private Decryptor decryptor; private Evaluator evaluator; private IntegerEncoder encoder; private PublicKey publicKey; private SecretKey secretKey; public SealHomomorphicEncryption() { initializeSEAL(); } private void initializeSEAL() { try { // Create encryption parameters EncryptionParameters params = new EncryptionParameters(SchemeType.BFV); // Set polynomial modulus degree (power of 2) int polyModulusDegree = 4096; params.setPolyModulusDegree(polyModulusDegree); // Set coefficient modulus params.setCoeffModulus(CoeffModulus.BFVDefault(polyModulusDegree)); // Set plaintext modulus params.setPlainModulus(PlainModulus.Batching(polyModulusDegree, 20)); // Create SEAL context this.context = SEALContext.create(params); // Create core objects this.keyGenerator = new KeyGenerator(context); this.encryptor = new Encryptor(context, keyGenerator.publicKey()); this.decryptor = new Decryptor(context, keyGenerator.secretKey()); this.evaluator = new Evaluator(context); this.encoder = new IntegerEncoder(context); // Store keys this.publicKey = keyGenerator.publicKey(); this.secretKey = keyGenerator.secretKey(); logger.info("Microsoft SEAL initialized with polynomial modulus degree: {}", polyModulusDegree); } catch (Exception e) { logger.error("Failed to initialize Microsoft SEAL", e); throw new SealInitializationException("SEAL initialization failed", e); } } @Override public KeyPair generateKeyPair() { // Keys are generated during initialization return new KeyPair( new PublicKey(publicKey.save().toByteArray(), null), new PrivateKey(secretKey.save().toByteArray(), null) ); } @Override public byte[] encrypt(int value) { try { Plaintext plaintext = encoder.encode(value); Ciphertext ciphertext = new Ciphertext(context); encryptor.encrypt(plaintext, ciphertext); return ciphertext.save().toByteArray(); } catch (Exception e) { logger.error("SEAL encryption failed for value: {}", value, e); throw new EncryptionException("SEAL encryption failed", e); } } @Override public byte[] encrypt(long value) { // For large values, you might need different parameters return encrypt((int) value); // Note: this may overflow for large longs } @Override public byte[] encrypt(double value) { try { // Scale double to integer for encoding long scaledValue = (long) (value * 1000); // 3 decimal places Plaintext plaintext = encoder.encode((int) scaledValue); Ciphertext ciphertext = new Ciphertext(context); encryptor.encrypt(plaintext, ciphertext); return ciphertext.save().toByteArray(); } catch (Exception e) { logger.error("SEAL encryption failed for double value: {}", value, e); throw new EncryptionException("SEAL encryption failed", e); } } @Override public int decryptInt(byte[] ciphertext) { try { Ciphertext encrypted = new Ciphertext(context); encrypted.load(context, new ByteArrayPool(ciphertext)); Plaintext plaintext = new Plaintext(); decryptor.decrypt(encrypted, plaintext); return encoder.decodeInt32(plaintext); } catch (Exception e) { logger.error("SEAL decryption failed", e); throw new DecryptionException("SEAL decryption failed", e); } } @Override public long decryptLong(byte[] ciphertext) { return decryptInt(ciphertext); // Note: SEAL with IntegerEncoder works with int32 } @Override public double decryptDouble(byte[] ciphertext) { int scaledValue = decryptInt(ciphertext); return scaledValue / 1000.0; } @Override public byte[] add(byte[] ciphertext1, byte[] ciphertext2) { try { Ciphertext encrypted1 = new Ciphertext(context); Ciphertext encrypted2 = new Ciphertext(context); encrypted1.load(context, new ByteArrayPool(ciphertext1)); encrypted2.load(context, new ByteArrayPool(ciphertext2)); Ciphertext result = new Ciphertext(context); evaluator.add(encrypted1, encrypted2, result); return result.save().toByteArray(); } catch (Exception e) { logger.error("SEAL homomorphic addition failed", e); throw new HomomorphicOperationException("SEAL addition failed", e); } } @Override public byte[] add(byte[] ciphertext, int plaintext) { try { Ciphertext encrypted = new Ciphertext(context); encrypted.load(context, new ByteArrayPool(ciphertext)); Plaintext plain = encoder.encode(plaintext); Ciphertext result = new Ciphertext(context); evaluator.addPlain(encrypted, plain, result); return result.save().toByteArray(); } catch (Exception e) { logger.error("SEAL homomorphic addition with plaintext failed", e); throw new HomomorphicOperationException("SEAL addition with plaintext failed", e); } } @Override public byte[] multiply(byte[] ciphertext1, byte[] ciphertext2) { try { Ciphertext encrypted1 = new Ciphertext(context); Ciphertext encrypted2 = new Ciphertext(context); encrypted1.load(context, new ByteArrayPool(ciphertext1)); encrypted2.load(context, new ByteArrayPool(ciphertext2)); Ciphertext result = new Ciphertext(context); evaluator.multiply(encrypted1, encrypted2, result); return result.save().toByteArray(); } catch (Exception e) { logger.error("SEAL homomorphic multiplication failed", e); throw new HomomorphicOperationException("SEAL multiplication failed", e); } } @Override public byte[] multiply(byte[] ciphertext, int plaintext) { try { Ciphertext encrypted = new Ciphertext(context); encrypted.load(context, new ByteArrayPool(ciphertext)); Plaintext plain = encoder.encode(plaintext); Ciphertext result = new Ciphertext(context); evaluator.multiplyPlain(encrypted, plain, result); return result.save().toByteArray(); } catch (Exception e) { logger.error("SEAL homomorphic multiplication with plaintext failed", e); throw new HomomorphicOperationException("SEAL multiplication with plaintext failed", e); } } // Additional SEAL-specific operations public byte[] subtract(byte[] ciphertext1, byte[] ciphertext2) { try { Ciphertext encrypted1 = new Ciphertext(context); Ciphertext encrypted2 = new Ciphertext(context); encrypted1.load(context, new ByteArrayPool(ciphertext1)); encrypted2.load(context, new ByteArrayPool(ciphertext2)); Ciphertext result = new Ciphertext(context); evaluator.sub(encrypted1, encrypted2, result); return result.save().toByteArray(); } catch (Exception e) { logger.error("SEAL homomorphic subtraction failed", e); throw new HomomorphicOperationException("SEAL subtraction failed", e); } } public byte[] square(byte[] ciphertext) { try { Ciphertext encrypted = new Ciphertext(context); encrypted.load(context, new ByteArrayPool(ciphertext)); Ciphertext result = new Ciphertext(context); evaluator.square(encrypted, result); return result.save().toByteArray(); } catch (Exception e) { logger.error("SEAL homomorphic square failed", e); throw new HomomorphicOperationException("SEAL square failed", e); } } // Serialization methods @Override public byte[] serializeCiphertext(byte[] ciphertext) { return ciphertext; // SEAL already provides serialization } @Override public byte[] deserializeCiphertext(byte[] data) { return data; // SEAL already provides deserialization } @Override public byte[] serializePublicKey() { return publicKey.save().toByteArray(); } @Override public byte[] serializePrivateKey() { return secretKey.save().toByteArray(); } @Override public void loadPublicKey(byte[] publicKeyData) { try { this.publicKey = new PublicKey(context, new ByteArrayPool(publicKeyData)); this.encryptor = new Encryptor(context, publicKey); } catch (Exception e) { throw new KeyLoadException("Failed to load SEAL public key", e); } } @Override public void loadPrivateKey(byte[] privateKeyData) { try { this.secretKey = new SecretKey(context, new ByteArrayPool(privateKeyData)); this.decryptor = new Decryptor(context, secretKey); } catch (Exception e) { throw new KeyLoadException("Failed to load SEAL private key", e); } } @PreDestroy public void cleanup() { // SEAL objects need to be explicitly destroyed to free native memory if (encoder != null) encoder.close(); if (evaluator != null) evaluator.close(); if (decryptor != null) decryptor.close(); if (encryptor != null) encryptor.close(); if (keyGenerator != null) keyGenerator.close(); if (context != null) context.close(); logger.info("SEAL resources cleaned up"); } } Practical Use Cases
4. Secure Voting System
@Service public class SecureVotingService { private static final Logger logger = LoggerFactory.getLogger(SecureVotingService.class); private final HomomorphicEncryptionService encryptionService; private final Map<String, byte[]> encryptedVotes = new ConcurrentHashMap<>(); public SecureVotingService(HomomorphicEncryptionService encryptionService) { this.encryptionService = encryptionService; } /** * Cast an encrypted vote */ public String castVote(String voterId, int candidateId) { try { // Encode vote: 1 for candidate, 0 for others byte[] encryptedVote = encryptionService.encrypt(candidateId); encryptedVotes.put(voterId, encryptedVote); logger.info("Vote cast by voter: {} for candidate: {}", voterId, candidateId); return "VOTE_" + voterId + "_" + System.currentTimeMillis(); } catch (Exception e) { logger.error("Failed to cast vote for voter: {}", voterId, e); throw new VotingException("Vote casting failed", e); } } /** * Tally votes without decrypting individual votes */ public Map<Integer, Integer> tallyVotes() { try { if (encryptedVotes.isEmpty()) { return Collections.emptyMap(); } // Start with encrypted zero byte[] total = encryptionService.encrypt(0); // Homomorphically add all votes for (byte[] encryptedVote : encryptedVotes.values()) { total = encryptionService.add(total, encryptedVote); } // Decrypt the total int totalVotes = encryptionService.decryptInt(total); // In a real system, you'd have multiple candidates // This is a simplified example with one candidate Map<Integer, Integer> results = new HashMap<>(); results.put(1, totalVotes); // Assuming candidate ID 1 logger.info("Vote tally completed. Total votes: {}", totalVotes); return results; } catch (Exception e) { logger.error("Vote tally failed", e); throw new VotingException("Vote tally failed", e); } } /** * Verify individual vote (in real system, this would use zero-knowledge proofs) */ public boolean verifyVote(String voterId, int expectedCandidate) { try { byte[] encryptedVote = encryptedVotes.get(voterId); if (encryptedVote == null) { return false; } int actualCandidate = encryptionService.decryptInt(encryptedVote); return actualCandidate == expectedCandidate; } catch (Exception e) { logger.error("Vote verification failed for voter: {}", voterId, e); return false; } } } 5. Privacy-Preserving Analytics
@Service public class PrivacyPreservingAnalytics { private static final Logger logger = LoggerFactory.getLogger(PrivacyPreservingAnalytics.class); private final HomomorphicEncryptionService encryptionService; public PrivacyPreservingAnalytics(HomomorphicEncryptionService encryptionService) { this.encryptionService = encryptionService; } /** * Compute average of encrypted values without decrypting individual values */ public double computeAverage(List<byte[]> encryptedValues) { try { if (encryptedValues.isEmpty()) { return 0.0; } // Start with encrypted zero byte[] sum = encryptionService.encrypt(0); // Homomorphically add all values for (byte[] encryptedValue : encryptedValues) { sum = encryptionService.add(sum, encryptedValue); } // Decrypt the sum int total = encryptionService.decryptInt(sum); // Compute average double average = (double) total / encryptedValues.size(); logger.info("Computed average: {} from {} values", average, encryptedValues.size()); return average; } catch (Exception e) { logger.error("Average computation failed", e); throw new AnalyticsException("Average computation failed", e); } } /** * Compute variance of encrypted values */ public double computeVariance(List<byte[]> encryptedValues) { try { if (encryptedValues.size() < 2) { return 0.0; } double mean = computeAverage(encryptedValues); // Compute sum of squares byte[] sumSquares = encryptionService.encrypt(0); for (byte[] encryptedValue : encryptedValues) { // For each value: (x - mean)^2 // Since we can't subtract mean homomorphically, we'll compute differently // This is a simplified approach byte[] squared = encryptionService.multiply(encryptedValue, encryptionService.decryptInt(encryptedValue)); sumSquares = encryptionService.add(sumSquares, squared); } int totalSquares = encryptionService.decryptInt(sumSquares); double variance = (totalSquares / (double) encryptedValues.size()) - (mean * mean); logger.info("Computed variance: {} from {} values", variance, encryptedValues.size()); return Math.max(variance, 0.0); // Variance can't be negative } catch (Exception e) { logger.error("Variance computation failed", e); throw new AnalyticsException("Variance computation failed", e); } } /** * Perform secure data aggregation from multiple sources */ public AggregationResult aggregateData(Map<String, List<byte[]>> encryptedDataBySource) { try { Map<String, Integer> sourceCounts = new HashMap<>(); Map<String, Double> sourceAverages = new HashMap<>(); byte[] globalSum = encryptionService.encrypt(0); int totalValues = 0; // Process each data source for (Map.Entry<String, List<byte[]>> entry : encryptedDataBySource.entrySet()) { String source = entry.getKey(); List<byte[]> values = entry.getValue(); // Compute source sum byte[] sourceSum = encryptionService.encrypt(0); for (byte[] value : values) { sourceSum = encryptionService.add(sourceSum, value); } // Add to global sum globalSum = encryptionService.add(globalSum, sourceSum); // Store source statistics sourceCounts.put(source, values.size()); int sourceTotal = encryptionService.decryptInt(sourceSum); sourceAverages.put(source, (double) sourceTotal / values.size()); totalValues += values.size(); } // Compute global statistics int globalTotal = encryptionService.decryptInt(globalSum); double globalAverage = (double) globalTotal / totalValues; return AggregationResult.builder() .sourceCounts(sourceCounts) .sourceAverages(sourceAverages) .globalTotal(globalTotal) .globalAverage(globalAverage) .totalValues(totalValues) .build(); } catch (Exception e) { logger.error("Data aggregation failed", e); throw new AnalyticsException("Data aggregation failed", e); } } @Data @Builder public static class AggregationResult { private Map<String, Integer> sourceCounts; private Map<String, Double> sourceAverages; private int globalTotal; private double globalAverage; private int totalValues; } } Performance Optimization
6. Caching and Batch Operations
@Service public class OptimizedHomomorphicService { private static final Logger logger = LoggerFactory.getLogger(OptimizedHomomorphicService.class); private final HomomorphicEncryptionService encryptionService; private final Cache<String, byte[]> encryptionCache; private final ExecutorService batchExecutor; public OptimizedHomomorphicService(HomomorphicEncryptionService encryptionService) { this.encryptionService = encryptionService; this.encryptionCache = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(1, TimeUnit.HOURS) .build(); this.batchExecutor = Executors.newFixedThreadPool(4); } /** * Batch encrypt multiple values */ public List<byte[]> batchEncrypt(List<Integer> values) { try { List<Callable<byte[]>> tasks = values.stream() .map(value -> (Callable<byte[]>) () -> encryptWithCache(value)) .collect(Collectors.toList()); List<Future<byte[]>> futures = batchExecutor.invokeAll(tasks); List<byte[]> results = new ArrayList<>(); for (Future<byte[]> future : futures) { results.add(future.get()); } return results; } catch (Exception e) { logger.error("Batch encryption failed", e); throw new EncryptionException("Batch encryption failed", e); } } /** * Batch homomorphic addition */ public byte[] batchAdd(List<byte[]> ciphertexts) { try { if (ciphertexts.isEmpty()) { return encryptionService.encrypt(0); } byte[] result = ciphertexts.get(0); for (int i = 1; i < ciphertexts.size(); i++) { result = encryptionService.add(result, ciphertexts.get(i)); } return result; } catch (Exception e) { logger.error("Batch addition failed", e); throw new HomomorphicOperationException("Batch addition failed", e); } } private byte[] encryptWithCache(int value) { String cacheKey = "encrypt_" + value; return encryptionCache.get(cacheKey, k -> encryptionService.encrypt(value)); } @PreDestroy public void cleanup() { batchExecutor.shutdown(); try { if (!batchExecutor.awaitTermination(5, TimeUnit.SECONDS)) { batchExecutor.shutdownNow(); } } catch (InterruptedException e) { batchExecutor.shutdownNow(); Thread.currentThread().interrupt(); } } } Security Considerations
7. Security Configuration and Best Practices
@Configuration @EnableConfigurationProperties(HomomorphicEncryptionProperties.class) public class SecurityConfig { @Bean @ConditionalOnProperty(name = "homomorphic.encryption.type", havingValue = "paillier") public HomomorphicEncryptionService paillierEncryptionService() { return new PaillierHomomorphicEncryption(); } @Bean @ConditionalOnProperty(name = "homomorphic.encryption.type", havingValue = "seal") public HomomorphicEncryptionService sealEncryptionService() { return new SealHomomorphicEncryption(); } @Bean public SecureRandom secureRandom() { return new SecureRandom(); } } @ConfigurationProperties(prefix = "homomorphic.encryption") @Data public class HomomorphicEncryptionProperties { private String type = "paillier"; // paillier, seal private int keySize = 2048; private boolean enableCaching = true; private int cacheSize = 1000; private SecurityLevel securityLevel = SecurityLevel.LEVEL_128; public enum SecurityLevel { LEVEL_128, LEVEL_192, LEVEL_256 } } This comprehensive implementation provides a robust foundation for homomorphic encryption in Java, covering both basic Paillier encryption and advanced Microsoft SEAL integration with practical use cases and performance optimizations.