Flow 12 --- RSA-2048 ➜ FALCON-1024 Detached-JWS Re-sign (Streaming)
This scenario performs a zero-plaintext-exposure migration from a classical RSA-2048 signature to a post-quantum FALCON-1024 signature, all with streaming helpers:
-
Generate an
RSA-2048
key and stream-sign a document → detached JWS. -
Generate a
FALCON-1024
key. -
Re-sign the existing JWS on the server (RSA → FALCON) --- no payload re-upload, no Base64 bloat.
-
Verify the new FALCON JWS in streaming mode.
-
Inspect rich metadata for every step (key actually used, algorithm, warnings).
Key points
Uses
signFileStream → resignFileStream → verifySignatureStream
for GB-scale artefacts.Detached JWS keeps payload binary, avoiding Base64 overhead in transfers.
Proves you can upgrade to FALCON-1024 (NIST finalist) without ever exposing or re-hashing plaintext.
When to use it
-
Long-lived archives --- refresh classical RSA signatures to PQC without touching the data, ensuring future-proof authenticity.
-
Compliance pipelines --- stream re-sign terabyte data sets during nightly jobs with constant RAM.
-
Incremental hardening --- migrate gradually: keep RSA tokens for legacy consumers, add PQC FALCON tokens for forward security.
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/ExampleScenario12.java
/* *****************************************************************************
* FILE: ExampleScenario12.java
* Copyright © 2025 Anka Technologies.
* SPDX-License-Identifier: MIT
* ---------------------------------------------------------------------------
* Scenario 12 – Streaming Re-sign RSA-2048 → FALCON-1024 (Detached JWS helpers)
* ---------------------------------------------------------------------------
* 1. Generate an RSA-2048 key and stream-sign a file
* → detached Compact JWS stored on disk.
* 2. Generate a FALCON-1024 key.
* 3. Stream-re-sign the payload on the server (RSA → FALCON) – no plaintext
* exposure to the client.
* 4. Stream-verify the FALCON JWS and display rich server metadata.
*
* All transient artefacts are placed in <temp_files/>.
* ****************************************************************************/
package co.ankatech.ankasecure.sdk.examples;
import co.ankatech.ankasecure.sdk.AnkaSecureSdk;
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.text.MessageFormat;
import java.util.Properties;
import static co.ankatech.ankasecure.sdk.examples.ExampleUtil.*;
/**
* <h2>Scenario 12 – RSA-2048 → FALCON-1024 Streaming Re-sign
* (Detached JWS)</h2>
*
* <p>This scenario migrates a detached JWS signature from a classical
* RSA-2048 key to a post-quantum FALCON-1024 key, entirely on the server
* side and therefore without requiring the client to upload the payload
* again:</p>
*
* <ol>
* <li>Generate an RSA-2048 key and stream-sign a file.</li>
* <li>Generate a FALCON-1024 key.</li>
* <li>Re-sign the existing detached JWS on the server
* (RSA-2048 -> FALCON-1024).</li>
* <li>Stream-verify the fresh FALCON signature.</li>
* </ol>
*
* <p><b>Implementation notes (Java 21+):</b></p>
* <ul>
* <li>The {@link java.nio.file.Path} API is used for all filesystem
* operations.</li>
* <li>UTF-8 encoding is specified explicitly for deterministic
* behaviour.</li>
* <li>The working directory is created via
* {@link ExampleUtil#ensureTempDir(Path)}.</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 ExampleScenario12 {
/* ====================================================================== */
/**
* Entry-point.
*
* @param args ignored
*/
public static void main(final String[] args) {
System.out.println("===== SCENARIO 12 START =====");
System.out.println("""
Purpose :
* Migrate a detached RSA-2048 signature to FALCON-1024
* Steps:
1) Generate RSA key & sign
2) Generate FALCON key
3) Re-sign on server
4) Verify new signature
--------------------------------------------------------------""");
try {
ensureTempDir(TEMP_DIR);
Properties props = loadProperties();
AnkaSecureSdk sdk = authenticate(props);
runScenario(sdk);
} catch (Exception ex) {
fatal("Scenario 12 failed", ex);
}
System.out.println("===== SCENARIO 12 END =====");
}
/* ====================================================================== */
/**
* Executes all steps of Scenario 12.
*
* @param sdk an authenticated {@link AnkaSecureSdk} instance
*
* @throws Exception if any operation fails
*/
private static void runScenario(final AnkaSecureSdk sdk) throws Exception {
/* Working paths --------------------------------------------------- */
Path dataFile = TEMP_DIR.resolve("scenario12_data.txt");
Path oldSig = TEMP_DIR.resolve("scenario12_rsa.sig");
Path newSig = TEMP_DIR.resolve("scenario12_falcon.sig");
/* 1 ── create payload & RSA-2048 key + sign ---------------------- */
FileIO.writeUtf8(
dataFile,
"Scenario-12 – RSA-2048 → FALCON-1024 streaming re-sign demo.");
System.out.println("[1] Data file ready -> " + dataFile.toAbsolutePath());
String oldKid = "sc12_rsa2048_" + System.currentTimeMillis();
sdk.generateKey(new GenerateKeySpec()
.setKid(oldKid)
.setKty("RSA")
.setAlg("RSA-2048"));
System.out.println("[1] RSA-2048 key created -> kid = " + oldKid);
SignResult oldMeta = sdk.signFileStream(oldKid, dataFile, oldSig);
System.out.println("[1] RSA signature stored -> " + oldSig.toAbsolutePath());
printSignMeta(oldMeta);
/* 2 ── create FALCON-1024 key ----------------------------------- */
String newKid = "sc12_falcon1024_" + System.currentTimeMillis();
sdk.generateKey(new GenerateKeySpec()
.setKid(newKid)
.setKty("FALCON")
.setAlg("FALCON-1024"));
System.out.println("[2] FALCON-1024 key created -> kid = " + newKid);
/* 3 ── re-sign on server ---------------------------------------- */
ResignResult reMeta = sdk.resignFileStream(
newKid, // replacement key
oldSig, // existing detached JWS header
dataFile, // payload to sign
newSig); // output file
System.out.println("[3] New signature written -> " + newSig.toAbsolutePath());
printResignMeta(reMeta);
/* 4 ── verify new signature ------------------------------------- */
VerifySignatureResult verMeta = sdk.verifySignatureStream(dataFile, newSig);
System.out.println(MessageFormat.format(
"[4] Signature valid? -> {0}", verMeta.isValid()));
printVerifyMeta(verMeta);
System.out.println(verMeta.isValid()
? "[5] SUCCESS – FALCON-1024 JWS verified."
: "[5] FAILURE – verification failed.");
}
/**
* Private constructor prevents instantiation.
*/
private ExampleScenario12() {
/* utility class – no instantiation */
}
}
How to run
Console milestones
-
RSA-2048 key generation
-
Detached-JWS stream-sign →
sc12_rsa.sig
-
FALCON-1024 key generation
-
Server-side stream re-sign →
sc12_falcon.sig
-
Stream verification → SUCCESS confirmation