Skip to content

Flow 2 – RSA-2048 Stream Sign / Verify (Detached JWS)

This flow walks through a streamed digital-signature round-trip with the AnkaSecure API:

  1. Generate a classical RSA-2048 key-pair.
  2. Stream-sign a document – server returns a detached JWS (General JSON).
  3. Stream-verify the signature against the same payload.

Key points

  • Uses the streaming helpers signFileStream / verifySignatureStream, so you can sign or verify multi-gigabyte artefacts without ever loading them fully into RAM.\
  • Produces a detached JWS (General JSON) — the payload stays as raw binary while the header and signature live in a tiny JSON part (no Base64 bloat, easy to cache or checksum independently).\
  • Surfaces rich lifecycle telemetry in HTTP headers (key actually used, rotations, soft-limit warnings) ready for dashboards, logs or automated compliance hooks.

When to use it

  • Release pipelines that still rely on classical RSA trust anchors — e.g. package repos, container registries or firmware images that must remain backwards-compatible with existing RSA-2048 verification tooling.
  • Very large files — think multi-GB log bundles or VM snapshots where holding the whole payload in memory is infeasible but you still want a single authoritative signature.
  • Bandwidth-sensitive or append-only storage — detached JWS keeps the payload untouched, so you can stream directly to S3/GCS/CDNs and append signatures later without rewriting the object.

Dependency — the example uses the common helper class co.ankatech.ankasecure.sdk.examples.ExampleUtil.
If you haven’t copied that file yet, grab it from example_util.md and place it next to the scenario sources.


Complete Java implementation

src/main/java/co/ankatech/ankasecure/sdk/examples/ExampleScenario2.java

/* *****************************************************************************
 * FILE: ExampleScenario2.java
 * ****************************************************************************/
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.SignResult;
import co.ankatech.ankasecure.sdk.model.VerifySignatureResult;
import co.ankatech.ankasecure.sdk.util.FileIO;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.text.MessageFormat;
import java.util.Properties;

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

/**
 * <h2>Scenario&nbsp;2 – RSA-2048 <em>Streaming</em> Sign / Verify</h2>
 *
 * <p>This walkthrough illustrates a detached-JWS workflow:</p>
 * <ol>
 *   <li><strong>Key-pair generation</strong> – classical RSA-2048.</li>
 *   <li><strong>Streaming sign</strong> – the SDK uploads the file in chunks
 *       and receives a <em>detached JWS&nbsp;(General JSON)</em> whose
 *       {@code payload} is {@code null}.</li>
 *   <li><strong>Streaming verify</strong> – the same detached signature is
 *       verified against the binary payload with constant memory.</li>
 * </ol>
 *
 * <p><b>Rule of thumb</b>:</p>
 * <ul>
 *   <li><em>Non-streaming</em> → compact JWS / compact JWE</li>
 *   <li><em>Streaming</em>      → detached JWS / detached JWE</li>
 * </ul>
 *
 * <p>All artefacts are written under <kbd>temp_files/</kbd>.</p>
 *
 * <p><b>Implementation notes (Java&nbsp;21+):</b></p>
 * <ul>
 *   <li>File handling leverages the {@link java.nio.file.Path} API.</li>
 *   <li>UTF-8 is explicitly specified for deterministic encoding.</li>
 *   <li>Directory creation uses {@link Files#createDirectories(Path, FileAttribute[])}.</li>
 * </ul>
 *
 * <p><b>Thread-safety:</b> the class is stateless and immutable; all variables
 * are confined to the current thread.</p>
 *
 * @author ANKATech – Security Engineering
 * @since 2.1.0
 */
public final class ExampleScenario2 {

    /* ====================================================================== */
    /**
     * Entry-point.
     *
     * @param args ignored
     */
    public static void main(final String[] args) {

        System.out.println("===== SCENARIO 2 START =====");
        System.out.println("""
                Purpose   : RSA-2048 key generation, detached-JWS streaming sign & verify
                Artefacts : temp_files/scenario2_doc.txt  (payload)
                            temp_files/scenario2.sig      (detached JWS)
                --------------------------------------------------------------""");

        try {
            Files.createDirectories(TEMP_DIR);

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

            runScenario(sdk);

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

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

    /* ====================================================================== */
    /**
     * Executes all steps of Scenario&nbsp;2.
     *
     * @param sdk an authenticated {@link AnkaSecureSdk} instance
     *
     * @throws Exception if any operation fails
     */
    private static void runScenario(final AnkaSecureSdk sdk) throws Exception {

        /* 1 ── create sample document ------------------------------------- */
        Path doc = TEMP_DIR.resolve("scenario2_doc.txt");
        FileIO.writeUtf8(
                doc,
                "Scenario-2 – RSA-2048 detached-JWS streaming sign & verify.");
        System.out.println("[1] Document ready       -> " + doc.toAbsolutePath());

        /* 2 ── generate RSA-2048 key -------------------------------------- */
        String kid = "sc2_rsa_" + System.currentTimeMillis();

        GenerateKeySpec rsa = new GenerateKeySpec()
                .setKid(kid)
                .setKty("RSA")
                .setAlg("RSA-2048");
        sdk.generateKey(rsa);
        System.out.println("[2] RSA-2048 key created -> kid = " + kid);

        /* 3 ── streaming sign (detached JWS) ------------------------------ */
        Path sig = TEMP_DIR.resolve("scenario2.sig");
        SignResult signMeta = sdk.signFileStream(kid, doc, sig);
        System.out.println("[3] File signed          -> " + sig.toAbsolutePath());
        printSignMeta(signMeta);

        /* 4 ── streaming verify ------------------------------------------ */
        VerifySignatureResult verifyMeta = sdk.verifySignatureStream(doc, sig);
        System.out.println(MessageFormat.format(
                "[4] Signature valid      : {0}", verifyMeta.isValid()));
        printVerifyMeta(verifyMeta);

        System.out.println(verifyMeta.isValid()
                ? "SUCCESS – signature verified."
                : "FAILURE – signature verification failed.");
    }

    /**
     * Private constructor prevents instantiation.
     */
    private ExampleScenario2() {
        /* utility class – no instantiation */
    }
}

How to run


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

Console milestones:

  • RSA-2048 key creation (kid = sc2_rsa_<timestamp>)

  • Detached-JWS stream-sign → scenario2.sig

  • Streaming verify → SUCCESS confirmation


Where next?