Present W3C Credential
Using the walt.id verifiable credentials library we can create a verifiable presentation including one or more W3C credentials (v1.1 and v2) in Kotlin/Java.
A verifiable presentation is needed when a holder needs to present one or more credentials to a verifier. Because, as the credential itself holds the issuer's signature, the holder must validate their ownership of the DID specified in the credential's subject by including all requested credentials into a verifiable presentation signed with his key, thereby establishing proof of ownership.
Creating a Verifiable Presentation
Below we are creating a Verifiable Presentation using the PresentationBuilder
. We set the DID of the subject, the
nonce and add either a JWT-VC or SD-JWT-VC to the presentation using the addCredential
or addCredentials
if we
have more than one.
val vp = PresentationBuilder().apply {
did = "did:key:123456789" // sets sub, iss, vp.holder
/* nbf, iat, jti set automatically to sane default values */
nonce = "ABC123DEF456GHI789JKL"
/* vp.context, vp.type, vp.id set automatically to sane default values */
addCredential(JsonPrimitive(jwtVc)
}
Signing a Verifiable Presentation
Using the PresentationBuilder's
sign function, we can turn the Verifiable Presentation into a signed JSON-Web-Token (
JWT-VP) which can be shared with the verifier.
// Creating Verifiable Presentation
val vp = PresentationBuilder().apply {
did = "did:key:123456789"
nonce = "ABC123DEF456GHI789JKL"
addCredential(JsonPrimitive(jwtVc))
}
// Sign Verifiable Presentation
val vpSigned = vp.buildAndSign(subjectKey)
Signed VP
eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDprZXk6ejZNa3B1aWhYcW40ajdkbTRxcFMxZVA5TVIyU3Nkb0NGdVdoZFRZRjh6R1V1SEZaI3o2TWtwdWloWHFuNGo3ZG00cXBTMWVQOU1SMlNzZG9DRnVXaGRUWUY4ekdVdUhGWiJ9.eyJzdWIiOiJkaWQ6a2V5Ono2TWtwdWloWHFuNGo3ZG00cXBTMWVQOU1SMlNzZG9DRnVXaGRUWUY4ekdVdUhGWiIsIm5iZiI6MTcwNTQ5MzA0NCwiaWF0IjoxNzA1NDkzMTA0LCJqdGkiOiJ1cm46dXVpZDo1YTdlMjllMC0wNzdkLTRmZGQtYTFlMy1lN2QyYmVjNTBkYTYiLCJpc3MiOiJkaWQ6a2V5Ono2TWtwdWloWHFuNGo3ZG00cXBTMWVQOU1SMlNzZG9DRnVXaGRUWUY4ekdVdUhGWiIsIm5vbmNlIjoiMjAzOTQwMjkzNDAiLCJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVQcmVzZW50YXRpb24iXSwiaWQiOiJ1cm46dXVpZDo1YTdlMjllMC0wNzdkLTRmZGQtYTFlMy1lN2QyYmVjNTBkYTYiLCJob2xkZXIiOiJkaWQ6a2V5Ono2TWtwdWloWHFuNGo3ZG00cXBTMWVQOU1SMlNzZG9DRnVXaGRUWUY4ekdVdUhGWiIsInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlpFUlRRU0lzSW10cFpDSTZJbVJwWkRwclpYazZlalpOYTJWMVdIbDFXbU5JTVVNM2JtUkZjMFJyYVRGaVdqaFZOV2h4ZWxoaVlVUnViVFk1VlhWQ1kyOXBjbkJpSW4wLmV5SnBjM01pT2lKa2FXUTZhMlY1T25vMlRXdGxkVmg1ZFZwalNERkROMjVrUlhORWEya3hZbG80VlRWb2NYcFlZbUZFYm0wMk9WVjFRbU52YVhKd1lpSXNJbk4xWWlJNkltUnBaRHByWlhrNmVqWk5hM0IxYVdoWWNXNDBhamRrYlRSeGNGTXhaVkE1VFZJeVUzTmtiME5HZFZkb1pGUlpSamg2UjFWMVNFWmFJaXdpZG1NaU9uc2lRR052Ym5SbGVIUWlPbHNpYUhSMGNITTZMeTkzZDNjdWR6TXViM0puTHpJd01UZ3ZZM0psWkdWdWRHbGhiSE12ZGpFaUxDSm9kSFJ3Y3pvdkwzQjFjbXd1YVcxeloyeHZZbUZzTG05eVp5OXpjR1ZqTDI5aUwzWXpjREF2WTI5dWRHVjRkQzB6TGpBdU1pNXFjMjl1SWwwc0luUjVjR1VpT2xzaVZtVnlhV1pwWVdKc1pVTnlaV1JsYm5ScFlXd2lMQ0pQY0dWdVFtRmtaMlZEY21Wa1pXNTBhV0ZzSWwwc0ltTnlaV1JsYm5ScFlXeFRkV0pxWldOMElqcDdJblI1Y0dVaU9sc2lRV05vYVdWMlpXMWxiblJUZFdKcVpXTjBJbDBzSW1GamFHbGxkbVZ0Wlc1MElqcDdJbWxrSWpvaWRYSnVPblYxYVdRNllXTXlOVFJpWkRVdE9HWmhaQzAwWW1JeExUbGtNamt0Wldaa09UTTROVE0yT1RJMklpd2lkSGx3WlNJNld5SkJZMmhwWlhabGJXVnVkQ0pkTENKdVlXMWxJam9pU2taR0lIZ2dkbU10WldSMUlGQnNkV2RHWlhOMElETWdTVzUwWlhKdmNHVnlZV0pwYkdsMGVTSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVZHaHBjeUIzWVd4c1pYUWdjM1Z3Y0c5eWRITWdkR2hsSUhWelpTQnZaaUJYTTBNZ1ZtVnlhV1pwWVdKc1pTQkRjbVZrWlc1MGFXRnNjeUJoYm1RZ2FHRnpJR1JsYlc5dWMzUnlZWFJsWkNCcGJuUmxjbTl3WlhKaFltbHNhWFI1SUdSMWNtbHVaeUIwYUdVZ2NISmxjMlZ1ZEdGMGFXOXVJSEpsY1hWbGMzUWdkMjl5YTJac2IzY2daSFZ5YVc1bklFcEdSaUI0SUZaRExVVkVWU0JRYkhWblJtVnpkQ0F6TGlJc0ltTnlhWFJsY21saElqcDdJblI1Y0dVaU9pSkRjbWwwWlhKcFlTSXNJbTVoY25KaGRHbDJaU0k2SWxkaGJHeGxkQ0J6YjJ4MWRHbHZibk1nY0hKdmRtbGtaWEp6SUdWaGNtNWxaQ0IwYUdseklHSmhaR2RsSUdKNUlHUmxiVzl1YzNSeVlYUnBibWNnYVc1MFpYSnZjR1Z5WVdKcGJHbDBlU0JrZFhKcGJtY2dkR2hsSUhCeVpYTmxiblJoZEdsdmJpQnlaWEYxWlhOMElIZHZjbXRtYkc5M0xpQlVhR2x6SUdsdVkyeDFaR1Z6SUhOMVkyTmxjM05tZFd4c2VTQnlaV05sYVhacGJtY2dZU0J3Y21WelpXNTBZWFJwYjI0Z2NtVnhkV1Z6ZEN3Z1lXeHNiM2RwYm1jZ2RHaGxJR2h2YkdSbGNpQjBieUJ6Wld4bFkzUWdZWFFnYkdWaGMzUWdkSGR2SUhSNWNHVnpJRzltSUhabGNtbG1hV0ZpYkdVZ1kzSmxaR1Z1ZEdsaGJITWdkRzhnWTNKbFlYUmxJR0VnZG1WeWFXWnBZV0pzWlNCd2NtVnpaVzUwWVhScGIyNHNJSEpsZEhWeWJtbHVaeUIwYUdVZ2NISmxjMlZ1ZEdGMGFXOXVJSFJ2SUhSb1pTQnlaWEYxWlhOMGIzSXNJR0Z1WkNCd1lYTnphVzVuSUhabGNtbG1hV05oZEdsdmJpQnZaaUIwYUdVZ2NISmxjMlZ1ZEdGMGFXOXVJR0Z1WkNCMGFHVWdhVzVqYkhWa1pXUWdZM0psWkdWdWRHbGhiSE11SW4wc0ltbHRZV2RsSWpwN0ltbGtJam9pYUhSMGNITTZMeTkzTTJNdFkyTm5MbWRwZEdoMVlpNXBieTkyWXkxbFpDOXdiSFZuWm1WemRDMHpMVEl3TWpNdmFXMWhaMlZ6TDBwR1JpMVdReTFGUkZVdFVFeFZSMFpGVTFRekxXSmhaR2RsTFdsdFlXZGxMbkJ1WnlJc0luUjVjR1VpT2lKSmJXRm5aU0o5ZlN3aWFXUWlPaUprYVdRNmEyVjVPbm8yVFd0elJtNWhlRFo0UTBKWFpHODJhR2htV2pkd1JYTmllV0Z4T1UxTlRtUnRRVUpUTVU1eVkwTkVXa3B5TXlKOUxDSnBaQ0k2SW5WeWJqcDFkV2xrT2pReE56ZGxNRFE0TFRsaE5HRXRORGMwWlMwNVpHTTJMV0ZsWkRSbE5qRmhOalF6T1NJc0ltbHpjM1ZsY2lJNkltUnBaRHByWlhrNmVqWk5hM05HYm1GNE5uaERRbGRrYnpab2FHWmFOM0JGYzJKNVlYRTVUVTFPWkcxQlFsTXhUbkpqUTBSYVNuSXpJaXdpYVhOemRXRnVZMlZFWVhSbElqb2lNakF5TkMwd01TMHhOMVF4TWpvd05Ub3dOQzR5TlRJMU16QmFJaXdpWlhod2FYSmhkR2x2YmtSaGRHVWlPaUl5TURJMExUQXhMVEU1VkRFeU9qQTFPakEwTGpJMU16QTNOVm9pTENKdVlXMWxJam9pU2taR0lIZ2dkbU10WldSMUlGQnNkV2RHWlhOMElETWdTVzUwWlhKdmNHVnlZV0pwYkdsMGVTSjlmUS4yLV9NaXc3RmpjdkpfUlBhYmVwS1VJeS01amZSajBoa2stTXRnUlNzMHlEaTFsZnlialE2cTh1Wks3cndPdEQyakc0ZC1mSzRSNmdMZTRheDMtU3JBQSJdfX0.rdLymfQCVHt-q-BQYvlGGiABkigkywdcIlPhVBCDJobpOmfhm-oOjmRQUi8NAFzZnRFlqGafHf59YqYIVbs2DA
Full Example
import id.walt.credentials.CredentialBuilder
import id.walt.credentials.CredentialBuilderType
import id.walt.credentials.PresentationBuilder
import id.walt.crypto.keys.KeyType
import id.walt.crypto.keys.LocalKey
import id.walt.crypto.utils.JsonUtils.toJsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlin.time.Duration.Companion.days
import id.walt.did.dids.DidService
import id.walt.sdjwt.DecoyMode
import id.walt.sdjwt.SDField
import id.walt.sdjwt.SDMap
import kotlin.js.ExperimentalJsExport
@OptIn(ExperimentalJsExport::class)
suspend fun main() {
DidService.minimalInit()
val myIssuerKey = LocalKey.generate(KeyType.Ed25519)
val myIssuerDid = DidService.registerByKey("key", myIssuerKey).did
val mySubjectKey = LocalKey.generate(KeyType.Ed25519)
val mySubjectDid = DidService.registerByKey("key", mySubjectKey).did
// Creating The Verifiable Credential
val vc = CredentialBuilder(CredentialBuilderType.W3CV11CredentialBuilder).apply {
addContext("https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.2.json")
addType("OpenBadgeCredential")
credentialId = "urn:uuid:4177e048-9a4a-474e-9dc6-aed4e61a6439"
issuerDid = "did:key:z6MksFnax6xCBWdo6hhfZ7pEsbyaq9MMNdmABS1NrcCDZJr3"
validFromNow()
validFor(2.days)
subjectDid = "did:key:z6MksFnax6xCBWdo6hhfZ7pEsbyaq9MMNdmABS1NrcCDZJr3"
useData("name", JsonPrimitive("JFF x vc-edu PlugFest 3 Interoperability"))
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()
// Signing The Verifiable Credential
val jwtVc: String = vc.signJws(myIssuerKey, myIssuerDid, mySubjectDid)
// Creating Verifiable Presentation
val vp = PresentationBuilder().apply {
did = mySubjectDid
nonce = "20394029340"
addCredential(JsonPrimitive(jwtVc))
}
// Sign Verifiable Presentation as JWT
val vpSigned = vp.buildAndSign(mySubjectKey)
println(vpSigned)
}