Flow 21 --- Compact-Token Rotation (Re-Encrypt)
This scenario migrates a Compact JWE from a classical RSA key to a post-quantum ML-KEM key --- entirely in memory and without exposing the plaintext:
-
Generate an RSA-2048 key (decrypt-capable).
-
Generate an ML-KEM-1024 key (encrypt-capable).
-
Encrypt a UTF-8 message → Compact JWE under the RSA key.
-
Re-encrypt that token so the payload is now protected by the ML-KEM key (server-side, zero plaintext exposure).
-
Compare the protected-header segments and print rich metadata to prove the rotation.
Key points
In-place token upgrade --- perfect for crypto-agility or PQC migrations.
Uses the SDK's
reencrypt()
helper: no manual JWE parsing needed.Works completely in RAM: ideal for micro-services, event handlers, or serverless functions.
Shows how to inspect the Base64URL header to verify the new algorithm/KID.
When to use it
-
Immediate crypto upgrades when a recipient still holds legacy tokens that must be secured with stronger or PQ algorithms.
-
Zero-downtime rotations in pipelines where decrypting and re-encrypting on the client side is impossible or too slow.
-
Regulatory mandates that require transparent key roll-overs without touching the original plaintext.
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/ExampleScenario21.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: ExampleScenario21.java
* ****************************************************************************/
package co.ankatech.ankasecure.sdk.examples;
import co.ankatech.ankasecure.sdk.AnkaSecureSdk;
import co.ankatech.ankasecure.sdk.exception.AnkaSecureSdkException;
import co.ankatech.ankasecure.sdk.model.EncryptResult;
import co.ankatech.ankasecure.sdk.model.GenerateKeySpec;
import co.ankatech.ankasecure.sdk.model.ReencryptResult;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* <h2>Scenario 21 – Compact-Token Rotation (RSA-2048 ➜ ML-KEM-1024)</h2>
*
* <p>This example migrates a Compact JWE from a classical RSA-2048 key to
* a post-quantum ML-KEM-1024 key <em>entirely in memory</em>. The original
* plaintext is never exposed to the client.</p>
*
* <h3>Prerequisites</h3>
* <ul>
* <li>A valid encrypted CLI initialisation file (credentials).</li>
* <li>An access-token with the scopes:
* <code>key_management.generateKey</code>,
* <code>secure.encrypt</code>,
* <code>secure.reencrypt</code>.</li>
* </ul>
*
* @author ANKATech – Security Engineering
* @since 2.1.0
*/
public final class ExampleScenario21 {
/* Dynamically generated key IDs ensure isolation across runs. */
private static final String SRC_KID = "sc21_RSA2048_" + System.currentTimeMillis();
private static final String DST_KID = "sc21_MLKEM1024_" + System.currentTimeMillis();
/* ====================================================================== */
/** Entry-point. */
public static void main(final String[] args) {
System.out.println("===== SCENARIO 21 START =====");
System.out.println("""
Purpose :
* Rotate a Compact JWE from RSA-2048 to ML-KEM-1024.
Steps :
1) Generate RSA-2048 key (decrypt).
2) Generate ML-KEM-1024 key (encrypt).
3) Encrypt plaintext ➜ Compact JWE (RSA).
4) Re-encrypt JWE ➜ Compact JWE (ML-KEM).
5) Compare headers & print metadata.
--------------------------------------------------------------""");
try {
Properties props = loadProperties();
AnkaSecureSdk sdk = authenticate(props);
runScenario(sdk);
} catch (Exception ex) {
fatal("Scenario 21 failed", ex);
}
System.out.println("===== SCENARIO 21 END =====");
}
/* ====================================================================== */
/** Executes key creation, encryption, and token rotation. */
private static void runScenario(final AnkaSecureSdk sdk)
throws AnkaSecureSdkException {
/* 1 ── RSA-2048 key (decrypt-capable) -------------------------- */
sdk.generateKey(new GenerateKeySpec()
.setKid(SRC_KID)
.setKty("RSA")
.setAlg("RSA-2048")
.setKeyOps(List.of("encrypt", "decrypt")));
System.out.println("[1] RSA-2048 key generated -> kid = " + SRC_KID);
/* 2 ── ML-KEM-1024 key (encrypt-capable) ---------------------- */
sdk.generateKey(new GenerateKeySpec()
.setKid(DST_KID)
.setKty("ML-KEM")
.setAlg("ML-KEM-1024")
.setKeyOps(List.of("encrypt", "decrypt")));
System.out.println("[2] ML-KEM-1024 key generated -> kid = " + DST_KID);
/* 3 ── Encrypt plaintext ➜ Compact JWE (RSA) ------------------ */
byte[] plaintext = "Compact-token rotation demo".getBytes(UTF_8);
EncryptResult encRes = sdk.encrypt(SRC_KID, plaintext);
String oldJwe = encRes.getJweToken();
System.out.println("[3] Original JWE header = " + protectedHeader(oldJwe));
/* 4 ── Re-encrypt JWE ➜ ML-KEM key ---------------------------- */
ReencryptResult renRes = sdk.reencrypt(DST_KID, oldJwe);
String newJwe = renRes.getJweToken();
System.out.println("[4] Rotated JWE header = " + protectedHeader(newJwe));
printReencryptMeta(renRes);
/* 5 ── Verify header changed ---------------------------------- */
boolean changed = !Objects.equals(
protectedHeader(oldJwe),
protectedHeader(newJwe));
System.out.println(changed
? "[✔] SUCCESS – header changed as expected."
: "[✘] FAILURE – header did not change!");
}
/** Extracts and Base64URL-decodes the protected-header segment. */
private static String protectedHeader(final String compactJwe) {
int dot = compactJwe.indexOf('.');
if (dot <= 0) return "(invalid)";
try {
byte[] decoded = Base64.getUrlDecoder()
.decode(compactJwe.substring(0, dot));
return new String(decoded, UTF_8);
} catch (IllegalArgumentException ex) {
return "(decode error)";
}
}
/** Utility class – no instantiation. */
private ExampleScenario21() { /* no-instantiation */ }
}
How to run
Console milestones
-
RSA-2048 & ML-KEM-1024 keys generated
-
Original Compact-JWE header (RSA) printed
-
Re-encrypted Compact-JWE header (ML-KEM) printed
-
Re-encrypt metadata shows old/new key IDs and algorithm
-
SUCCESS -- protected-header segment changed
Where next?
© 2025 ANKATech Solutions INC. All rights reserved.