Java Platform Guide

Overview

MetaMUI Crypto for Java provides cryptographic implementations for JVM applications. Supporting Java 8+ and compatible with Android. Build from source and include as a local dependency.

Installation

Build from source:

cd metamui-crypto-java
mvn compile

Running Tests

cd metamui-crypto-java
mvn test

Using in Your Project

After building, you can install to your local Maven repository:

cd metamui-crypto-java
mvn install

Then reference in your project’s pom.xml:

<dependency>
    <groupId>com.metamui.crypto</groupId>
    <artifactId>metamui-crypto</artifactId>
    <version>0.1.0-SNAPSHOT</version>
</dependency>

Or for Gradle:

dependencies {
    implementation files('../metamui-crypto-java/target/metamui-crypto-0.1.0-SNAPSHOT.jar')
}

Usage Examples

Basic Hashing with BLAKE3

import id.metamui.crypto.Blake3;
import id.metamui.crypto.HashResult;

public class HashingExample {
    public static void main(String[] args) {
        // Simple hashing
        byte[] data = "Hello, World!".getBytes(StandardCharsets.UTF_8);
        byte[] hash = Blake3.hash(data);
        System.out.println("BLAKE3 Hash: " + bytesToHex(hash));

        // Streaming hash for large data
        Blake3.Hasher hasher = Blake3.newHasher();
        hasher.update(data1);
        hasher.update(data2);
        byte[] result = hasher.finalize();

        // Keyed hashing (MAC)
        byte[] key = new byte[32];
        SecureRandom.getInstanceStrong().nextBytes(key);
        byte[] mac = Blake3.keyedHash(key, data);

        // Derive key material
        byte[] derivedKey = Blake3.deriveKey("context string", data);
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
}

Post-Quantum Key Exchange (ML-KEM-768)

import id.metamui.crypto.pqc.MlKem768;
import id.metamui.crypto.pqc.KemKeypair;
import id.metamui.crypto.pqc.EncapsulationResult;

public class QuantumSafeKeyExchange {
    public static void demonstrateKEM() throws Exception {
        // Generate keypair
        KemKeypair keypair = MlKem768.generateKeypair();
        byte[] publicKey = keypair.getPublicKey();
        byte[] secretKey = keypair.getSecretKey();

        // Sender: Encapsulate shared secret
        EncapsulationResult encapResult = MlKem768.encapsulate(publicKey);
        byte[] ciphertext = encapResult.getCiphertext();
        byte[] sharedSecretSender = encapResult.getSharedSecret();

        // Receiver: Decapsulate shared secret
        byte[] sharedSecretReceiver = MlKem768.decapsulate(ciphertext, secretKey);

        // Verify shared secrets match
        boolean match = Arrays.equals(sharedSecretSender, sharedSecretReceiver);
        System.out.println("Shared secrets match: " + match);

        // Use shared secret for AES encryption
        SecretKeySpec aesKey = new SecretKeySpec(sharedSecretSender, "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, aesKey);

        // Clean up sensitive data
        Arrays.fill(secretKey, (byte) 0);
        Arrays.fill(sharedSecretSender, (byte) 0);
        Arrays.fill(sharedSecretReceiver, (byte) 0);
    }
}

Authenticated Encryption (ChaCha20-Poly1305)

import id.metamui.crypto.ChaCha20Poly1305;
import id.metamui.crypto.AeadResult;

public class SecureMessaging {
    private static final int KEY_SIZE = 32;
    private static final int NONCE_SIZE = 12;
    private static final int TAG_SIZE = 16;

    public static AeadResult encrypt(byte[] plaintext, byte[] key,
                                    byte[] associatedData) throws Exception {
        // Generate random nonce
        byte[] nonce = new byte[NONCE_SIZE];
        SecureRandom.getInstanceStrong().nextBytes(nonce);

        // Create cipher instance
        ChaCha20Poly1305 cipher = new ChaCha20Poly1305(key);

        // Encrypt with associated data
        byte[] ciphertext = cipher.encrypt(nonce, plaintext, associatedData);

        return new AeadResult(ciphertext, nonce);
    }

