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
- Issues: GitHub
- Commercial Support: [email protected]