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
*/
/* *****************************************************************************
* FILE: ExampleScenario9.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.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.*;
/**
* <h2>Scenario 9 – RSA-2048 → ML-DSA-65 Bulk Re-sign (Compact JWS)</h2>
*
* <p>This scenario migrates a Compact 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 → ML-DSA).</li>
* <li>Verify the new ML-DSA JWS.</li>
* </ol>
*
* <p><b>Implementation notes (Java 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 – Security Engineering
* @since 2.1.0
*/
public final class ExampleScenario9 {
/* --------------------------------------------------------------------- */
/** Working artefacts. */
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");
/* ===================================================================== */
/** Entry-point. */
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();
AnkaSecureSdk sdk = authenticate(props);
runScenario(sdk);
} catch (Exception ex) {
fatal("Scenario 9 failed", ex);
}
System.out.println("===== SCENARIO 9 END =====");
}
/* ===================================================================== */
/**
* Executes Scenario 9.
*
* @param sdk an authenticated {@link AnkaSecureSdk} instance
* @throws Exception if any operation fails
*/
private static void runScenario(final AnkaSecureSdk 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.");
}
/* ---------- helper: key generation ----------------------------------- */
private static void generateKey(final AnkaSecureSdk 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);
}
/** Utility class – no instantiation. */
private ExampleScenario9() {
/* no-instantiation */
}
}
How to run
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