Skip to content

Flow 11 --- ML-DSA-87 Compact-JWS Sign / Verify (Non-Streaming)

This scenario walks through a post-quantum, non-streaming signature round-trip with an ML-DSA-87 key:

  • Generate an ML-DSA-87 key-pair (kty="ML-DSA").

  • Sign a document → compact JWS written as UTF-8 text.

  • Export the key's public metadata to a JSON file.

  • Verify the compact JWS.

  • Validate that the fresh signature is accepted by the server.

Key points

  • Uses signFile( ) / verifySignature( ) helpers (entire token in memory).

  • Demonstrates the Compact JWS format, easy to paste into logs or manifests.

  • Prints rich server metadata: key actually used, algorithm, policy warnings.

When to use it

  • Portable attestations --- compact-JWS strings are perfect for embedding in SBOMs, PDF manifests or CI artefacts without binary handling.

  • Post-quantum assurance --- ML-DSA-87 delivers NIST-selected signature security that survives future RSA/ECC compromises.

  • Quick auditing --- public-key JSON export lets auditors validate signatures offline or store public keys in catalogues.

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/ExampleScenario11.java

/* *****************************************************************************
 * FILE: ExampleScenario11.java
 * Copyright © 2025 Anka Technologies.
 * SPDX-License-Identifier: MIT
 * ---------------------------------------------------------------------------
 * Scenario 11 – ML-DSA-87 Bulk Sign / Verify (Compact JWS helpers)
 * ---------------------------------------------------------------------------
 *   1. Generate an ML-DSA-87 key.
 *   2. Sign a local document → Compact JWS on disk.
 *   3. Export the key’s public metadata to JSON.
 *   4. Verify the JWS and print 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.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;11 – ML-DSA-87 Bulk Helpers (Compact&nbsp;JWS)</h2>
 *
 * <p>This scenario demonstrates the <strong>non-streaming</strong> helper
 * methods for RFC&nbsp;7515 <em>Compact&nbsp;JWS</em> using an ML-DSA-87 key:</p>
 * <ol>
 *   <li>Generate an ML-DSA-87 key.</li>
 *   <li>Sign a plaintext document.</li>
 *   <li>Export the key’s public metadata.</li>
 *   <li>Verify the resulting JWS.</li>
 * </ol>
 *
 * <p><b>Implementation notes (Java&nbsp;21+):</b></p>
 * <ul>
 *   <li>All filesystem operations rely on the {@link java.nio.file.Path} API.</li>
 *   <li>UTF-8 encoding is explicitly specified for deterministic behaviour.</li>
 *   <li>Temporary directories are 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 ExampleScenario11 {

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

        System.out.println("===== SCENARIO 11 START =====");
        System.out.println("""
                Purpose :
                  • Demonstrate Compact JWS helpers with an ML-DSA-87 key
                  • Steps: generate → sign → export → verify
                --------------------------------------------------------------""");

        try {
            ensureTempDir(TEMP_DIR);

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

            runScenario(sdk);

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

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

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

        Path docFile  = TEMP_DIR.resolve("scenario11_doc.txt");
        Path jwsFile  = TEMP_DIR.resolve("scenario11.jws");
        Path metaJson = TEMP_DIR.resolve("scenario11_keydata.json");

        /* 1 ── create document ----------------------------------------- */
        FileIO.writeUtf8(
                docFile,
                "Scenario-11 – ML-DSA-87 non-streaming sign / verify demo.");
        System.out.println("[1] Document ready        -> " + docFile.toAbsolutePath());

        /* 2 ── generate ML-DSA-87 key ---------------------------------- */
        String kid = "sc11_dsa87_" + System.currentTimeMillis();
        sdk.generateKey(new GenerateKeySpec()
                .setKid(kid)
                .setKty("ML-DSA")
                .setAlg("ML-DSA-87"));
        System.out.println("[2] Key generated         -> kid = " + kid);

        /* 3 ── sign document (Compact JWS) ------------------------------ */
        SignResult signMeta = sdk.signFile(kid, docFile, jwsFile);
        System.out.println("[3] JWS written           -> " + jwsFile.toAbsolutePath());
        printSignMeta(signMeta);

        /* 4 ── export key metadata ------------------------------------- */
        FileIO.writeUtf8(
                metaJson,
                toJson(sdk.exportKey(kid)));
        System.out.println("[4] Metadata exported     -> " + metaJson.toAbsolutePath());

        /* 5 ── verify signature ---------------------------------------- */
        VerifySignatureResult verMeta = sdk.verifySignature(jwsFile);
        System.out.println(MessageFormat.format(
                "[5] Signature valid?      -> {0}", verMeta.isValid()));
        printVerifyMeta(verMeta);

        System.out.println(verMeta.isValid()
                ? "[6] SUCCESS – JWS verified."
                : "[6] FAILURE – verification failed.");
    }

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

How to run

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

Console milestones

  • ML-DSA-87 key generation

  • Compact-JWS signing → scenario11.jws

  • Key-metadata export → scenario11_keydata.json

  • Signature verification → SUCCESS confirmation


Where next?