google-site-verification: google61fe8ba583a51912.html
Microsoft SEAL in Java: Complete Guide to Homomorphic Encryption


Article

Microsoft SEAL is a homomorphic encryption library that allows computations on encrypted data without decrypting it first. This guide covers how to use SEAL in Java applications, including basic operations, advanced schemes, and practical implementations.

What is Homomorphic Encryption?

  • Fully Homomorphic Encryption (FHE): Perform any computation on encrypted data
  • Somewhat Homomorphic Encryption (SHE): Limited computation depth
  • BFV Scheme: For integer arithmetic
  • CKKS Scheme: For approximate real/complex number arithmetic

Project Setup and Dependencies

Maven Dependencies:

<properties> <seal-java.version>4.1.0</seal-java.version> <jna.version>5.13.0</jna.version> </properties> <dependencies> <!-- Microsoft SEAL Java Bindings --> <dependency> <groupId>com.microsoft.seal</groupId> <artifactId>seal-java</artifactId> <version>${seal-java.version}</version> </dependency> <!-- JNA for native library loading --> <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>${jna.version}</version> </dependency> <!-- JSON for serialization --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- Apache Commons for utilities --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> <!-- For big integer operations --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.77</version> </dependency> </dependencies> <!-- Repository for SEAL --> <repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>

1. Basic SEAL Setup and Configuration

SEAL Context and Key Management:

