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

/*
 * 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.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.Path;
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&nbsp;JWS / compact&nbsp;JWE</li>
 *   <li><em>Streaming</em>      → detached&nbsp;JWS / detached&nbsp;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 relies on the {@link java.nio.file.Path} API.</li>
 *   <li>UTF-8 is enforced explicitly for deterministic encoding.</li>
 *   <li>Directory creation delegates to {@link ExampleUtil#ensureTempDir(java.nio.file.Path)}.</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. */
    public static void main(final String[] args) {

        System.out.println("===== SCENARIO 2 START =====");
        System.out.println("""
                Purpose :
                  * RSA-2048 streaming signature life-cycle
                  * Demonstrates detached-JWS pipeline (sign → verify)
                Steps   :
                  1) Generate RSA-2048 key
                  2) Create sample payload
                  3) Sign payload (detached JWS)
                  4) Verify signature
                  5) Report result
                --------------------------------------------------------------""");

        try {
            ensureTempDir(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 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();
        sdk.generateKey(new GenerateKeySpec()
                .setKid(kid)
                .setKty("RSA")
                .setAlg("RSA-2048"));
        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.");
    }

    /** Utility class – no instantiation. */
    private ExampleScenario2() {
        /* 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?