Flow 5 – ML-KEM-512 Non-Streaming Encrypt / Decrypt (Compact JWE)
In this flow we use the non-streaming helpers of the SDK (compact-token mode) to encrypt and decrypt a file with a post-quantum ML-KEM-512 key.
Steps
- Generate an
ML-KEM-512
key-pair. - Export its public metadata to JSON.
- Encrypt a file via
encryptFile(…)
→ compact JWE on disk. - Decrypt the token via
decryptFile(…)
. - Validate that plaintext round-trips intact.
Non-streaming = the whole compact token is handled in memory.
For GB-scale files use the streaming APIs shown in Flows 1-4.Key points\ - Leverages the encryptFile( ) / decryptFile( ) helpers – the entire ciphertext is one Compact JWE string kept in memory.\ - Uses a post-quantum ML-KEM-512 key, giving long-term confidentiality that survives future RSA/ECC breaks.\ - Exports the key’s public metadata to a prettified JSON file so others can encrypt to you (or audit) without server access.\
When to use it
- Single-blob logistics – perfect when you want a self-contained text token that can be logged, emailed, checked into Git, or embedded in a manifest without worrying about binary streams or multipart boundaries.
- Quantum-resilient archives – ideal for backups, legal holds, or research data that must remain secret for decades; ML-KEM-512 protects against both classical and quantum adversaries.
- Offline or air-gapped workflows – the exported JSON + Compact JWE give auditors (or disaster-recovery teams) everything they need to decrypt on an isolated machine with no API calls.
Shared helper – this code imports the utility class from
example_util.md (configuration, authentication, JSON).
Complete Java implementation
src/main/java/co/ankatech/ankasecure/sdk/examples/ExampleScenario5.java
/* *****************************************************************************
* FILE: ExampleScenario5.java
* Copyright © 2025 Anka Technologies.
* ---------------------------------------------------------------------------
* Scenario 5 – ML-KEM-512 Bulk Encrypt / Decrypt (Compact-JWE helpers)
* ---------------------------------------------------------------------------
* 1. Generate an ML-KEM-512 key-pair.
* 2. Export the public metadata to JSON.
* 3. Encrypt a local file with encryptFile (compact JWE, <strong>no</strong> streaming).
* 4. Decrypt the ciphertext with decryptFile.
* 5. Display rich server metadata for both operations.
* 6. Confirm that decrypted plaintext equals the original.
* ---------------------------------------------------------------------------
* Detached artefacts are stored under <temp_files/>.
* ****************************************************************************/
package co.ankatech.ankasecure.sdk.examples;
import co.ankatech.ankasecure.sdk.AnkaSecureSdk;
import co.ankatech.ankasecure.sdk.model.DecryptResultMetadata;
import co.ankatech.ankasecure.sdk.model.EncryptResult;
import co.ankatech.ankasecure.sdk.model.GenerateKeySpec;
import co.ankatech.ankasecure.sdk.util.FileIO;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.Properties;
import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;
/**
* <h2>Scenario 5 – ML-KEM-512 Bulk Helpers (Compact JWE)</h2>
*
* <p>This scenario demonstrates the <strong>non-streaming</strong> helper
* methods that work with the RFC 7516 <em>Compact JWE</em> format:</p>
* <ol>
* <li>Generate an ML-KEM-512 key.</li>
* <li>Export the key’s metadata.</li>
* <li>Encrypt a file via {@link AnkaSecureSdk#encryptFile(String, Path, Path)}
* (compact token written to disk).</li>
* <li>Decrypt via {@link AnkaSecureSdk#decryptFile(Path, Path)}.</li>
* <li>Print rich metadata for each call.</li>
* <li>Validate round-trip integrity.</li>
* </ol>
*
* <p><b>Implementation notes (Java 21+):</b></p>
* <ul>
* <li>All filesystem interactions use the {@link java.nio.file.Path} API.</li>
* <li>UTF-8 is explicitly specified for deterministic encoding.</li>
* <li>Directory creation delegates to {@link ExampleUtil#ensureTempDir(Path)}.</li>
* </ul>
*
* <p><b>Thread-safety:</b> this class is stateless and immutable.</p>
*
* @author ANKATech – Security Engineering
* @since 2.1.0
*/
public final class ExampleScenario5 {
/* ====================================================================== */
/**
* Entry-point.
*
* @param args ignored
*/
public static void main(final String[] args) {
System.out.println("===== SCENARIO 5 START =====");
System.out.println("""
Purpose :
* ML-KEM-512 compact-JWE encrypt / decrypt helpers
* No streaming – full token handled in memory
--------------------------------------------------------------""");
try {
ensureTempDir(TEMP_DIR);
Properties props = loadProperties();
AnkaSecureSdk sdk = authenticate(props);
runScenario(sdk);
} catch (Exception ex) {
ExampleUtil.fatal("Scenario 5 failed", ex);
}
System.out.println("===== SCENARIO 5 END =====");
}
/* ====================================================================== */
/**
* Executes all steps of Scenario 5.
*
* @param sdk an authenticated {@link AnkaSecureSdk} instance
*
* @throws Exception if any step fails
*/
private static void runScenario(final AnkaSecureSdk sdk) throws Exception {
/* 1 ── prepare plaintext ----------------------------------------- */
Path plainFile = TEMP_DIR.resolve("scenario5_plain.txt");
FileIO.writeUtf8(
plainFile,
"Scenario-5 – ML-KEM-512 compact-JWE encryption demo.");
System.out.println("[1] Plaintext ready -> " + plainFile.toAbsolutePath());
/* 2 ── generate ML-KEM-512 key ----------------------------------- */
String kid = "sc5_kem_" + System.currentTimeMillis();
sdk.generateKey(new GenerateKeySpec()
.setKid(kid)
.setKty("ML-KEM")
.setAlg("ML-KEM-512"));
System.out.println("[2] Key generated -> kid = " + kid);
/* 3 ── export metadata ------------------------------------------- */
Path exportJson = TEMP_DIR.resolve("scenario5_key.json");
FileIO.writeUtf8(
exportJson,
ExampleUtil.toJson(sdk.exportKey(kid)));
System.out.println("[3] Metadata exported -> " + exportJson.toAbsolutePath());
/* 4 ── encrypt (compact JWE) ------------------------------------- */
Path cipherFile = TEMP_DIR.resolve("scenario5.cipher.jwe");
EncryptResult encMeta = sdk.encryptFile(kid, plainFile, cipherFile);
System.out.println(MessageFormat.format(
"[4] Ciphertext written -> {0}", cipherFile.toAbsolutePath()));
printEncryptMeta(encMeta);
/* 5 ── decrypt ---------------------------------------------------- */
Path decFile = TEMP_DIR.resolve("scenario5_decrypted.txt");
DecryptResultMetadata decMeta = sdk.decryptFile(cipherFile, decFile);
System.out.println(MessageFormat.format(
"[5] Decrypted file -> {0}", decFile.toAbsolutePath()));
printDecryptMeta(decMeta);
/* 6 ── validation ------------------------------------------------- */
String original = FileIO.readUtf8(plainFile);
String recovered = FileIO.readUtf8(decFile);
System.out.println(original.equals(recovered)
? "[6] Validation OK – plaintext matches."
: "[6] WARNING – plaintext mismatch!");
}
/**
* Private constructor prevents instantiation.
*/
private ExampleScenario5() {
/* utility class – no instantiation */
}
}
How to run
Console milestones:
-
ML-KEM-512 key generation
-
Metadata export →
scenario5_key.json
-
Non-streaming compact-JWE encryption →
scenario5.cipher.jwe
-
Decryption →
scenario5_decrypted.txt
-
Validation OK confirming byte-perfect round-trip