Flow 4 – Streaming Re-encrypt RSA-2048 ➜ ML-KEM-1024
This scenario performs a zero-plaintext-exposure migration from a classical RSA key to a post-quantum ML-KEM key:
- Generate an RSA-2048 key.
- Stream-encrypt a file (detached JWET).
- Generate an ML-KEM-1024 key.
- Stream-re-encrypt the ciphertext on the server (RSA → KEM).
- Stream-decrypt with the ML-KEM key.
- Validate the recovered plaintext.
Key points
- End-to-end streaming pipeline--encryptFileStream → reencryptFileStream → decryptFileStream--so even multi-gigabyte files never hit memory in one piece.
- Uses a detached JWET (General-JSON header + raw binary part), eliminating Base64 overhead during transfer.
- Zero-plaintext migration – the server converts RSA ciphertext to ML-KEM ciphertext without ever decrypting or returning sensitive data.
- Demonstrates live crypto-agility: two independent keys, negotiated algorithms, and rich lifecycle telemetry (actual key used, warnings, migration-mode flag).
When to use it
- Post-quantum retrofits – you already have archives protected with RSA and need to harden them against future quantum attacks without re-downloading or re-encrypting locally.
- Massive data lakes & backups – streaming re-encryption lets you upgrade multi-terabyte stores in place, one pass, no staging disk, no plaintext ever written.
- Regulated environments – proves to auditors that secret material stays encrypted end-to-end while cryptographic strength is increased.
- Maintenance windows – server-side key migration is quicker than a full decrypt/re-encrypt cycle, reducing downtime for business-critical datasets.
Shared helper —
ExampleUtil
(configuration-loading, authentication, JSON, etc.).
If you haven’t copied it yet, fetch example_util.md and place the Java file beside the scenario sources.
Complete Java implementation
src/main/java/co/ankatech/ankasecure/sdk/examples/ExampleScenario4.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
*/
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.model.ReencryptResult;
import co.ankatech.ankasecure.sdk.util.FileIO;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;
/**
* <h2>Scenario 4 – Streaming Re-encrypt (RSA-2048 → ML-KEM-1024)</h2>
*
* <p>This scenario demonstrates how to migrate classical RSA-encrypted data to
* a post-quantum ML-KEM-encrypted format <em>without exposing plaintext</em>.
* All cryptographic operations use the <strong>streaming</strong> API, meaning
* the service exchanges a two-part <em>detached JWE</em>
* (General-JSON header + raw ciphertext stream).</p>
*
* <ol>
* <li>Generate an {@code RSA-2048} key.</li>
* <li>Stream-encrypt a file (detached JWE).</li>
* <li>Generate an {@code ML-KEM-1024} key.</li>
* <li>Re-encrypt the ciphertext on the server (RSA → KEM).</li>
* <li>Stream-decrypt with the KEM key.</li>
* <li>Validate round-trip integrity.</li>
* </ol>
*
* <p>Temporary artefacts are placed under <kbd>temp_files/</kbd>.</p>
*
* <p><b>Implementation notes (Java 21+):</b></p>
* <ul>
* <li>All file operations rely on the {@link java.nio.file.Path} API.</li>
* <li>Encoding is explicitly set to UTF-8 for deterministic behaviour.</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 ExampleScenario4 {
/* ====================================================================== */
/**
* Entry-point.
*
* @param args ignored
*/
public static void main(final String[] args) {
System.out.println("===== SCENARIO 4 START =====");
System.out.println("""
Purpose :
* RSA-2048 → ML-KEM-1024 streaming re‑encryption
* Demonstrates detached‑JWE pipeline (encrypt → re‑encrypt → decrypt)
Steps :
1) Generate RSA‑2048 key
2) Create dummy payload
3) Encrypt payload (detached JWE)
4) Generate ML‑KEM‑1024 key
5) Re‑encrypt ciphertext stream
6) Decrypt KEM ciphertext
7) Validate integrity
--------------------------------------------------------------""");
try {
ensureTempDir(TEMP_DIR);
Properties props = loadProperties();
AnkaSecureSdk sdk = authenticate(props);
runScenario(sdk);
} catch (Exception ex) {
ex.printStackTrace();
fatal("Scenario 4 failed", ex);
}
System.out.println("===== SCENARIO 4 END =====");
}
/* ====================================================================== */
/**
* Executes a streaming re-encryption scenario for large files using the AnkaSecureSdk.
* <p>
* This method demonstrates end-to-end streaming encryption under RSA-2048,
* one-pass re-encryption to ML-KEM-1024, and streaming decryption, all without
* buffering entire files in memory.
*
* @param sdk the AnkaSecureSdk instance to use for all operations
* @throws Exception on any I/O or cryptographic failure
*/
private static void runScenario(final AnkaSecureSdk sdk) throws Exception {
/* 1 ── prepare large plaintext file (e.g., 1 Mb) --------------------- */
Path plainFile = TEMP_DIR.resolve("scenario4_plain_large.bin");
byte[] dummy = new byte[1 * 1024 * 1024]; // zero-filled 1 Mb buffer
FileIO.writeBytes(plainFile, dummy);
System.out.println("[1] Plaintext ready -> " + plainFile.toAbsolutePath());
/* 2 ── generate RSA-2048 key ----------------------------------------- */
String rsaKid = "sc4_rsa_" + System.currentTimeMillis();
sdk.generateKey(new GenerateKeySpec()
.setKid(rsaKid)
.setKty("RSA")
.setAlg("RSA-2048"));
System.out.println("[2] RSA-2048 key created -> kid = " + rsaKid);
/* 3 ── streaming encrypt under RSA-2048 ------------------------------ */
Path rsaCipher = TEMP_DIR.resolve("scenario4_rsa_large.enc");
EncryptResult rsaEncMeta = sdk.encryptFileStream(rsaKid, plainFile, rsaCipher);
System.out.println("[3] RSA ciphertext written -> " + rsaCipher.toAbsolutePath());
printEncryptMeta(rsaEncMeta);
/* 4 ── generate ML-KEM-1024 key ------------------------------------- */
String kemKid = "sc4_kem_" + System.currentTimeMillis();
sdk.generateKey(new GenerateKeySpec()
.setKid(kemKid)
.setKty("ML-KEM")
.setAlg("ML-KEM-1024"));
System.out.println("[4] ML-KEM-1024 key created -> kid = " + kemKid);
/* 5 ── one-pass streaming re-encrypt RSA → ML-KEM ---------------------- */
Path kemCipher = TEMP_DIR.resolve("scenario4_kem_large.enc");
ReencryptResult reMeta = sdk.reencryptFileStream(kemKid, rsaCipher, kemCipher);
System.out.println("[5] Re-encrypted file -> " + kemCipher.toAbsolutePath());
printReencryptMeta(reMeta);
/* 6 ── streaming decrypt under ML-KEM-1024 --------------------------- */
Path decFile = TEMP_DIR.resolve("scenario4_large_decrypted.bin");
DecryptResultMetadata decMeta = sdk.decryptFileStream(kemCipher, decFile);
System.out.println("[6] Decrypted file -> " + decFile.toAbsolutePath());
printDecryptMeta(decMeta);
/* 7 ── validation via streaming compare ------------------------------ */
try (InputStream in1 = Files.newInputStream(plainFile);
InputStream in2 = Files.newInputStream(decFile)) {
boolean match = streamsAreEqual(in1, in2);
System.out.println(match
? "[7] Validation OK – plaintext matches."
: "[7] WARNING – plaintext mismatch!");
}
}
/**
* Private constructor prevents instantiation.
*/
private ExampleScenario4() {
/* utility class – no instantiation */
}
}
How to run
Sequence to watch:
-
RSA-2048 key creation & stream-encryption.
-
ML-KEM-1024 key creation.
-
Server-side re-encrypt (RSA → KEM).
-
Stream-decryption with KEM key.
-
Validation OK proving lossless migration.