Issuing a SD-JWT Credential

Learn about issuing SD-JWT credentials which includes hashing, creating disclosures and adding decoy hashes

SD-JWT Credential Issuance Process

  1. Credential Creation: The issuer first creates the credential, as usual.

  2. Conversion to SD-JWT Credential: The issuer then transforms the credential into an SD-JWT. This is done by hashing all or only a subset of claims, adding decoy hashes, and preparing disclosures. At the end, the SD-JWT credential can contain plain-text claims next to disclosable ones.

  3. Claim Hashing: The claim is transformed into a disclosure, which is a plain-text representations of the claim, by concatenating the attribute name and value, then prefixing it with a salt. The salt prevents attackers from guessing plain-text values via dictionary attacks. This is then converted to a base64 string, which will represent the disclosure. For example, the disclosure may look like this (salt + attribute name + value): [ “dC12Y2xpYi9tYXN0ZXI”, “given_name”, “John” ]. The disclosure is then put into a hash function, and the result gets included in the SD-JWT.

  4. Adding Decoy Hashes: At this point, decoy hashes are also added to the SD-JWT. These decoy hashes are essentially dummy values that help conceal the actual number of claims a credential holds. By using decoy hashes, the issuer can prevent potential observers from determining how many claims are contained within the credential based on the number of hashes.

  5. SD-JWT Credential Transfer to Holder: The issuer sends the SD-JWT and all disclosures to the holder. Because the holder now has the SD-JWT credential as well as all the disclosures, he can read the entire content of the credential. This allows the holder to decide which disclosures to send alongside the SD-JWT in a transaction with a verifier.

  6. Transfer Format: On transfer, the SD-JWT is shared with the concatenated disclosures using the ~ sign. An example of this format would be:

Issuance in Action

Using either the CLI, Kotlin or REST option, you can start issuing your SD-JWT credential.

Setup section, if you never used the SSi-Kit before. I will be using ssikit as an alias for ./ssikit.sh in this section.

DID creation

Creating a did:key for our SD-JWT credential. Please refer to DIDs section for all options.

ssikit did create

Example Response

walt.id SSI Kit 1.SNAPSHOT (running on Java 18.0.1+10-24)

Creating did:id.walt.cli.did.KeyMethodOption@59c04bee (key: 98de22f79c7e445dbedc0c72a18f6f9a)

Results:


DID created: did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a


DID document (below, JSON):

{
    "assertionMethod" : [
        "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a#z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a"
    ],
    "authentication" : [
        "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a#z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a"
    ],
    "capabilityDelegation" : [
        "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a#z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a"
    ],
    "capabilityInvocation" : [
        "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a#z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a"
    ],
    "@context" : "https://www.w3.org/ns/did/v1",
    "id" : "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a",
    "keyAgreement" : [
        "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a#z6LSdemtq8mkV4nRKqvQvGkvs1JzSm4sCKKWNh6KkVox1vFn"
    ],
    "verificationMethod" : [
        {
            "controller" : "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a",
            "id" : "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a#z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a",
            "publicKeyBase58" : "FMZP5xZMrXr8cveAR28hr1s5wnybptiz5RddaM7KqnHC",
            "type" : "Ed25519VerificationKey2019"
        },
        {
            "controller" : "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a",
            "id" : "did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a#z6LSdemtq8mkV4nRKqvQvGkvs1JzSm4sCKKWNh6KkVox1vFn",
            "publicKeyBase58" : "2ybjJpxtPc4gETYePdEyYR6WbcXkVi9MViNeG3ARJYV2",
            "type" : "X25519KeyAgreementKey2019"
        }
    ]
}

Issue VC with SD-JWT format

We will be using the VerifiableId credential template for this example, but you can use whatever template you want. When issuing we specify the format of the VC as SD-JWT, the fields which we want to make selectily discloable and the number of decoy digests we want to include (optiona).

ssikit vc issue -s did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a \
-i did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a \
-y SD_JWT -t VerifiableId \
--sd credentialSubject \
--sd credentialSubject.dateOfBirth \
--num-decoys 2 \
--interactive vc.txt

Options:

  • -s, --subject-did: DID of the subject

  • -i, --issuer-did: DID of the issuer

  • -y, --proof-type: Either JWT, LD_PROOF or SD_JWT

  • -t, --template: VC template, e.g VerifiableId

  • --sd, --selective-disclosure: Path to selectively disclosable fields (if supported by chosen proof type), in a simplified JsonPath format, can be specified multiple times, e.g credentialSubject.dataOfBirth

  • --num-decoys: Number of SD-JWT decoy digests to add (fixed mode), or max num of decoy digests (random mode)

  • --interactive: Interactively prompt for VC data to fill in

  • vc.txt: Path to output the generated VC

Example Response

Receiving a JWT token with disclosures appended via '~'.

Parsing the response

Viewing body of SD-JWT in JSON format

ssikit vc parse -c vc.txt

Options:

  • -c: credential content or file path

Example Responsen

{
    "type":[
        "VerifiableCredential",
        "VerifiableAttestation",
        "VerifiableId"
    ],
    "@context":[
        "https://www.w3.org/2018/credentials/v1"
    ],
    "id":"urn:uuid:2d35aad0-cce2-4286-adf7-c4f9a2e57f96",
    "issuer":"did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a",
    "issuanceDate":"2023-06-12T14:21:34Z",
    "issued":"2023-06-12T14:21:34Z",
    "validFrom":"2023-06-12T14:21:34Z",
    "credentialSchema":{
        "id":"https://raw.githubusercontent.com/walt-id/waltid-ssikit-vclib/master/src/test/resources/schemas/VerifiableId.json",
        "type":"FullJsonSchemaValidator2021"
    },
    "credentialSubject":{
        "id":"did:key:z6MktopRgCooC5LbjRUs6b6Yh7R5mNFTEmyLmSYZQd5Lm14a",
        "currentAddress":[
            "Vienna"
        ],
        "familyName":"Baumann",
        "firstName":"Tamino",
        "gender":"Male",
        "nameAndFamilyNameAtBirth":"Jane DOE",
        "personalIdentifier":"0904008084H",
        "placeOfBirth":"Munich",
        "dateOfBirth":"2023"
    },
    "evidence":[
        {
            "documentPresence":[
                "Physical"
            ],
            "evidenceDocument":[
                "Passport"
            ],
            "subjectPresence":"Physical",
            "type":[
                "DocumentVerification"
            ],
            "verifier":"did:ebsi:2A9BZ9SUe6BatacSpvs1V5CdjHvLpQ7bEsi2Jb6LdHKnQxaN"
        }
    ]
}

Demo Video

Last updated