Skip to content

Flow 9 -- RSA-2048 ➜ ML-DSA-65 Compact-JWS Re-Sign (Non-Streaming)

This scenario shows how to upgrade an existing signature from a classical RSA scheme to a post-quantum ML-DSA-65 key without re-uploading the original data:

  • Generate an RSA-2048 key (oldKid).

  • Sign a file → compact JWS.

  • Generate an ML-DSA-65 key (newKid).

  • Re-sign the very same payload RSA ➜ ML-DSA-65 on the server.

  • Verify both the old and the new signature.

Key points

  • Uses the helper trio signFile → verifySignature → resignFile (entire token handled in memory).

  • Demonstrates a zero-plaintext signature migration---only the detached JWS leaves the service.

  • Produces rich telemetry for every step (key actually used, algorithm, warnings).

When to use it

  • Long-term manifest hardening -- refresh existing RSA signatures in software bills of materials or firmware bundles so they remain trustworthy after quantum breakthroughs.

  • Regulatory migrations -- meet PQC deadlines without touching or re-hashing the original artefacts.

  • Hybrid release workflows -- keep legacy RSA signatures for backward compatibility while attaching a forward-secure ML-DSA token in a single API pass.

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/ExampleScenario9.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.GenerateKeySpec;
import co.ankatech.ankasecure.sdk.model.ResignResult;
import co.ankatech.ankasecure.sdk.model.SignResult;
import co.ankatech.ankasecure.sdk.model.VerifySignatureResult;
import co.ankatech.ankasecure.sdk.util.FileIO;

import java.nio.file.Path;
import java.util.Properties;

import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;

/**
 * Scenario 9 — RSA-2048 → ML-DSA-65 Bulk Re-sign (Compact JWS).
 *
 * <p>This scenario migrates a Compact&nbsp;JWS signature from a classical
 * RSA-2048 key to a post-quantum ML-DSA-65 key without uploading the payload
 * again:</p>
 *
 * <ol>
 *   <li>Generate an RSA-2048 key and sign a file.</li>
 *   <li>Verify the RSA-based JWS.</li>
 *   <li>Generate an ML-DSA-65 key.</li>
 *   <li>Re-sign the payload on the server (RSA &rarr; ML-DSA).</li>
 *   <li>Verify the new ML-DSA JWS.</li>
 * </ol>
 *
 * <p><b>Implementation notes (Java&nbsp;21+):</b></p>
 * <ul>
 *   <li>Filesystem APIs rely on {@link java.nio.file.Path}.</li>
 *   <li>UTF-8 encoding is enforced explicitly for deterministic behaviour.</li>
 *   <li>Temporary artefacts reside under <kbd>temp_files/</kbd>.</li>
 * </ul>
 *
 * <p><b>Thread-safety:</b> this class is stateless and immutable.</p>
 *
 * @author ANKATech Solutions Inc.
 * @since 3.0.0
 * @see ExampleUtil
 * @see AuthenticatedSdk
 */
public final class ExampleScenario9 {

    private static final Path DATA_FILE = TEMP_DIR.resolve("scenario9_data.txt");
    private static final Path OLD_JWS   = TEMP_DIR.resolve("scenario9_rsa.jws");
    private static final Path NEW_JWS   = TEMP_DIR.resolve("scenario9_mldsa.jws");

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

    /**
     * Runs the RSA-2048 to ML-DSA-65 compact JWS re-sign 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(final String[] args) {

        System.out.println("===== SCENARIO 9 START =====");
        System.out.println("""
                Purpose :
                  * Re-sign a Compact JWS from RSA-2048 to ML-DSA-65 (non-streaming helpers)
                  * Demonstrates signFile -> verifySignature -> resignFile -> verifySignature
                Steps   :
                  1) Generate RSA-2048 key
                  2) Sign payload (compact JWS)
                  3) Verify RSA-based JWS
                  4) Generate ML-DSA-65 key
                  5) Re-sign payload on server
                  6) Verify ML-DSA JWS
                --------------------------------------------------------------""");

        try {
            ensureTempDir(TEMP_DIR);

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

            runScenario(sdk);

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

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

    /**
     * Executes the 6-step RSA-2048 to ML-DSA-65 re-sign scenario.
     *
     * <ol>
     *   <li>Create sample data file.</li>
     *   <li>Generate RSA-2048 key and sign.</li>
     *   <li>Verify RSA-based JWS.</li>
     *   <li>Generate ML-DSA-65 key.</li>
     *   <li>Re-sign payload on server.</li>
     *   <li>Verify new ML-DSA JWS.</li>
     * </ol>
     *
     * @param sdk an authenticated {@link AuthenticatedSdk} instance;
     *            must not be {@code null}
     * @throws Exception if any operation fails
     */
    private static void runScenario(final AuthenticatedSdk sdk) throws Exception {

        /* 1 -- sample data -------------------------------------------------- */
        FileIO.writeUtf8(
                DATA_FILE,
                "Scenario-9 - bulk re-sign RSA-2048 -> ML-DSA-65.");
        System.out.println("[1] Data file               -> " + DATA_FILE.toAbsolutePath());

        /* 2 -- RSA-2048 key + sign ------------------------------------------ */
        String rsaKid = "sc9_rsa2048_" + System.currentTimeMillis();
        generateKey(sdk, rsaKid, "RSA", "RSA-2048");

        SignResult rsaSign = sdk.signFile(rsaKid, DATA_FILE, OLD_JWS);
        System.out.println("[2] RSA-2048 JWS created    -> " + OLD_JWS.toAbsolutePath());
        printSignMeta("RSA-2048 SIGN", rsaSign);

        VerifySignatureResult rsaVerify = sdk.verifySignature(OLD_JWS);
        printVerifyMeta("RSA-2048 VERIFY", rsaVerify);

        /* 3 -- ML-DSA-65 key ------------------------------------------------ */
        String dsaKid = "sc9_mldsa65_" + System.currentTimeMillis();
        generateKey(sdk, dsaKid, "ML-DSA", "ML-DSA-65");

        /* 4 -- re-sign payload ---------------------------------------------- */
        ResignResult reMeta = sdk.resignFile(dsaKid, OLD_JWS, NEW_JWS);
        System.out.println("[4] ML-DSA-65 JWS created   -> " + NEW_JWS.toAbsolutePath());
        printResignMeta(reMeta);

        /* 5 -- verify new JWS ----------------------------------------------- */
        VerifySignatureResult dsaVerify = sdk.verifySignature(NEW_JWS);
        printVerifyMeta("ML-DSA-65 VERIFY", dsaVerify);

        System.out.println(dsaVerify.isValid()
                ? "[6] SUCCESS - new JWS is valid."
                : "[6] FAILURE - new JWS is INVALID.");
    }

    private static void generateKey(final AuthenticatedSdk sdk,
                                    final String kid,
                                    final String kty,
                                    final String alg) throws AnkaSecureSdkException {

        sdk.generateKey(new GenerateKeySpec()
                .setKid(kid)
                .setKty(kty)
                .setAlg(alg));
        System.out.printf("    * Key generated -> kid=%s, alg=%s%n", kid, alg);
    }
}

How to run


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

Console milestones

  • RSA-2048 key generation & compact-JWS signature

  • Successful verification of the RSA signature

  • ML-DSA-65 key generation

  • Server-side re-sign → new ML-DSA compact JWS

  • Verification of the new post-quantum signature → SUCCESS


Where next?