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:
-
Generate a post-quantum key ---
kty = ML-KEM
,alg = ML-KEM-1024
. -
Export its full JSON metadata (includes
publicKey
Base-64). -
Encrypt a plaintext file with
encryptFileUtilityStream
, feeding only the public key (detached JWE on disk). -
Decrypt that ciphertext in streaming mode via
decryptFileStream(kid, ...)
. -
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 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
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.