Skip to content
Registry StackDocsDevelopment (unreleased)

Enable SD-JWT VC in a deployment

For the assertion provider and consumer or verifier

View as Markdown

Start with a complete governed deployment project. Evidence Gateway can serialize the same stateless assertion as signed JWS or SD-JWT VC. The question, source access, derivation, supported values, subject binding, purpose, audience, signing key, and audit boundaries remain the same.

For an executable introduction, complete Explore SD-JWT VC locally first. Local question authoring uses responseFormats to compile both local permission gates. A deployment instead keeps the bundle ceiling and authority grant under deployment governance.

Keep signed JWS enabled and add sd-jwt-vc to the bundle-wide response formats:

responseFormats: [signed-jws, sd-jwt-vc]

Add the same format to every exact authority grant that may return it:

authorityProfiles:
approved-caller:
kind: explicit-request
requesterTags: [approved-caller]
grants:
- requirement: urn:example:requirement:age-bracket:v1
purpose: service-path-selection
audienceFrom: authenticated-requester
responseFormats: [signed-jws, sd-jwt-vc]
subjects:
- role: person
selectorProfile: person-reference-v1
valueOrigin: request

Both gates must permit the format. Enabling only one returns the ordinary authorization refusal before source access. An unknown, combined, parameterized, or malformed Accept value returns HTTP 406.

Run the candidate checks and deploy the exact reviewed revision:

Terminal window
evidencectl fixtures run --project "<evidence-project>"
evidence check --runtime <evidence-project>/runtime.yaml

An ordinary scalar or unprojected governed value becomes one root disclosure named by its concept URI. No extra requirement configuration is needed.

To make the direct fields of a reviewed structure independently encoded disclosures, add an sdJwtVc projection to that concept:

concepts:
- id: urn:example:concept:birth-certificate:v1
form: reviewed-structured-value
required: true
constraints:
schema: urn:example:schema:birth-certificate:v1
maximumSerializedBytes: 2048
sdJwtVc:
claim: birthCertificate
disclosure: top-level

birthCertificate is adopter configuration, not a built-in Evidence Gateway claim. A direct nested object remains one atomic disclosure. Review the complete and combined disclosure surfaces before widening a production requirement.

The requester must retain its expected format before sending the request. For local integration testing, evidencectl request prepare records that expectation:

Terminal window
evidencectl request prepare "<question>" \
--purpose "<purpose>" \
--subject "<role>:<field>=<value>" \
--format sd-jwt-vc \
--name "<request-name>"

Only response negotiation changes at the HTTP boundary:

Terminal window
curl --silent --show-error --fail-with-body \
--config authorization.curl \
--request POST \
--url https://<evidence-host>/v1/evidence \
--header 'Content-Type: application/json' \
--header 'Accept: application/dc+sd-jwt' \
--data-binary @request.json \
--output assertion.sd-jwt

Treat assertion.sd-jwt as untrusted opaque input. Do not decode its disclosures into application state before verification.

Use an independently retained verification policy and trusted key set:

Terminal window
evidence verify \
--sd-jwt-vc assertion.sd-jwt \
--jwks trusted-issuer-keys.json \
--policy verification-policy.json

The verifier opens no network connection. trusted-issuer-keys.json is the complete approved trust set for the run, and verification-policy.json comes from the consumer’s request and governance records, not from the credential.

evidence verify reads a stored credential, which ends in a trailing tilde and offers no proof of possession. A holder-bound presentation, which carries the holder’s key-binding JWT after its last tilde, is verified by a separate command against its own closed policy document:

Terminal window
evidence verify-presentation \
--sd-jwt-vc-presentation presentation.sd-jwt \
--jwks trusted-issuer-keys.json \
--policy holder-bound-policy.yaml

Neither command accepts the other’s document. Both open no network connection, and both accept --at <rfc3339-utc> to verify at a historical decision instant. evidence verify-presentation exits 0 when the presentation is authentic, possession is proven, and the assertion is currently valid; 3 when it is authentic and possession is proven but the assertion is not currently valid; and 1 for every other outcome. Comparing the expected challenge is not consuming it, so the same presentation verifies again under the same policy.

Provider metadata is published at /.well-known/jwt-vc-issuer, and signing keys remain at /.well-known/evidence/jwks.json. Both endpoints are discovery. Outside local mode, the service provider identifier must be a stable HTTPS origin. It becomes the signed iss, and issuer metadata publishes its exact issuer and jwks_uri.

Approve trust independently through Manage Evidence Gateway verifier trust.

This response is not a credential lifecycle, in either subject binding mode:

  • no OID4VCI offer, issuance session, token exchange, or wallet onboarding;
  • no status list, revocation, refresh, or reissuance;
  • no presentation exchange protocol, and no key-binding JWT appended at issuance;
  • no replay prevention for a presented credential.

By default the subject binding remains scoped to the assertion’s audience and purpose, and the credential is usable only by the relying party it was issued to. holderKeys carries public P-256 JWKs, each echoed into the cnf claim of the credential issued for it. Their inclusion alone proves no possession.

A requirement the bundle declares as holder-bound scopes its subject bindings to the presented holder key instead, so the credential can be presented to a relying party that was not named at issuance. That mode is a deployment declaration, never a caller option, and it narrows the requirement to the SD-JWT VC serialization and its batch envelope.

The profile emits application/dc+sd-jwt, typ: dc+sd-jwt, vct, SHA-256 disclosures, optional cnf.jwk, and a trailing tilde without a key-binding JWT. It is pinned to RFC 9901 and SD-JWT VC draft v18. evidence verify checks the complete stored credential. A selectively disclosed presentation carrying a holder-signed key-binding JWT is checked by evidence verify-presentation instead. Neither command implements a wallet protocol, and full SD-JWT VC conformance stays unclaimed.

For deterministic source-tree proof of the format and tamper refusals, see the maintained SD-JWT VC demo.