Skip to content

JWS Serialization & JAdES Compliance Tiers

This page explains the two standards-honest representations an ANKASecure signature can take on the attached sign endpoints, how a client chooses between them with the optional serialization parameter, and how the choice interacts with the backend-authoritative RFC 3161 timestamping policy. It is written for direct REST API clients; SDK and CLI users get the same behavior through ergonomic overloads/flags (see SDK & CLI Overview*).


1. Two Equally-Standard Forms

RFC 7515 (JSON Web Signature) defines two first-class, equally-standard serializations of the same signature:

  • JWS Compact Serialization — the familiar three dot-separated segments base64url(protected).base64url(payload).base64url(signature). Compact, URL-safe, and the default when no timestamp is mandated. It has no unprotected header, so it cannot carry any post-signing metadata.
  • JWS JSON Serialization — a JSON object that can carry an unprotected header (header) alongside the protected header and signature, and can hold multiple signatures (used by composite / hybrid keys). Because it has an unprotected header, it can carry post-signing metadata such as a signature timestamp.

Neither form is "more correct" than the other — the choice is a representation decision, not a security one.


2. Why a Timestamp Forces JWS JSON

ETSI TS 119 182-1 (JAdES) places the signature-timestamp sigTst inside the JWS Unprotected Header, at header.etsiU[].sigTst. Only JWS JSON Serialization has an unprotected header. Therefore:

A timestamped (qualified) signature can only be JWS JSON Serialization — never Compact.

This is a standards requirement, not a product opinion. It yields two honest compliance tiers:

Compliance tier Serialization Timestamp Standard
RFC 7515 JWS Compact or JSON none RFC 7515
JAdES B-T JSON only sigTst embedded ETSI TS 119 182-1

3. Two Orthogonal Axes

Output is resolved from two independent inputs:

  • serialization — the client's representation choice, an OPTIONAL request field serialization ∈ {COMPACT, JSON} on the attached sign endpoints. Omit it to let the server pick the standards-correct form.
  • stamping — the backend-authoritative RFC 3161 TSA usage policy (deployment ceiling/floor + optional per-tenant override), described in TSA RFC 3161 Time-Stamping Architecture*. A client cannot turn stamping on or off; it is governed per tenant/deployment.

The hard rule at the seam: stamping ⇒ output MUST be JWS JSON.


4. Behavior Truth Table (attached sign)

Applies to POST /api/v3/crypto/sign, /api/v3/crypto/resign, and /api/v3/crypto/sign-encrypt (for sign-encrypt the table governs the inner JWS that is then encrypted):

serialization effective stamping server action output 400?
omitted OFF server chooses compact JWS no
omitted REQUIRED server chooses (auto-upgrade) JWS-JSON + sigTst no
COMPACT OFF honor compact JWS no
COMPACT REQUIRED impossible 400 problem+json
JSON OFF honor JWS-JSON, no sigTst no
JSON REQUIRED honor JWS-JSON + sigTst no

Two invariants hold on every row:

  • An explicit COMPACT is never silently converted to JSON.
  • A mandated stamp is never silently dropped.

The COMPACT + REQUIRED row is the only combination this feature rejects.


5. The Single Rejection (COMPACT + REQUIRED)

When a client explicitly asks for COMPACT but the effective stamping policy is REQUIRED, the two cannot be reconciled (a compact JWS cannot carry sigTst), so the request is rejected — loudly, before anything is signed:

{
  "type": "https://docs.ankatech.co/errors/serialization-incompatible-with-timestamp",
  "title": "Serialization Incompatible With Timestamp",
  "status": 400,
  "detail": "A required RFC 3161 signature timestamp mandates JWS JSON Serialization; COMPACT serialization cannot carry the timestamp. Omit the serialization field or request JSON.",
  "instance": "/api/v3/crypto/sign",
  "extensions": { "errorCode": "SERIALIZATION_INCOMPATIBLE_WITH_TIMESTAMP" }
}
  • Content-Type: application/problem+json (RFC 7807).
  • The detail is oracle-safe: it names only the RFC 3161 → JWS-JSON requirement; it never echoes the key id, tenant, or any internal path.
  • No signature is produced — the compact token is not upgraded and the stamp is not dropped.
  • instance is the endpoint that was called (/api/v3/crypto/sign, /resign, or /sign-encrypt); for sign-encrypt no partial JWE is produced on rejection.

Precedence. If the timestamping policy is genuinely unavailable (TSA unreachable, or an unresolved REQUIRED policy), the endpoint returns 503 first — a real outage pre-empts this 400, even for an explicit COMPACT request.


6. What the Client Parses (wire contract)

On the HTTP wire the sign responses always wrap the produced JWS in a typed JWS JSON Serialization object (jwsToken) inside the response metadata envelope (keyRequested, materialVersion, algorithmUsed, warnings). The serialization choice changes only the JWS representation within jwsToken:

  • Compact tier — a single-signature JWS-JSON object with no unprotected header and no sigTst. It is losslessly reducible to the RFC 7515 three-segment compact string; the SDK performs that reduction so a sign call persists a compact token.
  • JSON / JAdES-B-T tier — a JWS-JSON object; when stamped it carries the JAdES timestamp at header.etsiU[].sigTst.tstTokens[].val (SIMPLE keys) or on each entry of signatures[].header (composite keys).

A client that needs a compact string can reduce a single-signature, no-unprotected-header JWS-JSON to compact; a stamped JWS-JSON cannot be reduced without discarding the timestamp (which is exactly why stamping forces JSON).


7. Streaming Is Unchanged

The streaming sign endpoints (/api/v3/crypto/stream/sign, /stream/resign) do not accept a serialization parameter. They already always emit detached JWS JSON Serialization ({"protected","signature"} for SIMPLE keys, {"signatures":[…],"header":{…}} for composite, payload omitted), stamped or not — they are TSA-native. No compact-detached form exists. See Streaming Operations*.


8. Verify Is Serialization-Agnostic and JAdES-Aware

The verify endpoints — /api/v3/crypto/verify, /api/v3/crypto/stream/verify, and the decrypt-then-verify inside /api/v3/crypto/decrypt-verifyauto-detect the serialization (compact vs JWS-JSON), so a client never has to declare which form it holds. Each verify response surfaces an informational qualifiedTimestamp block:

{
  "present": true,
  "trustStatus": "VALID",
  "genTime": "2026-07-05T10:20:00Z",
  "serialNumber": "84215045",
  "policyOid": "1.3.6.1.4.1.99999.1.1"
}
  • trustStatusVALID, UNTRUSTED, REVOKED, EXPIRED, TIME_ANOMALY.
  • The block is omitted when no sigTst is present.
  • It is purely informational and never flips the boolean valid: a cryptographically valid signature whose timestamp is absent or untrusted still verifies valid=true.

The timestamp semantics live in TSA Architecture §5.4 (Verify — Extract, Validate, Report)*.



Target Audience: Integration Developers, API Consumers, Compliance Officers

* restricted content — see higher-tier documentation