Skip to content

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

  1. Generate an ML-KEM-512 key-pair.
  2. Export its public metadata to JSON.
  3. Encrypt a file via encryptFile(…)  → compact JWE on disk.
  4. Decrypt the token via decryptFile(…).
  5. 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&nbsp;5 – ML-KEM-512 Bulk Helpers (Compact&nbsp;JWE)</h2>
 *
 * <p>This scenario demonstrates the <strong>non-streaming</strong> helper
 * methods that work with the RFC&nbsp;7516 <em>Compact&nbsp;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&nbsp;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&nbsp;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


mvn -q compile exec:java\
  -Dexec.mainClass="co.ankatech.ankasecure.sdk.examples.ExampleScenario5"

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


Where next?