    public static byte[] decrypt(byte[] ciphertext, byte[] nonce,
                                byte[] key, byte[] associatedData) throws Exception {
        ChaCha20Poly1305 cipher = new ChaCha20Poly1305(key);

        // Decrypt and verify
        byte[] plaintext = cipher.decrypt(nonce, ciphertext, associatedData);

        return plaintext;
    }

    public static void demonstrateAEAD() throws Exception {
        // Generate key
        byte[] key = new byte[KEY_SIZE];
        SecureRandom.getInstanceStrong().nextBytes(key);

        // Data to encrypt
        byte[] message = "Secret message".getBytes(StandardCharsets.UTF_8);
        byte[] metadata = "Public metadata".getBytes(StandardCharsets.UTF_8);

        // Encrypt
        AeadResult encrypted = encrypt(message, key, metadata);

        // Decrypt
        byte[] decrypted = decrypt(encrypted.getCiphertext(),
                                  encrypted.getNonce(), key, metadata);

        System.out.println("Decrypted: " + new String(decrypted, StandardCharsets.UTF_8));
    }
}

Digital Signatures with Dilithium

import id.metamui.crypto.pqc.Dilithium3;
import id.metamui.crypto.pqc.SignatureKeypair;

public class QuantumSafeSignatures {
    public static void demonstrateSignatures() throws Exception {
        // Generate signing keypair
        SignatureKeypair keypair = Dilithium3.generateKeypair();
        byte[] publicKey = keypair.getPublicKey();
        byte[] privateKey = keypair.getPrivateKey();

        // Sign a message
        byte[] message = "Important document".getBytes(StandardCharsets.UTF_8);
        byte[] signature = Dilithium3.sign(message, privateKey);

        // Verify signature
        boolean valid = Dilithium3.verify(message, signature, publicKey);
        System.out.println("Signature valid: " + valid);

        // Secure cleanup
        Arrays.fill(privateKey, (byte) 0);
    }
}

Password Hashing with Argon2id

import id.metamui.crypto.Argon2id;
import id.metamui.crypto.Argon2Config;
import id.metamui.crypto.Argon2Result;

public class PasswordManager {
    public static String hashPassword(String password) throws Exception {
        // Generate salt
        byte[] salt = new byte[16];
        SecureRandom.getInstanceStrong().nextBytes(salt);

        // Configure Argon2id parameters
        Argon2Config config = Argon2Config.builder()
            .memoryCost(65536)  // 64 MB
            .timeCost(4)         // 4 iterations
            .parallelism(2)      // 2 threads
            .hashLength(32)      // 32 bytes output
            .salt(salt)
            .build();

        // Hash password
        Argon2Result result = Argon2id.hash(password.toCharArray(), config);

        // Return encoded string (includes all parameters)
        return result.getEncoded();
    }

    public static boolean verifyPassword(String password, String encodedHash) {
        return Argon2id.verify(encodedHash, password.toCharArray());
    }

    public static void demonstratePasswordHashing() throws Exception {
        String password = "userPassword123!";

        // Hash password
        String hash = hashPassword(password);
        System.out.println("Encoded hash: " + hash);

        // Verify password
        boolean valid = verifyPassword(password, hash);
        System.out.println("Password valid: " + valid);

        // Test invalid password
        boolean invalid = verifyPassword("wrongPassword", hash);
        System.out.println("Wrong password rejected: " + !invalid);
    }
}

Spring Boot Integration

@Configuration
public class CryptoConfiguration {

    @Bean
    public Blake3Service blake3Service() {
        return new Blake3Service();
    }

    @Bean
    public MlKem768Service mlKem768Service() {
        return new MlKem768Service();
    }
}

@Service
public class CryptoService {
    private final Blake3Service blake3;
    private final MlKem768Service mlKem;

