Flow 15 --- Compact-Token Rotation (Re-Encrypt)
This scenario migrates a Compact JWE from a classical RSA-3072 key to a post-quantum ML-KEM-768 key --- entirely in memory and without exposing the plaintext:
-
Resolve the pre-provisioned RSA-3072 key (decrypt-capable).
-
Resolve the pre-provisioned ML-KEM-768 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.
Prerequisites
The flow examples are data-plane only. They never create, rotate, or delete keys — they resolve pre-provisioned playground keys and operate on them. Before running any flow example you must:
- Provision a fresh
demo-cliplayground. Use theankasecure-demo-provisioningtool to provision thedemo-cliplayground. This seeds the cryptographic keys the examples operate on and thecli-reference@demo-cliactor that holds capability grants on them. - Use the emitted
cli.properties. The provisioning tool writes acli.propertiesfile that carries theankasecure.demo.kidscatalogue line — the comma-separated list of provisioned key ids, in YAML file order. The examples load this file to discover which keys exist. - Authenticate as
cli-reference@demo-cli. Authenticate the SDK as thecli-reference@demo-cliactor — the all-operations actor of thedemo-cliplayground — so the resolved keys carry the capability grants each operation needs.
Key selection in the examples is by algorithm or by exact name:
kidForAlgorithm(alg)returns a pre-provisioned key id matching the requested algorithm — used by the single-key operation flows.requireKid(name)asserts that an exact, named cross-kid endpoint is present in theankasecure.demo.kidscatalogue — used by the cross-kid REENCRYPT / RESIGN flows that must operate on a specific granted source/target pair.
If cli.properties is missing the ankasecure.demo.kids line, the examples fail fast with a clear message instructing you to (re-)provision the demo-cli playground — they will not silently fall back.
PKCS#7 / CMS examples (Flow 19 & Flow 20)
The PKCS#7 interop examples operate on a packaged, non-sensitive CMS EnvelopedData fixture — you do not supply a PKCS#7 file:
- Fixture:
src/main/resources/pkcs7/enveloped-data-sample.p7m, loaded from the classpath byPkcs7ExampleFixture. It is a single-recipient, zero-signer EnvelopedData artifact from the QA test signer; it carries no private key and no secret. - Flow 19 (analysis) needs no key. Structural analysis (
analyzePkcs7/analyzePkcs7Stream) inspects the CMS envelope metadata only, so it runs with no decryption key and no extra configuration. - Flow 20 (conversion) needs a pre-provisioned recipient key. Converting EnvelopedData to JWE requires the platform to decrypt the envelope, so it needs the recipient's private key referenced by the
pkcs7.decryptionKidproperty incli.properties. That key is matched by issuer DN + serial number and is imported by a control-plane step (outside the data-plane SDK); until it is provisioned, Flow 20's conversion calls fail at the server while Flow 19 still runs. - Data-plane only. Like every flow example, these never import keystores or perform any key-lifecycle operation.
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/ExampleScenario15.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.AuthenticatedSdk;
import co.ankatech.ankasecure.sdk.exception.AnkaSecureSdkException;
import co.ankatech.ankasecure.sdk.model.EncryptResult;
import co.ankatech.ankasecure.sdk.model.ReencryptResult;
import java.util.Base64;
import java.util.Objects;
import java.util.Properties;
import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Scenario 15 — Compact-Token Rotation (RSA-3072 → ML-KEM-768).
*
* <p>This example migrates a Compact JWE from a classical RSA-3072 key to
* a post-quantum ML-KEM-768 key <em>entirely in memory</em>. The original
* plaintext is never exposed to the client.</p>
*
* <p>The SDK is data-plane-only: it does not create keys. This scenario operates
* on the <strong>pre-provisioned granted cross-kid pair</strong> of the
* {@code demo-cli} playground — source {@code cli-reencrypt2-source}
* ({@code RSA-3072}) and target {@code cli-reencrypt2-target} ({@code ML-KEM-768}).
* The {@code cli-reference} actor holds the cross-kid REENCRYPT grant on this pair
* plus the atomic encrypt/decrypt grants on each endpoint.</p>
*
* <h3>Prerequisites</h3>
* <ul>
* <li>A valid encrypted CLI initialisation file (credentials).</li>
* <li>An access-token with the scopes:
* <code>secure.encrypt</code>,
* <code>secure.reencrypt</code>.</li>
* <li>The {@code demo-cli} playground provisioned with the
* {@code cli-reencrypt2-source} / {@code cli-reencrypt2-target} grant.</li>
* </ul>
*
* @author ANKATech Solutions Inc.
* @since 3.0.0
* @see ExampleUtil
* @see ExamplePlaygroundKeys
* @see AuthenticatedSdk
*/
public final class ExampleScenario15 {
/** No instantiation — this class only exposes {@link #main(String[])}. */
private ExampleScenario15() { }
/**
* Runs the compact-token rotation scenario.
*
* <p>Loads CLI properties, authenticates against ANKASecure©,
* and delegates to the scenario logic. On any unrecoverable error
* the JVM terminates via
* {@link ExampleUtil#fatal(String, Throwable)}.</p>
*
* @param args command-line arguments (ignored)
*/
public static void main(final String[] args) {
System.out.println("===== SCENARIO 15 START =====");
System.out.println("""
Purpose :
* Rotate a Compact JWE from RSA-3072 to ML-KEM-768.
* Uses the pre-provisioned granted cross-kid pair
(cli-reencrypt2-source / cli-reencrypt2-target)
Steps :
1) Resolve granted RSA-3072 source + ML-KEM-768 target kids.
2) Encrypt plaintext ➜ Compact JWE (RSA).
3) Re-encrypt JWE ➜ Compact JWE (ML-KEM).
4) Compare headers & print metadata.
--------------------------------------------------------------""");
try {
Properties props = loadProperties();
AuthenticatedSdk sdk = authenticate(props);
ExamplePlaygroundKeys playground = ExamplePlaygroundKeys.from(props, sdk);
runScenario(sdk, playground);
} catch (Exception ex) {
fatal("Scenario 15 failed", ex);
}
System.out.println("===== SCENARIO 15 END =====");
}
/* ====================================================================== */
/**
* Executes encryption and token rotation on the granted cross-kid pair.
*
* @param sdk an authenticated {@link AuthenticatedSdk} instance
* @param playground the pre-provisioned playground key resolver; must not be {@code null}
* @throws AnkaSecureSdkException if any operation fails
*/
private static void runScenario(final AuthenticatedSdk sdk,
final ExamplePlaygroundKeys playground)
throws AnkaSecureSdkException {
/* 1 ── resolve granted cross-kid pair (data-plane SDK: no key creation) */
final String srcKid = playground.requireKid("cli-reencrypt2-source"); // RSA-3072
final String dstKid = playground.requireKid("cli-reencrypt2-target"); // ML-KEM-768
System.out.println("[1] Source kid (RSA-3072) -> " + srcKid);
System.out.println("[1] Target kid (ML-KEM-768) -> " + dstKid);
/* 2 ── Encrypt plaintext ➜ Compact JWE (RSA) ------------------ */
byte[] plaintext = "Compact-token rotation demo".getBytes(UTF_8);
EncryptResult encRes = sdk.encrypt(srcKid, plaintext);
String oldJwe = encRes.getJweToken();
System.out.println("[2] Original JWE header = " + protectedHeader(oldJwe));
/* 3 ── Re-encrypt JWE ➜ ML-KEM key ---------------------------- */
ReencryptResult renRes = sdk.reencrypt(dstKid, oldJwe);
String newJwe = renRes.getJweToken();
System.out.println("[3] Rotated JWE header = " + protectedHeader(newJwe));
printReencryptMeta(renRes);
/* 4 ── 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)";
}
}
}
How to run
Console milestones
-
RSA-3072 & ML-KEM-768 keys resolved from the playground
-
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.