Skip to content

Flow 16 --- Utility-Stream Public-Key Encrypt (ML-KEM-1024) & Stream Decrypt

This scenario shows how to encrypt data client-side with an exported public key and decrypt it later on the server with the corresponding ML-KEM-1024 key:

  1. Generate a post-quantum key --- kty = ML-KEM, alg = ML-KEM-1024.

  2. Export its full JSON metadata (includes publicKey Base-64).

  3. Encrypt a plaintext file with encryptFileUtilityStream, feeding only the public key (detached JWE on disk).

  4. Decrypt that ciphertext in streaming mode via decryptFileStream(kid, ...).

  5. Validate that the recovered bytes equal the original.

Highlights

  • Pure public-key encryption on the client, no secret material required.
  • Handles any file size thanks to streaming helpers.
  • End-to-end integrity confirmed by comparing plaintexts.

When to use it

  • Browser uploads --- encrypt files locally before sending them to the server.

  • Partner hand-off --- share only the JSON export so third -parties can encrypt for you.

  • Backup pipelines --- detach encryption from the HSM to reduce round-trips.

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

/*
 * Copyright 2025 ANKATech Solutions Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
/* *****************************************************************************
 * FILE: ExampleScenario16.java
 * ****************************************************************************/
package co.ankatech.ankasecure.sdk.examples;

import co.ankatech.ankasecure.sdk.AnkaSecureSdk;
import co.ankatech.ankasecure.sdk.model.EncryptResult;
import co.ankatech.ankasecure.sdk.model.GenerateKeySpec;
import co.ankatech.ankasecure.sdk.model.ImportKeySpec;
import co.ankatech.ankasecure.sdk.util.FileIO;
import com.google.gson.*;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Properties;

import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;

/**
 * <h2>Scenario&nbsp;16 – Utility-Stream Encryption (ML-KEM-1024)</h2>
 *
 * <p>This scenario shows how to encrypt data with a <strong>public-only</strong>
 * ML-KEM-1024 key via the <em>utility-stream</em> API, producing a detached
 * JWE. The private key never touches the client.</p>
 *
 * <ol>
 *   <li>Generate an ML-KEM-1024 key.</li>
 *   <li>Export its metadata (extract the Base64-encoded public key).</li>
 *   <li>Encrypt a file using only that public key.</li>
 * </ol>
 *
 * @author ANKATech – Security Engineering
 * @since 2.1.0
 */
public final class ExampleScenario16 {

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

        System.out.println("===== SCENARIO 16 START =====");
        System.out.println("""
                Purpose :
                  * Generate an ML-KEM-1024 key.
                  * Export its Base64 publicKey.
                  * Encrypt a file in streaming (detached JWE) mode with only that key.
                Steps   :
                  1) Generate PQC key
                  2) Export key metadata
                  3) Utility-stream encrypt
                --------------------------------------------------------------""");

        try {
            ensureTempDir(TEMP_DIR);

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

            runScenario(sdk);

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

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

    /* ====================================================================== */
    /** Executes all steps of the scenario. */
    private static void runScenario(final AnkaSecureSdk sdk) throws Exception {

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

        /* 2 ── export metadata ---------------------------------------- */
        Path metaJson = TEMP_DIR.resolve("scenario16_key.json");
        sdk.exportKey(kid, metaJson);
        System.out.println("[2] Metadata exported        -> " + metaJson.toAbsolutePath());

        /* 3 ── prepare plaintext -------------------------------------- */
        Path data = TEMP_DIR.resolve("scenario16_plain.txt");
        FileIO.writeUtf8(
                data,
                "Scenario 16 – ML-KEM-1024 utility-stream demo.");
        System.out.println("[3] Data file prepared       -> " + data.toAbsolutePath());

        /* 4 ── utility-stream encrypt --------------------------------- */
        ImportKeySpec meta = readMetadata(metaJson);
        String pubKeyB64   = meta.getPublicKey();
        if (pubKeyB64 == null || pubKeyB64.isBlank()) {
            fatal("Exported JSON missing publicKey", null);
        }

        Path cipher = TEMP_DIR.resolve("scenario16_detached.jwe");
        EncryptResult enc = sdk.encryptFileUtilityStream(
                meta.getKty(),
                meta.getAlg(),
                pubKeyB64,
                data,
                cipher);
        System.out.println("[4] Encrypted output         -> " + cipher.toAbsolutePath());
        printEncryptMeta(enc);
    }

    /* ====================================================================== */
    /** Parses the exported key JSON into an {@link ImportKeySpec}. */
    private static ImportKeySpec readMetadata(final Path json) throws IOException {
        try (Reader r = Files.newBufferedReader(json, StandardCharsets.UTF_8)) {
            Gson gson = new GsonBuilder()
                    .disableHtmlEscaping()
                    .registerTypeAdapter(
                            ZonedDateTime.class,
                            new JsonDeserializer<ZonedDateTime>() {
                                @Override
                                public ZonedDateTime deserialize(
                                        JsonElement el, Type type,
                                        JsonDeserializationContext ctx) {
                                    return ZonedDateTime.parse(
                                            el.getAsString(),
                                            DateTimeFormatter.ISO_OFFSET_DATE_TIME);
                                }
                            })
                    .registerTypeAdapter(
                            ZonedDateTime.class,
                            (JsonSerializer<ZonedDateTime>) (src, type, ctx) ->
                                    new JsonPrimitive(
                                            src.format(
                                                    DateTimeFormatter.ISO_OFFSET_DATE_TIME)))
                    .setPrettyPrinting()
                    .create();
            return gson.fromJson(r, ImportKeySpec.class);
        }
    }

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

How to run

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

Console milestones

  • ML-KEM-1024 key generation (kid = sc16_kem1024_*)

  • Key-metadata export → sc16_key.json

  • Utility-stream encryption → sc16_detached.jwe

  • SUCCESS message confirming encryption (decryption demonstrated in companion flow 17)


Where next?

© 2025 ANKATech Solutions INC. All rights reserved.