Sign W3C Credential
Sign credentials either as JWT or SD-JWT using the walt.id verifiable credentials lib in Kotlin/Java.
Before we can start singing credentials we need to get a few things in order.
- We need to decide on the structure of the raw credential data to be signed, which typically follows a predefined template or schema. You can find a list of common data structures here.
- We need to decide whether we want to sign the credential as JWT (JSON-Web-Token) or SD-JWT (Selective-Disclosure JWT). JWTs are commonly used whereas SD-JWTs are more experimental in their current implementation. Learn more about SD-JWT here.
- We (the issuer) need to have a key and the related DID to sign the credential, see managing keys and managing DIDs.
Create Credential
To build the credential we will be using the CredentialBuilder
which makes the creation of W3C credentials easier, by
automatically inserting required fix fields and providing utility functions to add custom data. Next to using
the CredentialBuilder
, you can also build W3C manually as described in the next section. If you want to update
credential
data defined via the CredentialBuilder
or manually, you can use data functions described here. You won't need this
though for most
use-cases.
The CredentialBuilder
supports the creation of W3C credentials based on version 1.1 or 2.0.
val v1 = CredentialBuilder(CredentialBuilderType.W3CV11CredentialBuilder)
// OR
val v2 = CredentialBuilder(CredentialBuilderType.W3CV2CredentialBuilder)
For this example we will be using V1 to create a Open Badge credential.
import id.walt.credentials.CredentialBuilder
import id.walt.credentials.CredentialBuilderType
import id.walt.crypto.utils.JsonUtils.toJsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlin.time.Duration.Companion.days
suspend fun main() {
val vc = CredentialBuilder(CredentialBuilderType.W3CV11CredentialBuilder).apply {
// Adds context next to default "https://www.w3.org/2018/credentials/v1"
addContext("https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json")
// Adds type next to default "VerifiableCredential"
addType("OpenBadgeCredential")
credentialId = "urn:uuid:4177e048-9a4a-474e-9dc6-aed4e61a6439"
issuerDid = "did:key:z6MksFnax6xCBWdo6hhfZ7pEsbyaq9MMNdmABS1NrcCDZJr3"
// Sets issuance date to current time - 1.5 min
validFromNow()
// Adds expiration date
validFor(2.days)
subjectDid = "did:key:z6MksFnax6xCBWdo6hhfZ7pEsbyaq9MMNdmABS1NrcCDZJr3"
// Used to add any custom data
useData("name", JsonPrimitive("JFF x vc-edu PlugFest 3 Interoperability"))
// Used to insert credential subject data
useCredentialSubject(
mapOf(
"type" to listOf("AchievementSubject"),
"achievement" to mapOf(
"id" to "urn:uuid:ac254bd5-8fad-4bb1-9d29-efd938536926",
"type" to listOf("Achievement"),
"name" to "JFF x vc-edu PlugFest 3 Interoperability",
"description" to "This wallet supports the use of W3C Verifiable Credentials and has demonstrated interoperability during the presentation request workflow during JFF x VC-EDU PlugFest 3.",
"criteria" to mapOf(
"type" to "Criteria",
"narrative" to "Wallet solutions providers earned this badge by demonstrating interoperability during the presentation request workflow. This includes successfully receiving a presentation request, allowing the holder to select at least two types of verifiable credentials to create a verifiable presentation, returning the presentation to the requestor, and passing verification of the presentation and the included credentials."
),
"image" to mapOf(
"id" to "https://w3c-ccg.github.io/vc-ed/plugfest-3-2023/images/JFF-VC-EDU-PLUGFEST3-badge-image.png",
"type" to "Image"
)
)
).toJsonObject()
)
}.buildW3C() // Builds the credential and returns W3CVC object
println(vc.toPrettyJson())
}
You can also add a Credential Status based on CredentialStatusList2021 standard using the useStatusList2021Revocation
.
The function tasks the status credential as a first and the index of the to be issued credential as a second. At the
moment,
we haven't fully integrated the credential revocation functionality yet. Please refer to
our roadmap for a timeline.
Credential as JSON
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json"
],
"type": [
"VerifiableCredential",
"OpenBadgeCredential"
],
"credentialSubject": {
"type": [
"AchievementSubject"
],
"achievement": {
"id": "urn:uuid:ac254bd5-8fad-4bb1-9d29-efd938536926",
"type": [
"Achievement"
],
"name": "JFF x vc-edu PlugFest 3 Interoperability",
"description": "This wallet supports the use of W3C Verifiable Credentials and has demonstrated interoperability during the presentation request workflow during JFF x VC-EDU PlugFest 3.",
"criteria": {
"type": "Criteria",
"narrative": "Wallet solutions providers earned this badge by demonstrating interoperability during the presentation request workflow. This includes successfully receiving a presentation request, allowing the holder to select at least two types of verifiable credentials to create a verifiable presentation, returning the presentation to the requestor, and passing verification of the presentation and the included credentials."
},
"image": {
"id": "https://w3c-ccg.github.io/vc-ed/plugfest-3-2023/images/JFF-VC-EDU-PLUGFEST3-badge-image.png",
"type": "Image"
}
},
"id": "did:key:z6MksFnax6xCBWdo6hhfZ7pEsbyaq9MMNdmABS1NrcCDZJr3"
},
"id": "urn:uuid:4177e048-9a4a-474e-9dc6-aed4e61a6439",
"issuer": "did:key:z6MksFnax6xCBWdo6hhfZ7pEsbyaq9MMNdmABS1NrcCDZJr3",
"issuanceDate": "2024-01-16T14:42:50.169834Z",
"expirationDate": "2024-01-18T14:42:50.172054Z",
"name": "JFF x vc-edu PlugFest 3 Interoperability"
}
Manual Create Credential
In the example below we are not using the CredentialBuilder
, but build the credential manually.
val vc = W3CVC.build(
context = listOf(
"https://www.w3.org/2018/credentials/v1",
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json"
),
type = listOf("VerifiableCredential", "OpenBadgeCredential"),
"id" to "urn:uuid:4177e048-9a4a-474e-9dc6-aed4e61a6439",
"name" to "JFF x vc-edu PlugFest 3 Interoperability",
"issuer" to myIssuerDid,
"issuanceDate" to "2023-08-02T08:03:13Z",
"credentialSubject" to mapOf(
"type" to listOf("AchievementSubject"),
"id" to mySubjectDid,
"achievement" to mapOf(
"id" to "urn:uuid:ac254bd5-8fad-4bb1-9d29-efd938536926",
"type" to listOf("Achievement"),
"name" to "JFF x vc-edu PlugFest 3 Interoperability",
"description" to "This wallet supports the use of W3C Verifiable Credentials and has demonstrated interoperability during the presentation request workflow during JFF x VC-EDU PlugFest 3.",
"criteria" to mapOf(
"type" to "Criteria",
"narrative" to "Wallet solutions providers earned this badge by demonstrating interoperability during the presentation request workflow. This includes successfully receiving a presentation request, allowing the holder to select at least two types of verifiable credentials to create a verifiable presentation, returning the presentation to the requestor, and passing verification of the presentation and the included credentials."
),
"image" to mapOf(
"id" to "https://w3c-ccg.github.io/vc-ed/plugfest-3-2023/images/JFF-VC-EDU-PLUGFEST3-badge-image.png",
"type" to "Image"
)
)
)
)
Sign Credential
To sign the credential we need to bring in a key and did for the issuer. Please refer to the manage Key and manage DID sections for more info.
suspend fun main() {
DidService.minimalInit()
val myIssuerKey = LocalKey.generate(KeyType.Ed25519)
val myIssuerDid = DidService.registerByKey("key", myIssuerKey).did
val mySubjectDid = "did:key:xyz"
// vc refers to the credential created in the previous section
val signed: String = vc.signJws(myIssuerKey, myIssuerDid, mySubjectDid)
}