Skip to content

Flow 23 --- RSA-2048 → ML-KEM-768 Immediate Rotation

This scenario shows an in-place algorithm migration from a classical RSA-2048 encryption key to a post-quantum ML-KEM-768 key — a same-purpose rotation (both serve ENCRYPT_DECRYPT). The key identifier (kid) is FIXED across rotation — rotation advances the key material to a new version under the same kid — and the same KID keeps working transparently:

  1. Generate an RSA-2048 encryption key (encrypt/decrypt, purpose ENCRYPT_DECRYPT).

  2. Rotate the key material to ML-KEM-768 in one API call (rotateKey). Both algorithms serve the same invariant purpose, so the rotation passes the purpose gate and the capability set is preserved. The kid does not change.

  3. Encrypt a plaintext with the same KID and observe that the platform resolves it to the new PRIMARY material (ML-KEM-768).

Key points

  • Invariant purpose: the Stable KID's KeyPurpose (ENCRYPT_DECRYPT) is fixed for the lifetime of the identity. RSA-2048 and ML-KEM-768 both serve it, so the rotation is admissible and the capabilities are preserved — there is no capability reduction and no acknowledgment field. A rotation whose target could not serve the purpose (e.g. a signing-only ML-DSA material) would be rejected with 422 rotation-purpose-violation.

  • Zero downtime: the kid stays valid (and unchanged) while the cryptography is instantly upgraded.

  • The SDK's response reports the same kid and the algorithm now in effect (ML-KEM-768).

  • Works for any same-purpose algorithm migration (P-384 encryption → ML-KEM-768, RSA-3072 signing → FALCON-1024, ...).

  • Perfect fit for brown-field services that must migrate to PQC without touching client code.

When to use it

  • Regulatory cut-overs---move to quantum-safe algorithms overnight yet keep the same identifiers alive.

  • Gradual roll-outs---encrypt new data with the new PRIMARY material while historical tokens continue to decrypt with the RETIRED material version.

  • Secrets-rotation policies---rotate keys on schedule (or on compromise) without ever re-encrypting data client-side.

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/ExampleScenario23.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.secure.client.model.KeyRequest;
import co.ankatech.secure.client.model.RotationMaterialSpec;
import co.ankatech.ankasecure.sdk.model.KeyMetadata;

import java.nio.file.Files;
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;

/**
 * Scenario 23 — Same-purpose algorithm migration (RSA-2048 → ML-KEM-768).
 *
 * <p>This scenario demonstrates an in-place algorithm migration within one
 * invariant purpose: an RSA-2048 encryption key (purpose ENCRYPT_DECRYPT) is
 * rotated to ML-KEM-768 (also ENCRYPT_DECRYPT). Both algorithms serve the same
 * purpose, so the rotation passes the purpose gate and the capability set is
 * preserved &mdash; there is no capability-reduction acknowledgment.</p>
 *
 * @author ANKATech Solutions Inc.
 * @since 3.0.0
 * @see ExampleUtil
 * @see AuthenticatedSdk
 */
public final class ExampleScenario23 {

    private static final String RSA_KID =
            "scenario23_rsa_" + System.currentTimeMillis();

    /** No instantiation &mdash; this class only exposes {@link #main(String[])}. */
    private ExampleScenario23() { }

    /**
     * Runs the same-purpose algorithm-migration rotation scenario.
     *
     * <p>Loads CLI properties, authenticates against ANKASecure&copy;,
     * 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(String[] args) {
        System.out.println("===== SCENARIO 23 START =====");
        System.out.println("""
                Purpose :
                  * Provision an RSA-2048 encryption key and rotate its material to ML-KEM-768.
                  * Demonstrate a same-purpose migration (both serve ENCRYPT_DECRYPT).
                  * Encrypt with the same KID and observe the new PRIMARY material.
                Steps   :
                  1) Generate RSA-2048 encryption key (purpose: ENCRYPT_DECRYPT)
                  2) Rotate material to ML-KEM-768 (same purpose), kid unchanged
                  3) Encrypt plaintext using the same KID
                --------------------------------------------------------------""");

        try {
            Files.createDirectories(TEMP_DIR);

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

            runScenario(sdk);

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

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

    /* ====================================================================== */
    private static void runScenario(AuthenticatedSdk sdk)
            throws AnkaSecureSdkException {

        /* 1 – RSA-2048 key ------------------------------------------------ */
        sdk.generateKey(new KeyRequest()
                .kid(RSA_KID)
                .kty("RSA")
                .alg("RSA-2048")
                .keyOps(List.of("encrypt", "decrypt")));
        System.out.println("[1] RSA-2048 key generated   -> kid = " + RSA_KID);

        /* 2 – rotate material to ML-KEM-768 (kid unchanged, same purpose) -- */
        KeyMetadata rotated = sdk.rotateKey(
                RSA_KID,
                new RotationMaterialSpec()
                        .kty("ML-KEM")
                        .alg("ML-KEM-768"));  // same purpose (ENCRYPT_DECRYPT) → passes the purpose gate

        Objects.requireNonNull(rotated, "Rotation must return key metadata");
        System.out.println("[2] Material rotated         -> kid = " + rotated.getKid() + " (unchanged)");
        System.out.println("    Purpose preserved: ENCRYPT_DECRYPT → ENCRYPT_DECRYPT");

        /* 3 – encrypt with original KID -------------------------------- */
        byte[] plaintext = "Post-rotation encryption demo".getBytes(UTF_8);
        EncryptResult enc = sdk.encrypt(RSA_KID, plaintext);

        System.out.println("[3] Encryption metadata");
        System.out.println("    Key requested    : " + nullSafe(enc.getKeyRequested()));
        System.out.println("    Material version : " + versionSafe(enc.getMaterialVersion()));
        System.out.println("    Algorithm used   : " + nullSafe(enc.getAlgorithmUsed()));
    }
}

How to run

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

Console milestones

  • RSA-2048 encryption key generated (purpose: ENCRYPT_DECRYPT)

  • Material rotated to ML-KEM-768 (rotateKey), a same-purpose migration; the kid is unchanged

  • Purpose preserved: ENCRYPT_DECRYPT → ENCRYPT_DECRYPT (no capability reduction)

  • Encryption call uses the same KID — the platform resolves it to the new PRIMARY material

  • Algorithm used reports ML-KEM-768+... confirming the in-place PQC migration


Where next?

© 2025 ANKATech Solutions INC. All rights reserved.