    @Autowired
    public CryptoService(Blake3Service blake3, MlKem768Service mlKem) {
        this.blake3 = blake3;
        this.mlKem = mlKem;
    }

    public byte[] hashData(byte[] data) {
        return blake3.hash(data);
    }

    public KemKeypair generateQuantumSafeKeypair() {
        return mlKem.generateKeypair();
    }
}

Concurrent Operations

import java.util.concurrent.*;

public class ConcurrentCrypto {
    private static final ExecutorService executor =
        Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public static Map<String, byte[]> hashFilesInParallel(List<File> files)
            throws InterruptedException, ExecutionException {
        List<Future<HashResult>> futures = new ArrayList<>();

        for (File file : files) {
            Future<HashResult> future = executor.submit(() -> {
                try (InputStream is = new FileInputStream(file)) {
                    Blake3.Hasher hasher = Blake3.newHasher();
                    byte[] buffer = new byte[8192];
                    int read;

                    while ((read = is.read(buffer)) != -1) {
                        hasher.update(buffer, 0, read);
                    }

                    return new HashResult(file.getName(), hasher.finalize());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            futures.add(future);
        }

        Map<String, byte[]> results = new HashMap<>();
        for (Future<HashResult> future : futures) {
            HashResult result = future.get();
            results.put(result.filename, result.hash);
        }

        return results;
    }

    static class HashResult {
        final String filename;
        final byte[] hash;

        HashResult(String filename, byte[] hash) {
            this.filename = filename;
            this.hash = hash;
        }
    }
}

Secure Memory Management

import id.metamui.crypto.security.SecureMemory;
import id.metamui.crypto.security.SecureString;

public class SecureOperations {
    public static void handleSensitiveData() {
        // Allocate secure memory
        try (SecureMemory memory = SecureMemory.allocate(32)) {
            byte[] buffer = memory.getBuffer();
            // Use buffer for sensitive operations
            generateSecretKey(buffer);

            // Memory is automatically zeroed when closed
        }

        // Secure string handling
        try (SecureString password = new SecureString("sensitive_password")) {
            char[] chars = password.getChars();
            // Use password
            authenticate(chars);
            // Chars are automatically cleared
        }
    }
}

Error Handling

import id.metamui.crypto.exceptions.*;

public class ErrorHandling {
    public static void handleCryptoErrors() {
        try {
            MlKem768.encapsulate(publicKey);
        } catch (InvalidKeyException e) {
            // Handle invalid key
            logger.error("Invalid public key", e);
        } catch (CryptoException e) {
            // Handle general crypto errors
            logger.error("Cryptographic operation failed", e);
        } catch (UnsupportedAlgorithmException e) {
            // Handle unsupported algorithms
            logger.error("Algorithm not available", e);
        }
    }
}

Platform Support

JVM Version Support Level Notes
Java 21 Full Latest LTS, recommended
Java 17 Full LTS
Java 11 Full LTS
Java 8 Full Extended support
Android 7+ Full API Level 24+

Testing

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class CryptoTest {
    @Test
    public void testBlake3Hashing() {
        byte[] data = "test data".getBytes(StandardCharsets.UTF_8);
        byte[] hash = Blake3.hash(data);

        assertEquals(32, hash.length);
        assertNotNull(hash);

        // Verify deterministic
        byte[] hash2 = Blake3.hash(data);
        assertArrayEquals(hash, hash2);
    }

    @Test
    public void testMlKem768KeyExchange() throws Exception {
        // Generate keypairs
        var aliceKeypair = MlKem768.generateKeypair();
        var bobKeypair = MlKem768.generateKeypair();

        // Alice encapsulates for Bob
        var encapResult = MlKem768.encapsulate(bobKeypair.getPublicKey());

        // Bob decapsulates
        byte[] sharedSecret = MlKem768.decapsulate(
            encapResult.getCiphertext(),
            bobKeypair.getSecretKey()
        );

        // Verify shared secrets match
        assertArrayEquals(encapResult.getSharedSecret(), sharedSecret);
    }
}

Support Resources