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 11 – ML-DSA-87 Bulk Helpers (Compact JWS)</h2>
*
* <p>This scenario demonstrates the <strong>non-streaming</strong> helper
* methods for RFC 7515 <em>Compact 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 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 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
Console milestones
-
ML-DSA-87 key generation
-
Compact-JWS signing →
scenario11.jws
-
Key-metadata export →
scenario11_keydata.json
-
Signature verification → SUCCESS confirmation