Skip to content

Flow 10 --- ML-KEM-1024 Compact-JWE Encrypt / Decrypt (Non-Streaming)

This scenario walks through a post-quantum, non-streaming round-trip with an ML-KEM-1024 key:

  • Generate an ML-KEM-1024 key-pair (kty="ML-KEM").

  • Export its public metadata to a JSON file.

  • Encrypt a plaintext file → compact JWE (five Base64URL segments).

  • Decrypt the compact JWE back to plaintext.

  • Validate that the recovered data matches the original.

Key points

  • Uses encryptFile( ) / decryptFile( ) helpers (entire token in memory, no streaming).

  • Demonstrates the Compact JWE format, ideal for pasting into logs, manifests or tickets.

  • Shows server-supplied metadata: key actually used, algorithm, lifetime warnings.

When to use it

  • Medium-sized artefacts --- perfect for files up to ~10 MB where a single self-contained token is simpler than a multipart stream.

  • Post-quantum readiness --- ML-KEM-1024 offers NIST-selected KEM protection for data that must survive future RSA/ECC breaks.

  • Easy distribution & audit --- the compact-JWE string can be emailed, committed to Git, or embedded in JSON APIs without binary handling.

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/ExampleScenario10.java

/* *****************************************************************************
 * FILE: ExampleScenario10.java
 * Copyright © 2025 Anka Technologies.
 * SPDX-License-Identifier: MIT
 * ---------------------------------------------------------------------------
 * Scenario 10 – ML-KEM-1024 Bulk Encrypt / Decrypt (Compact JWE helpers)
 * ---------------------------------------------------------------------------
 *   1. Generate an ML-KEM-1024 key.
 *   2. Export the key’s public metadata to JSON.
 *   3. Encrypt a local file → Compact JWE saved as UTF-8 text.
 *   4. Decrypt that JWE back to plaintext.
 *   5. Validate round-trip integrity and print rich server metadata.
 *
 * All transient artefacts are stored in <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.ExportedKeySpec;
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;10 – ML-KEM-1024 Bulk Helpers (Compact&nbsp;JWE)</h2>
 *
 * <p>This scenario demonstrates the <strong>non-streaming</strong> helper
 * methods for RFC&nbsp;7516 <em>Compact&nbsp;JWE</em> using an ML-KEM-1024
 * key:</p>
 * <ol>
 *   <li>Generate an ML-KEM-1024 key.</li>
 *   <li>Export its public metadata.</li>
 *   <li>Encrypt a plaintext file via {@link AnkaSecureSdk#encryptFile(String, Path, Path)}.</li>
 *   <li>Decrypt via {@link AnkaSecureSdk#decryptFile(Path, Path)}.</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 encoding is explicitly specified for deterministic behaviour.</li>
 *   <li>Temporary directories are ensured via {@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 ExampleScenario10 {

    /* ====================================================================== */
    /**
     * Entry-point.
     *
     * @param args ignored
     */
    public static void main(final String[] args) {

        System.out.println("===== SCENARIO 10 START =====");
        System.out.println("""
                Purpose :
                  * Demonstrate Compact JWE helpers with an ML-KEM-1024 key
                  * Steps: generate → export → encrypt → decrypt → validate
                --------------------------------------------------------------""");

        try {
            ensureTempDir(TEMP_DIR);

            Properties props = loadProperties();
            AnkaSecureSdk sdk = authenticate(props);

            runScenario(sdk);

        } catch (Exception ex) {
            fatal("Scenario 10 failed", ex);
        }

        System.out.println("===== SCENARIO 10 END =====");
    }

    /* ====================================================================== */
    /**
     * Executes all steps of Scenario&nbsp;10.
     *
     * @param sdk an authenticated {@link AnkaSecureSdk} instance
     *
     * @throws Exception if any step fails
     */
    private static void runScenario(final AnkaSecureSdk sdk) throws Exception {

        final String kid = "sc10_kem1024_" + System.currentTimeMillis();

        Path plainFile  = TEMP_DIR.resolve("scenario10_plain.txt");
        Path cipherFile = TEMP_DIR.resolve("scenario10_cipher.jwe");
        Path decFile    = TEMP_DIR.resolve("scenario10_dec.txt");
        Path exportJson = TEMP_DIR.resolve("scenario10_keydata.json");

        /* 1 ── sample plaintext ---------------------------------------- */
        FileIO.writeUtf8(
                plainFile,
                "Scenario-10 – ML-KEM-1024 non-streaming encrypt / decrypt.");
        System.out.println("[1] Plaintext ready       -> " + plainFile.toAbsolutePath());

        /* 2 ── create ML-KEM-1024 key ---------------------------------- */
        sdk.generateKey(new GenerateKeySpec()
                .setKid(kid)
                .setKty("ML-KEM")
                .setAlg("ML-KEM-1024"));
        System.out.println("[2] Key generated         -> kid = " + kid);

        /* 3 ── export metadata ---------------------------------------- */
        ExportedKeySpec meta = sdk.exportKey(kid);
        FileIO.writeUtf8(exportJson, toJson(meta));
        System.out.println("[3] Metadata exported     -> " + exportJson.toAbsolutePath());

        /* 4 ── encrypt (Compact JWE) ---------------------------------- */
        EncryptResult encMeta = sdk.encryptFile(kid, plainFile, cipherFile);
        System.out.println(MessageFormat.format(
                "[4] Ciphertext written    -> {0}", cipherFile.toAbsolutePath()));
        printEncryptMeta(encMeta);

        /* 5 ── decrypt (Compact JWE) ---------------------------------- */
        DecryptResultMetadata decMeta = sdk.decryptFile(cipherFile, decFile);
        System.out.println(MessageFormat.format(
                "[5] Decrypted file        -> {0}", decFile.toAbsolutePath()));
        printDecryptMeta(decMeta);

        /* 6 ── validation --------------------------------------------- */
        boolean match = FileIO.readUtf8(plainFile)
                .equals(FileIO.readUtf8(decFile));
        System.out.println(match
                ? "[6] SUCCESS – plaintext matches."
                : "[6] FAILURE – plaintext mismatch!");
    }

    /**
     * Private constructor prevents instantiation.
     */
    private ExampleScenario10() {
        /* utility class – no instantiation */
    }
}

How to run

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

Console milestones

  • ML-KEM-1024 key generation

  • Metadata export → scenario10_keydata.json

  • Compact-JWE encryption → scenario10_cipher.jwe

  • Decryption → scenario10_dec.txt

  • SUCCESS message confirming byte-perfect round-trip


Where next?