package com.example.seal.basics; import com.microsoft.seal.*; import java.util.Base64; public class SEALSetup { private EncryptionParameters encryptionParams; private SEALContext context; private KeyGenerator keyGenerator; private PublicKey publicKey; private SecretKey secretKey; private RelinKeys relinKeys; private GaloisKeys galoisKeys; private Encryptor encryptor; private Evaluator evaluator; private Decryptor decryptor; private BatchEncoder batchEncoder; private CKKSEncoder ckksEncoder; public SEALSetup(SchemeType scheme, int polyModulusDegree, long[] coeffModulus, long plainModulus) { // 1. Initialize encryption parameters based on scheme this.encryptionParams = new EncryptionParameters(scheme); // 2. Set polynomial modulus degree (power of 2, e.g., 4096, 8192, 16384) encryptionParams.setPolyModulusDegree(polyModulusDegree); // 3. Set coefficient modulus switch (scheme) { case bfv: // For BFV scheme encryptionParams.setCoeffModulus(CoeffModulus.BFVDefault(polyModulusDegree)); encryptionParams.setPlainModulus(PlainModulus.Batching(polyModulusDegree, 20)); break; case ckks: // For CKKS scheme encryptionParams.setCoeffModulus(CoeffModulus.Create( polyModulusDegree, new int[]{60, 40, 40, 60})); break; default: throw new IllegalArgumentException("Unsupported scheme type: " + scheme); } // 4. Create SEAL context this.context = new SEALContext(encryptionParams); // 5. Initialize key generator this.keyGenerator = new KeyGenerator(context); // 6. Generate keys this.publicKey = keyGenerator.createPublicKey(); this.secretKey = keyGenerator.secretKey(); // 7. Generate relinearization keys (for multiplication) this.relinKeys = keyGenerator.createRelinKeys(); // 8. Generate Galois keys (for rotation operations) this.galoisKeys = keyGenerator.createGaloisKeys(); // 9. Initialize encryptor, evaluator, decryptor this.encryptor = new Encryptor(context, publicKey); this.evaluator = new Evaluator(context); this.decryptor = new Decryptor(context, secretKey); // 10. Initialize encoders based on scheme if (scheme == SchemeType.bfv) { this.batchEncoder = new BatchEncoder(context); } else if (scheme == SchemeType.ckks) { this.ckksEncoder = new CKKSEncoder(context); } } public static class Builder { private SchemeType scheme = SchemeType.ckks; private int polyModulusDegree = 8192; private long plainModulus = 1032193; private long[] coeffModulus; public Builder withScheme(SchemeType scheme) { this.scheme = scheme; return this; } public Builder withPolyModulusDegree(int degree) { if ((degree & (degree - 1)) != 0 || degree < 1024) { throw new IllegalArgumentException("Poly modulus degree must be a power of 2 and >= 1024"); } this.polyModulusDegree = degree; return this; } public Builder withPlainModulus(long modulus) { this.plainModulus = modulus; return this; } public SEALSetup build() { return new SEALSetup(scheme, polyModulusDegree, coeffModulus, plainModulus); } } // Getters public EncryptionParameters getEncryptionParams() { return encryptionParams; } public SEALContext getContext() { return context; } public PublicKey getPublicKey() { return publicKey; } public SecretKey getSecretKey() { return secretKey; } public RelinKeys getRelinKeys() { return relinKeys; } public GaloisKeys getGaloisKeys() { return galoisKeys; } public Encryptor getEncryptor() { return encryptor; } public Evaluator getEvaluator() { return evaluator; } public Decryptor getDecryptor() { return decryptor; } public BatchEncoder getBatchEncoder() { return batchEncoder; } public CKKSEncoder getCkksEncoder() { return ckksEncoder; } // Serialization methods public String serializePublicKey() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); publicKey.save(outputStream); return Base64.getEncoder().encodeToString(outputStream.toByteArray()); } public PublicKey deserializePublicKey(String base64String) { byte[] data = Base64.getDecoder().decode(base64String); ByteArrayInputStream inputStream = new ByteArrayInputStream(data); PublicKey key = new PublicKey(); key.load(context, inputStream); return key; } // Parameter validation public void validateParameters() { if (!context.parametersSet()) { throw new IllegalStateException("Encryption parameters are not set correctly"); } if (context.firstContextData().qualifiers().usingBatching) { System.out.println("Batching enabled"); } if (context.firstContextData().qualifiers().usingFastPlainLift) { System.out.println("Fast plain lift enabled"); } // Print security level SecurityLevel security = context.firstContextData().qualifiers().secLevel; System.out.println("Security level: " + security); } }

2. BFV Scheme for Integer Operations

BFV (Brakerski/Fan-Vercauteren) Scheme Implementation:

package com.example.seal.bfv; import com.microsoft.seal.*; import java.util.ArrayList; import java.util.List; public class BFVOperations { private final SEALSetup setup; private final BatchEncoder batchEncoder; private final Encryptor encryptor; private final Evaluator evaluator; private final Decryptor decryptor; public BFVOperations(SEALSetup setup) { this.setup = setup; this.batchEncoder = setup.getBatchEncoder(); this.encryptor = setup.getEncryptor(); this.evaluator = setup.getEvaluator(); this.decryptor = setup.getDecryptor(); if (setup.getEncryptionParams().scheme() != SchemeType.bfv) { throw new IllegalArgumentException("BFV operations require BFV scheme"); } } // Encrypt a vector of integers public Ciphertext encryptVector(List<Long> values) { // Convert list to array long[] array = values.stream().mapToLong(Long::longValue).toArray(); // Encode the vector Plaintext plain = new Plaintext(); batchEncoder.encode(array, plain); // Encrypt Ciphertext cipher = new Ciphertext(); encryptor.encrypt(plain, cipher); return cipher; } // Decrypt a ciphertext to vector public List<Long> decryptVector(Ciphertext cipher) { // Decrypt Plaintext plain = new Plaintext(); decryptor.decrypt(cipher, plain); // Decode long[] array = new long[batchEncoder.slotCount()]; batchEncoder.decode(plain, array); // Convert to list List<Long> result = new ArrayList<>(); for (long value : array) { result.add(value); } return result; } // Homomorphic addition public Ciphertext add(Ciphertext cipher1, Ciphertext cipher2) { Ciphertext result = new Ciphertext(); evaluator.add(cipher1, cipher2, result); return result; } public Ciphertext add(Ciphertext cipher, long scalar) { // Encode scalar as plaintext Plaintext plainScalar = new Plaintext(); batchEncoder.encode(new long[]{scalar}, plainScalar); // Add Ciphertext result = new Ciphertext(); evaluator.addPlain(cipher, plainScalar, result); return result; } // Homomorphic subtraction public Ciphertext subtract(Ciphertext cipher1, Ciphertext cipher2) { Ciphertext result = new Ciphertext(); evaluator.sub(cipher1, cipher2, result); return result; } // Homomorphic multiplication public Ciphertext multiply(Ciphertext cipher1, Ciphertext cipher2) { Ciphertext result = new Ciphertext(); evaluator.multiply(cipher1, cipher2, result); // Relinearize to reduce ciphertext size evaluator.relinearizeInplace(result, setup.getRelinKeys()); return result; } public Ciphertext multiply(Ciphertext cipher, long scalar) { // Encode scalar as plaintext Plaintext plainScalar = new Plaintext(); batchEncoder.encode(new long[]{scalar}, plainScalar); // Multiply Ciphertext result = new Ciphertext(); evaluator.multiplyPlain(cipher, plainScalar, result); return result; } // Homomorphic negation public Ciphertext negate(Ciphertext cipher) { Ciphertext result = new Ciphertext(); evaluator.negate(cipher, result); return result; } // Square public Ciphertext square(Ciphertext cipher) { Ciphertext result = new Ciphertext(); evaluator.square(cipher, result); evaluator.relinearizeInplace(result, setup.getRelinKeys()); return result; } // Rotate vector elements public Ciphertext rotate(Ciphertext cipher, int steps) { Ciphertext result = new Ciphertext(); evaluator.rotateVector(cipher, steps, setup.getGaloisKeys(), result); return result; } // Sum all elements in encrypted vector public Ciphertext sumAll(Ciphertext cipher) { int slotCount = batchEncoder.slotCount(); Ciphertext result = cipher.clone(); // Use rotation to sum all elements for (int i = 1; i < slotCount; i *= 2) { Ciphertext rotated = new Ciphertext(); evaluator.rotateVector(result, i, setup.getGaloisKeys(), rotated); evaluator.addInplace(result, rotated); } return result; } // Dot product of two encrypted vectors public Ciphertext dotProduct(Ciphertext cipher1, Ciphertext cipher2) { // Multiply element-wise Ciphertext multiplied = multiply(cipher1, cipher2); // Sum all elements return sumAll(multiplied); } // Compute polynomial: a*x^2 + b*x + c public Ciphertext evaluatePolynomial(Ciphertext x, long a, long b, long c) { // Compute x^2 Ciphertext xSquared = square(x); // Compute a*x^2 Ciphertext ax2 = multiply(xSquared, a); // Compute b*x Ciphertext bx = multiply(x, b); // Add all terms Ciphertext result = add(ax2, bx); result = add(result, c); return result; } // Statistics operations public Ciphertext computeMean(Ciphertext cipher) { int slotCount = batchEncoder.slotCount(); // Sum all elements Ciphertext sum = sumAll(cipher); // Divide by slot count (plaintext division) Plaintext divisor = new Plaintext(); batchEncoder.encode(new long[]{slotCount}, divisor); Ciphertext result = new Ciphertext(); evaluator.multiplyPlain(sum, divisor, result); // Actually need to divide return result; } // Example: Secure voting system public Ciphertext countVotes(List<Ciphertext> votes) { if (votes.isEmpty()) { throw new IllegalArgumentException("No votes provided"); } // Start with first vote Ciphertext result = votes.get(0).clone(); // Sum all votes for (int i = 1; i < votes.size(); i++) { evaluator.addInplace(result, votes.get(i)); } return result; } }

3. CKKS Scheme for Real Number Operations

CKKS (Cheon-Kim-Kim-Song) Scheme Implementation:

package com.example.seal.ckks; import com.microsoft.seal.*; import java.util.ArrayList; import java.util.List; public class CKKSOperations { private final SEALSetup setup; private final CKKSEncoder encoder; private final Encryptor encryptor; private final Evaluator evaluator; private final Decryptor decryptor; private final double scale; public CKKSOperations(SEALSetup setup, double scale) { this.setup = setup; this.encoder = setup.getCkksEncoder(); this.encryptor = setup.getEncryptor(); this.evaluator = setup.getEvaluator(); this.decryptor = setup.getDecryptor(); this.scale = scale; if (setup.getEncryptionParams().scheme() != SchemeType.ckks) { throw new IllegalArgumentException("CKKS operations require CKKS scheme"); } } // Encrypt a vector of doubles public Ciphertext encryptVector(List<Double> values) { // Convert to double array double[] array = new double[encoder.slotCount()]; for (int i = 0; i < Math.min(values.size(), array.length); i++) { array[i] = values.get(i); } // Encode Plaintext plain = new Plaintext(); encoder.encode(array, scale, plain); // Encrypt Ciphertext cipher = new Ciphertext(); encryptor.encrypt(plain, cipher); return cipher; } // Decrypt a ciphertext to vector public List<Double> decryptVector(Ciphertext cipher) { // Decrypt Plaintext plain = new Plaintext(); decryptor.decrypt(cipher, plain); // Decode double[] array = new double[encoder.slotCount()]; encoder.decode(plain, array); // Convert to list List<Double> result = new ArrayList<>(); for (double value : array) { result.add(value); } return result; } // Basic arithmetic operations public Ciphertext add(Ciphertext cipher1, Ciphertext cipher2) { Ciphertext result = new Ciphertext(); evaluator.add(cipher1, cipher2, result); return result; } public Ciphertext add(Ciphertext cipher, double scalar) { Plaintext plainScalar = new Plaintext(); encoder.encode(scalar, scale, plainScalar); Ciphertext result = new Ciphertext(); evaluator.addPlain(cipher, plainScalar, result); return result; } public Ciphertext multiply(Ciphertext cipher1, Ciphertext cipher2) { Ciphertext result = new Ciphertext(); evaluator.multiply(cipher1, cipher2, result); // Rescale after multiplication evaluator.rescaleToNextInplace(result); // Relinearize evaluator.relinearizeInplace(result, setup.getRelinKeys()); return result; } public Ciphertext multiply(Ciphertext cipher, double scalar) { Plaintext plainScalar = new Plaintext(); encoder.encode(scalar, scale, plainScalar); Ciphertext result = new Ciphertext(); evaluator.multiplyPlain(cipher, plainScalar, result); // Rescale evaluator.rescaleToNextInplace(result); return result; } // Linear regression prediction: y = w·x + b public Ciphertext linearRegressionPredict(Ciphertext encryptedFeatures, List<Double> weights, double bias) { // Encrypt weights Ciphertext encryptedWeights = encryptVector(weights); // Compute dot product: w·x Ciphertext dotProduct = multiply(encryptedFeatures, encryptedWeights); // Add bias return add(dotProduct, bias); } // Logistic regression prediction: sigmoid(w·x + b) public Ciphertext logisticRegressionPredict(Ciphertext encryptedFeatures, List<Double> weights, double bias) { // Compute linear part Ciphertext linear = linearRegressionPredict(encryptedFeatures, weights, bias); // Apply sigmoid approximation (using polynomial approximation) return sigmoidApproximation(linear); } // Sigmoid approximation using Taylor series private Ciphertext sigmoidApproximation(Ciphertext x) { // 5th degree polynomial approximation: // sigmoid(x) ≈ 0.5 + 0.197x - 0.004x^3 + 0.0001x^5 Ciphertext result; // Compute x^2 Ciphertext x2 = square(x); // Compute x^3 = x * x^2 Ciphertext x3 = multiply(x, x2); // Compute x^5 = x^2 * x^3 Ciphertext x5 = multiply(x2, x3); // Apply coefficients Ciphertext term1 = multiply(x, 0.197); Ciphertext term3 = multiply(x3, -0.004); Ciphertext term5 = multiply(x5, 0.0001); // Sum all terms result = add(term1, term3); result = add(result, term5); result = add(result, 0.5); return result; } // Neural network layer (dense layer with ReLU) public Ciphertext neuralNetworkLayer(Ciphertext input, List<List<Double>> weights, List<Double> biases) { List<Ciphertext> outputs = new ArrayList<>(); // For each neuron for (int i = 0; i < weights.size(); i++) { // Encrypt weights for this neuron Ciphertext encryptedWeights = encryptVector(weights.get(i)); // Compute dot product Ciphertext dotProduct = multiply(input, encryptedWeights); // Add bias Ciphertext preActivation = add(dotProduct, biases.get(i)); // Apply ReLU approximation (using square root approximation) Ciphertext activated = reluApproximation(preActivation); outputs.add(activated); } // TODO: Combine outputs (would need packing/rotation) return outputs.get(0); // Simplified } private Ciphertext reluApproximation(Ciphertext x) { // ReLU approximation: max(0, x) ≈ (x + sqrt(x^2 + ε)) / 2 double epsilon = 0.001; // Compute x^2 Ciphertext x2 = square(x); // Add epsilon Ciphertext x2PlusEpsilon = add(x2, epsilon); // TODO: Need square root approximation // For now, return identity return x.clone(); } // Statistics operations public Ciphertext computeMean(Ciphertext cipher) { int slotCount = encoder.slotCount(); // Sum all elements Ciphertext sum = sumAll(cipher); // Divide by slot count return multiply(sum, 1.0 / slotCount); } public Ciphertext computeVariance(Ciphertext cipher) { // Compute mean Ciphertext mean = computeMean(cipher); // Subtract mean from all elements (need rotation/broadcasting) // TODO: Implement proper broadcasting // Square the differences // Sum squared differences // Divide by N-1 return mean; // Placeholder } private Ciphertext sumAll(Ciphertext cipher) { int slotCount = encoder.slotCount(); Ciphertext result = cipher.clone(); // Use rotation to sum all elements for (int i = 1; i < slotCount; i *= 2) { Ciphertext rotated = new Ciphertext(); evaluator.rotateVector(result, i, setup.getGaloisKeys(), rotated); evaluator.addInplace(result, rotated); } return result; } private Ciphertext square(Ciphertext cipher) { Ciphertext result = new Ciphertext(); evaluator.square(cipher, result); evaluator.relinearizeInplace(result, setup.getRelinKeys()); evaluator.rescaleToNextInplace(result); return result; } }

4. Practical Use Cases and Applications

Secure Data Analytics:

package com.example.seal.applications; import com.example.seal.bfv.BFVOperations; import com.example.seal.ckks.CKKSOperations; import com.microsoft.seal.*; import java.util.*; public class SecureDataAnalytics { // 1. Privacy-Preserving Statistics public static class SecureStatistics { private final BFVOperations bfvOps; public SecureStatistics(BFVOperations bfvOps) { this.bfvOps = bfvOps; } // Compute sum of encrypted values public Ciphertext secureSum(List<Ciphertext> encryptedValues) { if (encryptedValues.isEmpty()) { throw new IllegalArgumentException("No values provided"); } Ciphertext result = encryptedValues.get(0).clone(); Evaluator evaluator = bfvOps.getEvaluator(); for (int i = 1; i < encryptedValues.size(); i++) { evaluator.addInplace(result, encryptedValues.get(i)); } return result; } // Compute average without revealing individual values public Ciphertext secureAverage(List<Ciphertext> encryptedValues) { Ciphertext sum = secureSum(encryptedValues); // Multiply by 1/N (requires encoding 1/N as plaintext) Plaintext inverseN = bfvOps.encodePlaintext(1.0 / encryptedValues.size()); Ciphertext result = new Ciphertext(); bfvOps.getEvaluator().multiplyPlain(sum, inverseN, result); return result; } // Secure voting/counting public Map<String, Long> secureVoting(List<Vote> encryptedVotes, List<String> candidates) { Map<String, Ciphertext> candidateVotes = new HashMap<>(); // Initialize vote counts for each candidate for (String candidate : candidates) { Plaintext zero = bfvOps.encodePlaintext(0L); Ciphertext cipherZero = new Ciphertext(); bfvOps.getEncryptor().encrypt(zero, cipherZero); candidateVotes.put(candidate, cipherZero); } // Count votes for (Vote vote : encryptedVotes) { String candidate = vote.getCandidate(); Ciphertext currentVotes = candidateVotes.get(candidate); // Add vote (1 for this candidate) Ciphertext one = bfvOps.encryptVector(Arrays.asList(1L)); bfvOps.getEvaluator().addInplace(currentVotes, one); } // Decrypt results (would be done by trusted party) Map<String, Long> results = new HashMap<>(); // In real scenario, decryption would happen separately return results; } } // 2. Privacy-Preserving Machine Learning public static class SecureML { private final CKKSOperations ckksOps; public SecureML(CKKSOperations ckksOps) { this.ckksOps = ckksOps; } // Secure prediction service public Ciphertext securePrediction(Ciphertext encryptedFeatures, List<List<Double>> modelWeights, List<Double> modelBiases) { List<Ciphertext> layerOutputs = new ArrayList<>(); Ciphertext currentInput = encryptedFeatures; // Process each layer for (int layer = 0; layer < modelWeights.size(); layer++) { Ciphertext layerResult = neuralNetworkLayer( currentInput, modelWeights.get(layer), modelBiases.get(layer) ); layerOutputs.add(layerResult); currentInput = layerResult; } return layerOutputs.get(layerOutputs.size() - 1); } private Ciphertext neuralNetworkLayer(Ciphertext input, List<Double> weights, double bias) { // Simplified single neuron return ckksOps.linearRegressionPredict(input, weights, bias); } // Secure federated learning aggregation public List<Double> federatedAverage(List<List<Double>> encryptedGradients) { // This would actually work with encrypted gradients // For demonstration, assume we can compute average if (encryptedGradients.isEmpty()) { return new ArrayList<>(); } int numClients = encryptedGradients.size(); int gradientSize = encryptedGradients.get(0).size(); List<Double> average = new ArrayList<>(Collections.nCopies(gradientSize, 0.0)); // Sum gradients (in real scenario, this would be homomorphic) for (List<Double> gradient : encryptedGradients) { for (int i = 0; i < gradientSize; i++) { average.set(i, average.get(i) + gradient.get(i)); } } // Average for (int i = 0; i < gradientSize; i++) { average.set(i, average.get(i) / numClients); } return average; } } // 3. Secure Database Operations public static class SecureDatabase { private final BFVOperations bfvOps; private final Map<String, Ciphertext> encryptedRecords; public SecureDatabase(BFVOperations bfvOps) { this.bfvOps = bfvOps; this.encryptedRecords = new HashMap<>(); } public void insertRecord(String id, List<Long> values) { Ciphertext encrypted = bfvOps.encryptVector(values); encryptedRecords.put(id, encrypted); } // Search for records where attribute equals value (oblivious) public List<String> searchByAttribute(int attributeIndex, long value) { List<String> matches = new ArrayList<>(); // Encrypt the search value List<Long> searchPattern = new ArrayList<>(); for (int i = 0; i < bfvOps.getBatchEncoder().slotCount(); i++) { searchPattern.add(i == attributeIndex ? 1L : 0L); } Ciphertext searchMask = bfvOps.encryptVector(searchPattern); // For each record for (Map.Entry<String, Ciphertext> entry : encryptedRecords.entrySet()) { // Multiply record by search mask (zeros out other attributes) Ciphertext masked = bfvOps.multiply(entry.getValue(), searchMask); // TODO: Compare with encrypted value // This is simplified - real implementation would need more operations matches.add(entry.getKey()); } return matches; } // Compute statistics on encrypted data public Ciphertext computeColumnSum(int columnIndex) { Ciphertext sum = null; for (Ciphertext record : encryptedRecords.values()) { if (sum == null) { sum = record.clone(); } else { bfvOps.getEvaluator().addInplace(sum, record); } } return sum; } } // Supporting classes public static class Vote { private final String candidate; private final Ciphertext encryptedVote; public Vote(String candidate, Ciphertext encryptedVote) { this.candidate = candidate; this.encryptedVote = encryptedVote; } public String getCandidate() { return candidate; } public Ciphertext getEncryptedVote() { return encryptedVote; } } }

5. Performance Optimization and Batch Operations

Batch Processing and Optimization:

package com.example.seal.optimization; import com.microsoft.seal.*; import java.util.*; import java.util.concurrent.*; public class SEALOptimizer { // Parallel encryption/decryption public static class ParallelProcessor { private final ExecutorService executor; public ParallelProcessor(int threadCount) { this.executor = Executors.newFixedThreadPool(threadCount); } public List<Ciphertext> encryptBatch(List<List<Double>> data, Encryptor encryptor, CKKSEncoder encoder, double scale) throws InterruptedException { List<Future<Ciphertext>> futures = new ArrayList<>(); for (List<Double> vector : data) { futures.add(executor.submit(() -> { double[] array = new double[encoder.slotCount()]; for (int i = 0; i < Math.min(vector.size(), array.length); i++) { array[i] = vector.get(i); } Plaintext plain = new Plaintext(); encoder.encode(array, scale, plain); Ciphertext cipher = new Ciphertext(); encryptor.encrypt(plain, cipher); return cipher; })); } List<Ciphertext> results = new ArrayList<>(); for (Future<Ciphertext> future : futures) { try { results.add(future.get()); } catch (ExecutionException e) { throw new RuntimeException("Encryption failed", e); } } return results; } public void shutdown() { executor.shutdown(); } } // Ciphertext caching and reuse public static class CiphertextCache { private final Map<String, Ciphertext> cache; private final int maxSize; public CiphertextCache(int maxSize) { this.cache = new LinkedHashMap<String, Ciphertext>(maxSize, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<String, Ciphertext> eldest) { return size() > maxSize; } }; this.maxSize = maxSize; } public void put(String key, Ciphertext ciphertext) { synchronized (cache) { cache.put(key, ciphertext.clone()); } } public Optional<Ciphertext> get(String key) { synchronized (cache) { Ciphertext cipher = cache.get(key); if (cipher != null) { return Optional.of(cipher.clone()); } return Optional.empty(); } } public void clear() { synchronized (cache) { cache.clear(); } } } // Memory management for SEAL objects public static class SEALMemoryManager { public static void cleanUp(Object... objects) { for (Object obj : objects) { if (obj instanceof AbstractNativeObject) { ((AbstractNativeObject) obj).close(); } } } public static Ciphertext deepCopy(Ciphertext original) { if (original == null) return null; return original.clone(); } public static List<Ciphertext> batchCopy(List<Ciphertext> originals) { List<Ciphertext> copies = new ArrayList<>(); for (Ciphertext cipher : originals) { copies.add(deepCopy(cipher)); } return copies; } } // Performance monitoring public static class SEALPerformanceMonitor { private final Map<String, List<Long>> operationTimings; public SEALPerformanceMonitor() { this.operationTimings = new HashMap<>(); } public void timeOperation(String operationName, Runnable operation) { long startTime = System.nanoTime(); operation.run(); long endTime = System.nanoTime(); long duration = endTime - startTime; operationTimings .computeIfAbsent(operationName, k -> new ArrayList<>()) .add(duration); } public void printStatistics() { System.out.println("=== SEAL Performance Statistics ==="); for (Map.Entry<String, List<Long>> entry : operationTimings.entrySet()) { String operation = entry.getKey(); List<Long> timings = entry.getValue(); double avg = timings.stream().mapToLong(Long::longValue).average().orElse(0) / 1_000_000.0; double min = timings.stream().mapToLong(Long::longValue).min().orElse(0) / 1_000_000.0; double max = timings.stream().mapToLong(Long::longValue).max().orElse(0) / 1_000_000.0; System.out.printf("%s: avg=%.2fms, min=%.2fms, max=%.2fms, count=%d%n", operation, avg, min, max, timings.size()); } } } }

6. Serialization and Network Communication

SEAL Object Serialization:

package com.example.seal.serialization; import com.microsoft.seal.*; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.*; import java.util.*; import java.util.zip.*; public class SEALSerializer { private final ObjectMapper objectMapper; public SEALSerializer() { this.objectMapper = new ObjectMapper(); } // Serialize SEAL objects to base64 strings public String serializeToString(AbstractNativeObject sealObject) throws IOException { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); try (GZIPOutputStream gzipStream = new GZIPOutputStream(byteStream)) { sealObject.save(gzipStream); } return Base64.getEncoder().encodeToString(byteStream.toByteArray()); } public Ciphertext deserializeCiphertext(String base64String, SEALContext context) throws IOException { byte[] compressed = Base64.getDecoder().decode(base64String); ByteArrayInputStream byteStream = new ByteArrayInputStream(compressed); try (GZIPInputStream gzipStream = new GZIPInputStream(byteStream)) { Ciphertext ciphertext = new Ciphertext(); ciphertext.load(context, gzipStream); return ciphertext; } } public PublicKey deserializePublicKey(String base64String, SEALContext context) throws IOException { byte[] compressed = Base64.getDecoder().decode(base64String); ByteArrayInputStream byteStream = new ByteArrayInputStream(compressed); try (GZIPInputStream gzipStream = new GZIPInputStream(byteStream)) { PublicKey publicKey = new PublicKey(); publicKey.load(context, gzipStream); return publicKey; } } // Serialize encryption parameters public String serializeParameters(EncryptionParameters params) throws IOException { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); params.save(byteStream); return Base64.getEncoder().encodeToString(byteStream.toByteArray()); } public EncryptionParameters deserializeParameters(String base64String) throws IOException { byte[] data = Base64.getDecoder().decode(base64String); ByteArrayInputStream byteStream = new ByteArrayInputStream(data); EncryptionParameters params = new EncryptionParameters(); params.load(byteStream); return params; } // JSON wrapper for easier API communication public static class SEALMessage { private String type; private String data; private Map<String, String> metadata; public SEALMessage() {} public SEALMessage(String type, String data) { this.type = type; this.data = data; this.metadata = new HashMap<>(); } // Getters and setters public String getType() { return type; } public void setType(String type) { this.type = type; } public String getData() { return data; } public void setData(String data) { this.data = data; } public Map<String, String> getMetadata() { return metadata; } public void setMetadata(Map<String, String> metadata) { this.metadata = metadata; } public void addMetadata(String key, String value) { metadata.put(key, value); } public String toJson() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(this); } public static SEALMessage fromJson(String json) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(json, SEALMessage.class); } } // Network client for SEAL operations public static class SEALClient { private final String serverUrl; private final SEALSerializer serializer; public SEALClient(String serverUrl) { this.serverUrl = serverUrl; this.serializer = new SEALSerializer(); } public String sendCiphertext(Ciphertext ciphertext) throws IOException { String serialized = serializer.serializeToString(ciphertext); SEALMessage message = new SEALMessage("ciphertext", serialized); message.addMetadata("operation", "computation"); // In real implementation, use HTTP client // String response = httpClient.post(serverUrl, message.toJson()); return message.toJson(); } public Ciphertext receiveCiphertext(String jsonResponse, SEALContext context) throws IOException { SEALMessage response = SEALMessage.fromJson(jsonResponse); return serializer.deserializeCiphertext(response.getData(), context); } } }

7. Spring Boot Integration

Spring Boot Configuration:

package com.example.seal.config; import com.example.seal.basics.SEALSetup; import com.example.seal.bfv.BFVOperations; import com.example.seal.ckks.CKKSOperations; import com.microsoft.seal.SchemeType; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SEALConfig { @Value("${seal.scheme:ckks}") private String scheme; @Value("${seal.poly-modulus-degree:8192}") private int polyModulusDegree; @Value("${seal.scale:pow2(40)}") private double scale; @Bean(destroyMethod = "cleanup") public SEALSetup sealSetup() { SchemeType schemeType = scheme.equalsIgnoreCase("bfv") ? SchemeType.bfv : SchemeType.ckks; return new SEALSetup.Builder() .withScheme(schemeType) .withPolyModulusDegree(polyModulusDegree) .build(); } @Bean public BFVOperations bfvOperations(SEALSetup setup) { if (setup.getEncryptionParams().scheme() != SchemeType.bfv) { throw new IllegalStateException("BFV operations require BFV scheme"); } return new BFVOperations(setup); } @Bean public CKKSOperations ckksOperations(SEALSetup setup) { if (setup.getEncryptionParams().scheme() != SchemeType.ckks) { throw new IllegalStateException("CKKS operations require CKKS scheme"); } return new CKKSOperations(setup, scale); } } @RestController @RequestMapping("/api/seal") public class SEALController { private final BFVOperations bfvOps; private final CKKSOperations ckksOps; private final SEALSerializer serializer; public SEALController(BFVOperations bfvOps, CKKSOperations ckksOps) { this.bfvOps = bfvOps; this.ckksOps = ckksOps; this.serializer = new SEALSerializer(); } @PostMapping("/bfv/encrypt") public ResponseEntity<SEALMessage> bfvEncrypt(@RequestBody List<Long> values) { try { Ciphertext ciphertext = bfvOps.encryptVector(values); String serialized = serializer.serializeToString(ciphertext); SEALMessage response = new SEALMessage("ciphertext", serialized); return ResponseEntity.ok(response); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } @PostMapping("/ckks/compute") public ResponseEntity<SEALMessage> ckksCompute(@RequestBody ComputationRequest request) { try { // Deserialize input ciphertext Ciphertext input = serializer.deserializeCiphertext( request.getCiphertext(), ckksOps.getContext()); // Perform computation based on operation type Ciphertext result; switch (request.getOperation()) { case "add": result = ckksOps.add(input, request.getScalar()); break; case "multiply": result = ckksOps.multiply(input, request.getScalar()); break; default: return ResponseEntity.badRequest().build(); } // Serialize result String serialized = serializer.serializeToString(result); SEALMessage response = new SEALMessage("result", serialized); return ResponseEntity.ok(response); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } @PostMapping("/ml/predict") public ResponseEntity<SEALMessage> mlPredict(@RequestBody MLPredictRequest request) { try { // This would involve more complex model evaluation // Simplified for demonstration Ciphertext features = serializer.deserializeCiphertext( request.getFeatures(), ckksOps.getContext()); // Load model weights (in real scenario, from secure storage) List<Double> weights = request.getWeights(); double bias = request.getBias(); // Make prediction Ciphertext prediction = ckksOps.linearRegressionPredict(features, weights, bias); String serialized = serializer.serializeToString(prediction); SEALMessage response = new SEALMessage("prediction", serialized); return ResponseEntity.ok(response); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } } class ComputationRequest { private String ciphertext; private String operation; private double scalar; // Getters and setters } class MLPredictRequest { private String features; private List<Double> weights; private double bias; // Getters and setters }

8. Security Best Practices

Security Manager:

package com.example.seal.security; import com.microsoft.seal.*; import java.security.*; import java.util.*; public class SEALSecurityManager { // Validate encryption parameters for security public static SecurityReport validateParameters(EncryptionParameters params) { SecurityReport report = new SecurityReport(); try { SEALContext context = new SEALContext(params); SEALContext.ContextData contextData = context.firstContextData(); SEALContext.Qualifiers qualifiers = contextData.qualifiers(); // Check security level if (qualifiers.secLevel != SecurityLevel.tc128) { report.addWarning("Security level is not 128-bit"); } // Check parameter compatibility if (!qualifiers.parametersSet) { report.addError("Parameters are not set correctly"); } // Check for batching if needed if (!qualifiers.usingBatching) { report.addWarning("Batching is not enabled"); } // Validate coefficient modulus List<Modulus> coeffModulus = params.getCoeffModulus(); if (coeffModulus.size() < 2) { report.addWarning("Coefficient modulus chain might be too short"); } } catch (Exception e) { report.addError("Failed to validate parameters: " + e.getMessage()); } return report; } // Key rotation and management public static class KeyManager { private final Map<String, KeyPair> keyPairs; private final SecureRandom random; public KeyManager() { this.keyPairs = new HashMap<>(); this.random = new SecureRandom(); } public KeyPair generateKeyPair(SEALContext context) { KeyGenerator keyGen = new KeyGenerator(context); PublicKey publicKey = keyGen.createPublicKey(); SecretKey secretKey = keyGen.secretKey(); RelinKeys relinKeys = keyGen.createRelinKeys(); GaloisKeys galoisKeys = keyGen.createGaloisKeys(); String keyId = generateKeyId(); KeyPair keyPair = new KeyPair(publicKey, secretKey, relinKeys, galoisKeys); keyPairs.put(keyId, keyPair); return keyPair; } public void rotateKeys(String oldKeyId, SEALContext context) { // Generate new keys KeyPair newKeys = generateKeyPair(context); // Re-encrypt data with new keys if needed // Store mapping from old to new key // Eventually delete old keys keyPairs.remove(oldKeyId); } private String generateKeyId() { byte[] bytes = new byte[16]; random.nextBytes(bytes); return Base64.getEncoder().encodeToString(bytes); } } // Side-channel attack mitigation public static class SideChannelProtection { public static Ciphertext addNoise(Ciphertext cipher, Evaluator evaluator, double noiseLevel) { // Add encrypted noise to mask operations // Implementation depends on scheme return cipher; // Simplified } public static void constantTimeOperations() { // Ensure operations take constant time // This is more about implementation patterns } } // Audit logging for homomorphic operations public static class AuditLogger { private final List<AuditEntry> log; public AuditLogger() { this.log = new ArrayList<>(); } public void logOperation(String operation, String userId, String dataId, String resultHash) { AuditEntry entry = new AuditEntry( operation, userId, dataId, resultHash, new Date() ); log.add(entry); } public List<AuditEntry> getAuditTrail() { return Collections.unmodifiableList(log); } public static class AuditEntry { private final String operation; private final String userId; private final String dataId; private final String resultHash; private final Date timestamp; public AuditEntry(String operation, String userId, String dataId, String resultHash, Date timestamp) { this.operation = operation; this.userId = userId; this.dataId = dataId; this.resultHash = resultHash; this.timestamp = timestamp; } // Getters public String getOperation() { return operation; } public String getUserId() { return userId; } public String getDataId() { return dataId; } public String getResultHash() { return resultHash; } public Date getTimestamp() { return timestamp; } } } } class SecurityReport { private final List<String> errors = new ArrayList<>(); private final List<String> warnings = new ArrayList<>(); public void addError(String error) { errors.add(error); } public void addWarning(String warning) { warnings.add(warning); } public boolean isSecure() { return errors.isEmpty(); } public List<String> getErrors() { return Collections.unmodifiableList(errors); } public List<String> getWarnings() { return Collections.unmodifiableList(warnings); } } class KeyPair { private final PublicKey publicKey; private final SecretKey secretKey; private final RelinKeys relinKeys; private final GaloisKeys galoisKeys; public KeyPair(PublicKey publicKey, SecretKey secretKey, RelinKeys relinKeys, GaloisKeys galoisKeys) { this.publicKey = publicKey; this.secretKey = secretKey; this.relinKeys = relinKeys; this.galoisKeys = galoisKeys; } // Getters public PublicKey getPublicKey() { return publicKey; } public SecretKey getSecretKey() { return secretKey; } public RelinKeys getRelinKeys() { return relinKeys; } public GaloisKeys getGaloisKeys() { return galoisKeys; } }

Conclusion

Microsoft SEAL in Java enables powerful privacy-preserving computations. Key takeaways:

  1. Scheme Selection: BFV for integers, CKKS for real numbers
  2. Parameter Tuning: Balance security, performance, and capability
  3. Batch Operations: Maximize throughput using SIMD operations
  4. Performance Optimization: Parallel processing and caching
  5. Security Considerations: Parameter validation and key management

This implementation provides a foundation for building secure, privacy-preserving applications in finance, healthcare, machine learning, and data analytics.

Title: Advanced Java Security: OAuth 2.0, Strong Authentication & Cryptographic Best Practices

Summary: These articles collectively explain how modern Java systems implement secure authentication, authorization, and password protection using industry-grade standards like OAuth 2.0 extensions, mutual TLS, and advanced password hashing algorithms, along with cryptographic best practices for generating secure randomness.

Links with explanations:
https://macronepal.com/blog/dpop-oauth-demonstrating-proof-of-possession-in-java-binding-tokens-to-clients/ (Explains DPoP, which binds OAuth tokens to a specific client so stolen tokens cannot be reused by attackers)
https://macronepal.com/blog/beyond-bearer-tokens-implementing-mutual-tls-for-strong-authentication-in-java/ (Covers mTLS, where both client and server authenticate each other using certificates for stronger security than bearer tokens)
https://macronepal.com/blog/oauth-2-0-token-exchange-in-java-implementing-rfc-8693-for-modern-identity-flows/ (Explains token exchange, allowing secure swapping of access tokens between services in distributed systems)
https://macronepal.com/blog/true-randomness-integrating-hardware-rngs-for-cryptographically-secure-java-applications/ (Discusses hardware-based random number generation for producing truly secure cryptographic keys)
https://macronepal.com/blog/the-password-hashing-dilemma-bcrypt-vs-pbkdf2-in-java/ (Compares BCrypt and PBKDF2 for password hashing and their resistance to brute-force attacks)
https://macronepal.com/blog/scrypt-implementation-in-java-memory-hard-password-hashing-for-jvm-applications/ (Explains Scrypt, a memory-hard hashing algorithm designed to resist GPU/ASIC attacks)
https://macronepal.com/blog/modern-password-security-implementing-argon2-in-java-applications/ (Covers Argon2, a modern and highly secure password hashing algorithm with strong memory-hard protections)

Leave a Reply

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


Macro Nepal Helper