SD-JWT VC Format
SD-JWT VC is the credential format Proof uses, defined in RFC 9901 and SD-JWT VC draft 16.
SD-JWT VC is a Verifiable Credential format that encodes a credential as a Selective Disclosure JWT, defined in RFC 9901 and profiled by draft-ietf-oauth-sd-jwt-vc-16. Proof issues every Verifiable Credential in this format.
A presentation of an SD-JWT VC arrives as a single string with three kinds of components, delimited by tildes (~):
<Issuer-Signed JWT>~<Disclosure 1>~<Disclosure 2>~...~<Key Binding JWT>
| Component | Description |
|---|---|
| Issuer-Signed JWT | The base SD-JWT signed by Proof. Carries hashed claim digests in the _sd array, the Holder's public key in the cnf claim, and the credential type in vct. |
| Disclosures | Base64url-encoded arrays of [salt, claim_name, claim_value]. Only the claims requested by the OID4VP scope value are included. |
| Key Binding JWT | Signed by the Holder's private key in Proof's Cloud Wallet. Carries nonce, aud, and iat. Proves Holder possession, prevents replay, and optionally binds the presentation to specific transaction_data. |
Sample payload
The following shows a complete, decoded vp_token for a https://credentials.notarize.com/ProofCredentialV1 presentation where the Verifier requested given_name, family_name, and age_equal_or_over.18. Each component is shown separately.
Issuer-Signed JWT, header
{
"alg": "ES256",
"typ": "dc+sd-jwt",
"kid": "d96f1ca3-864f-49c8-92c7-17d341ae1234"
}
Thekidreferences a key in Proof's JWKS.
Issuer-Signed JWT, payload
{
"iss": "https://api.proof.com",
"sub": "user_8f3a92b1-47d6-4e2c-b5a0-1c9d3e7f6a84",
"vct": "https://credentials.notarize.com/ProofCredentialV1",
"iat": 1739280000,
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ",
"y": "Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8"
}
},
"_sd": [
"--NrmJe6y2u_S2dC8RKHHv3rElWsqpY8r6xslJuwaJI",
"JzYjH4svliH0R3PyEMfeZu6Jt69u5qehZo7F7EPYlSE",
"UgEtkU6-DwPQXQM-UrsaWMd7MOWMB_3iZjDeyp6gvUY",
"S4sER_VP45OHkmC7-BVsj2NqlA_0jhsJE7P0rTkkUFQ",
"bViw5OlxKJBrlXkjSBkz2MERPl1gUHnKxHmkR6Zf6Mk",
"h4JMnGjFOh9HfOB-r09jqeMxEpGELe3BDLn_fuY34vs",
"qF42T_IHyzGvTIGLH1gPMFGVLW92-Ol9HwFBaFDuGB4",
"UkVeSoup2-mIUXvkbsm2aQr6BdwyW34Tydo6PtLINQ0"
]
}The cnf.jwk is the public key of the user's end-entity certificate, issued by Proof Individual Authenticity Issuing CA R1. See Proof Certificate Authority for the full chain.
What to validate:
iss: must exactly matchhttps://api.proof.com(or the sandbox equivalent). Reject if different.vct: must match thevct_valuesyou specified in your DCQL query.iat: issuance timestamp. Use for audit logging. Optionally reject credentials older than your policy allows.cnf.jwk: the Holder's public key. Use this to verify the Key Binding JWT signature, and nothing else._sd: array of base64url-encoded SHA-256 digests. Each disclosure you receive must hash to one of these values.
Disclosures (3 of 8 disclosed)
Only the three claims requested via the scope are disclosed. The remaining five claims in the credential are not included in the presentation.
// given_name (base64url decoded)
["2GLC42sKQveCfGfryNRN9w", "given_name", "Darren"]
// family_name (base64url decoded)
["eluV5Og3gSNII8EYnsxA_A", "family_name", "Louie"]
// age_equal_or_over.18 (base64url decoded)
["6Ij7tM-a5iVPGboS5tmvVA", "age_equal_or_over.18", true]Each disclosure is an array of three elements:
[0]: salt. A random value that prevents rainbow-table attacks on claim digests.[1]: claim name. The disclosed attribute. The key you use when extracting claims.[2]: claim value. Type depends on the claim: strings for names, booleans for age threshold claims, objects for structured data.
For each disclosure, computeSHA-256(base64url_encoded_disclosure_string)and verify the resulting digest appears in the_sdarray. Hash the raw base64url string as received, not the decoded JSON.
Key Binding JWT
Header:
{
"alg": "ES256",
"typ": "kb+jwt"
}Payload:
{
"nonce": "n-0S6_WzA2Mj_6a8bRs2TU",
"aud": "camdrbpxd",
"iat": 1739280120,
"sd_hash": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo"
}What to validate:
nonce: must exactly match thenonceyou sent in the authorization request.aud: must match yourclient_id.iat: presentation creation time. Reject if too far in the past (recommended: no more than 5 minutes).sd_hash: SHA-256 hash over the Issuer-Signed JWT concatenated with all disclosures (with~delimiters). Binds the KB-JWT to this specific presentation.- Signature: verify with
cnf.jwkfrom the Issuer-Signed JWT.
If the request bound transaction_data, the Key Binding JWT carries those values as top-level claims. The full catalog is in Transaction Data Templates.
See also: Proof Certificate Authority · Verify a Credential · Verifiable Credential Types
Updated about 2 hours ago