Dit document valt onder de volgende licentie:
Creative Commons Attribution 4.0 International Public License
Deze Blauwe Knop Connect (BK Connect) standaard beschrijft hoe burgers (op gestandaardiseerde wijze) diensten kunnen gebruiken bij overheidsorganisaties. De basis van de standaard is de Blauwe Knop Connect specificatie. Blauwe Knop Connect bevat alle protocollen die nodig zijn voor het veilig leggen en gebruiken van verbindingen tussen burgers en overheidsorganisaties. Via deze verbindingen kunnen diensten worden aangeroepen. Dienstprofielen bevatten specifieke afspraken voor specifieke diensten.
Dit is een werkversie die op elk moment kan worden gewijzigd, verwijderd of vervangen door andere documenten. Het is geen goedgekeurde consultatieversie.
Waardengedreven ontwikkeling van digitale dienstverlening vereist een manier waarop burgers eenvoudig, veilig en vertrouwd gegevens kunnen uitwisselen met overheden. In het bijzonder in gevallen waar voor het oplossen van een specifiek probleem of het uitvoeren van een specifieke taak gegevens van meerdere organisaties moeten worden gecombineerd en/of communicatie met meerdere organisaties dient plaats te vinden (ketendiensten).
Voorbeelden van dergelijke (typen) ketendiensten zijn:
Met Blauwe Knop Connect kunnen organisaties diensten op betrouwbare wijze aan burgers aanbieden, en kunnen burgers deze diensten op betrouwbare en veilige wijze gebruiken.
Het basisprincipe van Blauwe Knop Connect is dat een burger een gestandaardiseerd (informatie)verzoek verstuurt naar een (overheids)organisatie, en als antwoord ook weer een gestandaardiseerd (informatie)document terug ontvangt van de betreffende organisatie.
Het Blauwe Knop Connect protocol is ontworpen met het doel dat burgers en (overheids)organisaties elk hun wettelijke rechten, taken en plichten effectief en zelfstandig kunnen uitvoeren, waarbij de rechtspositie van elke partij maximaal geborgd is:
Hoewel het Blauwe Knop protocol ontwikkeld is om de kansen die automatisering biedt maximaal te benutten, is het basisprincipe (het uitwisselen van documenten) zo eenvoudig, dat het ook op papier uitgevoerd kan worden. Dit basisprincipe fungeert als een toetssteen waarmee bewaakt wordt dat zo min mogelijk onnodige complexiteit geïntroduceerd wordt. Daardoor blijft het Blauwe Knop protocol eenvoudig te begrijpen, wat bijdraagt aan de robuustheid en het vertrouwen dat gebruikers erin stellen.
Voor alle digitale handtekeningen in deze standard MOETEN een van de volgende algoritmes gebruiken:
secp256r1
en prime256v1
),secp384r1
),secp512r1
).Merk op dat de Secure Enclave (SE) van iPhones en de Trusted Execution Environment (TEE) van Android telefoons uitsluitend ECDSA op P-256 ondersteunen. Wanneer een private key in een SE/TEE beveiligd dient te worden is dit dan dus de enige optie.
Deze standaard maakt veelvuldig gebruik van JWS (RFC 7515) om digitaal ondertekende datastructuren te encoderen. In het vervolg van deze standaard tonen we een JWS steeds als JSON structuur. Dus gegeven het volgende JWS voorbeeld:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFt
ZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.yt1lkupgb536FyX2_IzrElyQJ7
ep3MJhZ_7w4wpz0GIzWnLkESQ6RDvRmk_HTwTbLsJdLhGLj-7KB_FEvOo8sQ
Schrijven we het volgende:
{
"alg": "ES256",
"typ": "JWT"
}
.
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Deze weergave toont niet de handtekening (het laatste deel van de JWS, na de tweede punt). Alle whitespace (spaties en newlines) zijn enkel voor leesbaarheid en dienen te worden verwijderd bij het tekenen van een JWS.
Elk digitaal ondertekend bericht in deze standaard MOET in de vorm van een JWS (RFC 7515) zijn.
Voor alle asymmetrische encryptie in deze standaard MOET ECIES gebruikt worden (dit heet ECDH-ES
in JWEs (RFC 7518)), met de volgende parameters:
NB: Een versleuteld bericht wordt soms verstuurd in de vorm van een JWE. JWS en JWE zijn allebei vormen van een JWT (JSON Web Token, RFC 7519). De term JWT kan naar zowel een JWS als een JWE refereren. In dit document gebruiken we consistent "JWS" of "JWE" om expliciet te zijn.
Voor alle symmetrische encryptie in deze standaard MOET AES gebruikt worden, met als parameters een van de volgende twee:
A128GCM
, A192GCM
of A256GCM
in JWEs), ofWanneer een bericht wordt versleuteld in de vorm van een JWE, dan geldt voor de headers van die JWE het volgende:
enc
: MOET per bovenstaande A128GCM
, A192GCM
of A256GCM
zijn.alg
:ECDH-ES
hebben per voorgaande sectie.
(In dit geval zal de header ook een epk
veld moeten bevatten, en optioneel ook apu
en apv
; zie dit voorbeeld in RFC 7518.)dir
, A128KW
, A192KW
, of A256KW
, waarbij dir
wordt aangeraden.
Zie RFC 7518 voor de betekenis van deze velden.kid
: MAG gebruikt worden om te verwijzen naar een public key waarnaar de JWE versleuteld is, zodat de ontvanger kan weten welke private key gebruikt moet worden om de JWE te ontsleutelen. MOET een string zijn indien aanwezig. Dit veld kan gebruikt worden bijvoorbeeld in protocollen die key rollover ondersteunen, zodat bijvoorbeeld met "kid": "2"
aangegeven kan worden dat de tweede sleutel gebruikt moet worden om de JWE te ontsleutelen. Wanneer de ontvanger van de JWE uit de context al weet welke sleutel gebruikt moet worden dan is gebruik van deze header niet nodig.Alle verkeer tussen de App en de (infrastructuur van de) Verifier, Dienst, of Credential Issuer MOET gebruik maken van minstens TLS 1.3.
De Blauwe Knop Connect standaard beschrijft hoe organisaties diensten kunnen aanbieden aan natuurlijke personen, zodat die personen (gebruikers, burgers) die diensten makkelijk, veilig en efficiënt kunnen gebruiken om een bepaalde taak uit te voeren.
Het aanbieden en gebruiken van diensten moet gebruikersvriendelijk en in de praktijk eenvoudig zijn voor aanbieder en gebruiker. Daarom is Blauwe Knop Connect ontworpen vanuit de principes van Gebruiker Centraal.
Het aanbieden en gebruiken van diensten moet veilig zijn voor aanbieder en gebruiker, en de informatieveiligheid moet op elk moment maximaal geborgd zijn. Daarom is Blauwe Knop Connect ontworpen vanuit de principes van Security en Privacy by Design.
Het aanbieden en gebruiken van diensten moet efficiënt zijn voor de aanbieder en gebruiker. Dat betekent dat de meerwaarde van technologie benut wordt voor zover aanbieders en gebruikers dat willen.
Een organisatie biedt een dienst aan die door een burger wordt afgenomen. Om de dienst te gebruiken, stuurt de burger een verzoek naar een de organisatie. De organisatie ontvangt het verzoek, stelt een antwoord op, en stuurt het antwoord direct terug naar de burger. Als het opstellen van een antwoord enige tijd kost, kan de organisatie in plaats van het daadwerkelijke antwoord op het verzoek, ook een antwoord geven dat aangeeft hoe een burger de status van de afhandeling van het verzoek kan volgen. De inrichting hiervan kan voor specifieke diensten zelf worden bepaald.
Basisprincipe: Het basisprincipe van Blauwe Knop Connect is dus dat een burger een gestandaardiseerd (informatie)verzoek verstuurt naar een (overheids)organisatie, en als antwoord ook weer een gestandaardiseerd (informatie)document terug ontvangt van de betreffende organisatie.
Voorbeeld (Vorderingenoverzicht Rijk): een gebruiker dient een verzoek in bij een organisatie om een overzicht te verkrijgen van zijn financiële verplichtingen jegens die organisatie, en ontvangt als antwoord het betreffende overzicht.
Architectuurvereisten:
Bij veel maatschappelijke uitdagingen is het zo dat om een bepaalde taak uit te voeren, een combinatie van meerdere diensten beschikbaar dient te zijn om de uiteindelijke taak uit te voeren. Informatie uit verschillende bronnen wordt bijvoorbeeld door de gebruiker gecombineerd om op basis van persoonlijke omstandigheden de juiste vervolgactie te ondernemen (die actie op zichzelf kan ook weer een dienst zijn). De benodigde combinatie van diensten vormen samen een ketendienst. Het is mogelijk dat diensten een rol spelen in meerdere ketendiensten. De precieze werking van een specifieke ketendienst wordt beschreven in een toepassingsprofiel.
Voorbeeld (Vorderingenoverzicht Rijk): een gebruiker spreekt bij meerdere (bijvoorbeeld drie) organisaties een informatiedienst aan (informatie over financiële verplichtingen opvragen) om één totaaloverzicht te verkrijgen van financiële verplichtingen. Daarna spreekt de gebruiker bij een vierde organisaties een andere dienst aan om een regeling (bijvoorbeeld een overheidsbrede betalingsregeling) aan te vragen op basis van de eerder opgehaalde informatie.
Architectuurvereisten:
Om een ketendienst te laten werken, moeten de achterliggende specifiek diensten vindbaar zijn voor gebruikers. Voor het beheren van informatie en afspraken voer de werking van een ketendienst, zoals de vindbaarheid van organisaties die er bij betrokken zijn, kan voor een specifieke ketendienst een centraal beheerd stelseldocument worden ingezet. Bij het ontwerpen van een ketendienst is er behoorlijk veel flexibiliteit voor de specifieke structuur van het stelseldocument voor een specifieiek dients. Zo kan de inrichting hiervan passend gemaakt worden bij de gebruikscontext(en) waarvoor de dienst wordt ontworpen. De structuur en werking van het stelseldocument voor een specifieke dienst moet beschreven worden in het toepassingsprofiel voor de betreffende ketendienst. Indien in het dienstprofiel een stelseldocument wordt beschreven moet bij de uitvoering van de ketendienst een beheerder voor het stelseldocument worden aangewezen (stelselbeheerder).
Voorbeeld (Vorderingenoverzicht Rijk): voor de ketendienst Vorderingenoverzicht Rijk wordt een stelseldocument beheerd door een stelselbeheerder. In het stelseldocument zijn alle deelnemende organisaties opgenomen. Zo kunnen gebruikers de voor hen benodigde diensten vinden om de ketendienst te gebruiken.
Architectuurvereisten:
Om bij een organisatie diensten te kunnen gebruiken, moet het bij gebruikers bekend zijn waar zij deze diensten kunnen gebruiken. Een organisatie moet dus vindbaarheidsconfiguratie publiceren. Bovendien moet bij gebruikers bekend zijn hoe zij een specifieke dienst kunnen gebruiken. Voor elke dienst moet er dus een specificatie zijn die beschrijft hoe de dienst werkt. Gebruikers kunnen dan eerst de vindbaarheidsconfiguratie opvragen, en vervolgens met de daarin aanwezig informatie de dienst afnemen.
Voorbeeld (Vorderingenoverzicht Rijk): een gebruiker haalt de vindbaarheidsconfiguratie van elke organisatie die deelneemt aan Vorderingenoverzicht Rijk op. Deze informatie wordt vervolgens gebruikt om de dienst voor het opvragen van informatie over financiële verplichtingen aan de betreffende organisatie (
citizen-financial-claims-process
) af te nemen.
Architectuurvereisten:
Ketendiensten stellen bijna altijd gebruikersvriendelijke hulpmiddelen ter beschikking aan gebruikers zodat de ketendienst gemakkelijk en efficiënt te gebruiken is. Hulpmiddelen ondersteunen de gebruiker tijdens het gebruik van een ketendienst door de uit te voeren handelingen volgorderlijk te structuren, door het toegankelijk en begrijpelijk maken van de mogelijkheden van de ketendienst, en/of door het automatiseren van handelingen. Het gebruik van hulpmiddelen bij Blauwe Knop Connect maakt het mogelijk de balans te bewaken tussen eigen controle/autonomie van gebruikers en de zorgplicht/wens om gebruikers te helpen met het gebruiken van een ketendienst.
Voorbeeld (Vorderingenoverzicht Rijk): De Vorderingenoverzicht Rijk applicatie automatiseert een flink deel van de handelingen die nodig zijn om de ketendienst te gebruiken waarmee burgers een overzicht kunnen samenstellen van hun financiële verplichtingen aan overheidsorganisatie. De software handelt alle communicatie met de overheidsorganisaties af, fungeert als persoonlijke digitale ordner (in het domein van de burger zelf) voor het verzamelen van de antwoorden, en brengt overzicht aan in de ontvangen informatie, zodat deze waardevol en bruikbaar is.
Architectuurvereisten:
Blauwe Knop Connect is ontworpen volgens de principes van security by design om de veiligheid (beschikbaarheid, integriteit, confidentialiteit) van gegevens te waarborgen bij het gebruiken van diensten. Blauwe Knop Connect stelt eisen aan zowel organisatie als gebruiker op het gebied van communicatie en authenticatie. Diensten kunnen vervolgens vertrouwen op deze veiligheidsaspecten en kunnen daardor zelf eenvoudiger blijven.
Communicatie bij het afnemen van diensten dient plaats te vinden via een kanaal dat de veiligheid waarborgt. Dit kanaal kan per dienst verschillen.
Enkele voorbeelden zijn:
Publiek internet: met gebruik van TLS (HTTPS)
Publiek internet: met gebruik van tweezijdig TLS (mTLS)
Directe communicatie op locatie (bezorgen van berichten ter adres van de wederpartij)
Postdienst (bewaking van briefgeheim)
Diensten kunnen zelf aanvullende maatregelen nemen om de veiligheid verder te vergroten. Bijvoorbeeld extra versleuteling, dataminimalisatie of andere vormen van bescherming.
Authenticatie bestaat over het algemeen uit twee stappen:
Voorbeelden van controleerbaar bewijs zijn:
Het tonen van (digitale) certificaten of credentials
Het zetten van een digitale handtekening
Het zetten van een natte handtekening
Het controleren van het bewijs geschiedt op de wijze die past bij het aangeleverde bewijsmateriaal.
Veilige digitale verbindingen op basis van Blauwe Knop Connect werken als volgt:
De partijen zijn nu wederzijds geauthenticeerd.
Het resultaat van de authenticatie wordt beschikbaar gesteld aan de afgenomen dienst, zodat die erop kan vertrouwen en identiteitsinformatie en/of sleutelparen kan (her)gebruiken voor verdere veiligheidsmaatregelen zoals toegangscontrole tot de specifieke dienst, personalisatie en/of versleuteling.
Architectuurvereisten:
Aan een zelfgekozen sleutelpaar in een hulpmiddel kan (net als bij een zelfgekozen natte handtekening) enkel vertrouwen worden gesteld indien het sleutelpaar (of natte handtekening) reeds bij de partij die de sleutel controleert bekend is (alleen dan kan een gepresenteerde sleutel of handtekening worden vergeleken met de eerder bekende sleutel of handtekening).
Als dat nog niet het geval is, en zekerheid over de identiteit van de gebruiker voor het leveren van de dienst vereist is, is het nodig om extra bewijsmateriaal in de vorm van certificaten en/of credentials aan te leveren bij het opzetten van veilige verbindingen.
De gebruiker kan dit aanvullende bewijsmateriaal verkrijgen via het proces van legaliseren. Tijdens het legaliseren wordt de identiteit van de burger gecontroleerd en wordt de digitale sleutel (of natte handtekening) gekoppeld aan de persoon. Dit is een reeds lang bestaande juridische praktijk genaamd Legaliseren (van handtekeningen), waarvoor men in de papieren wereld onder meer terecht kan bij gemeenten, notarissen en/of (in het buitenland) bij een Nederlandse ambassade.
Digitaal kan dit op tenminste twee manieren:
Architectuurvereisten:
Omdat tijdens het verbinden met een dienst geregeld is dat een gebruiker van een dienst geauthenticeerd is, kan er bij de afhandeling van de dienst op worden vertrouwd dat de gebruiker van de dienst inderdaad is wie hij zegt dat hij is. De dienst kan dus zelf eenvoudiger blijven, omdat de complexiteit voor het veilig verbinden buiten de dienst wordt gehouden.
Een dienst krijgt bovendien toegang tot de informatie die tijdens de authenticatie van de gebruiker gecontroleerd is. Deze informatie kan daarom vervolgens worden gebruikt bij het bepalen of de dienst wordt verleend (autorisatie), en tijdens het afhandelen van de dienst (personalisatie). Deze en andere gegevens die worden gebruikt tijdens het afhandelen van de dienst mogen niet gebruikt worden voor andere doeleinden dan het afhandelen van de dienst.
Voorbeeld (Vorderingenoverzicht Rijk): Omdat de dienst waarmee een gebruiker informatie over betalingsverplichting aan een organisatie opvraagt wordt aangesproken via een veilige Blauwe Knop Connect verbinding, kan deze dienst bij de afhandeling (het verwerken van het informatieverzoek) gebruik maken van de tijdens de authenticatie verstrekte identiteitsgegevens (het BSN) om een persoonlijk antwoord op te stellen. Bovendien kan het tijdens het verbinden gebruikte sleutelmateriaal worden gebruikt om het persoonlijke antwoord te versleutelen zodat alleen de betreffende gebruiker het antwoord kan openen.
Architectuurvereisten:
De architectuur zoals hier beschreven kan op verschillende manieren in de praktijk worden gebracht. Via verschillende media (fysiek, digitaal, etc.), met verschillende soorten hulpmiddelen (pen en papier, formulieren, hard- en software). De rollen van stelselbeheerder, legalisator en deelnemende gebruikers en organisaties kunnen op verschillende manieren belegd worden (overheden, notarissen, dienstverleners, etc.), maar ook digitaal met toepassing van meer klassieke (OpenID4VP) of moderne standaarden (FSC voor natuurlijke personen). En met gebruik van verschilende wijzen van identiteitscontrole bij deelname van natuurlijke personen aan het stelsel (DigiD, Digitale Identiteit, identiteitscontrole in persoon). In het volgende hoofdstuk worden een aantal van deze implementaties in detail gespecificeerd.
⌛ nog niet beschikbaar: specificaties stelselbeheer
⌛ nog niet beschikbaar: specificaties vindbaarheidsconfiguratie
Deze sectie beschrijft een protocol waarmee de app aan een verifier kan aantonen dat hij controle heeft over de private key behorende bij een bepaalde public key. Dit protocol kan vervolgens gebruikt worden door:
Het protocol levert ook een sessietoken op die de gecreëerde sessie representeert voor gebruik in verdere applicatie-specifieke protocollen, en sleutelmateriaal om verder protocolverkeer te versleutelen.
In hoofdlijnen ziet dit protocol er als volgt uit.
CreateSession
protocol (beknopte weergave)Het protocol in deze sectie is generiek in de zin dat deze voor authenticatie bij verschillende organisaties kan worden toegepast, voor verschillende en (mogelijk) ongerelateerde toepassingen, zoals hierboven beschreven in de sectie over Architectuur en functies.
Er zijn daarom dus verschillende domeinen, of scopes, waarin authenticatie plaats moet kunnen vinden.
Om ervoor te zorgen dat authenticatie binnen het ene domein niet verward kan worden met authenticatie in het andere domein (bijvoorbeeld dat een gebruiker met een Verifiable Credential verkregen voor de ene toepassing zich kan authenticeren bij een andere ongerelateerde toepassing) bevat het Verifiable Credential wat gebruikt wordt in dit protocol een scope
parameter met een string als waarde.
Een organisatie gebruikt het protocol in deze sectie om een gebruiker te authenticeren teneinde een specifieke dienst aan te kunnen bieden.
Het domein van die dienst wordt gerepresenteerd door de scope
.
De organisatie MOET nadat het protocol in deze sectie is afgerond de scope
uit het Verifiable Credential vergelijken met de scope
die hij verwacht, en afbreken als die niet overeenkomen.
De App MAG meerdere Verifiable Credentials hebben met verschillende waardes voor de scope
parameter, zodat hij zich kan authenticeren in verschillende toepassingen.
De App moet voordat het protocol in deze sectie begint weten welke scope
de Verifier verwacht.
Het Verifiable Credential die de App ontvangt van de App Manager en tijdens authenticatie richting de Verifier presenteert MOET een JWS zijn, met daarin de volgende velden (claims):
Claim Name | Claim Description |
---|---|
app_public_key |
Public key van de burger in DER formaat, Base64-encoded |
scope |
Scope (zie "Scope" sectie hierboven) |
iss |
Organisatie-identificatienummer van de app manager |
iat |
(Issued At) Moment van uitgifte (Unix timestamp) |
nbf |
(Not Before) Begindatum van geldigheid (Unix timestamp) |
exp |
(Expiry) Verloopdatum (Unix timestap) |
bsn |
Burgerservicenummer |
given_name |
Voornaam/voornamen |
family_name |
Achternaam/achternamen |
Hierin zijn alle drieletterige claims behalve bsn
gestandaardiseerd in het IANA "JSON Web Token Claims" register.
De JWS MAG een kid
veld in de header bevatten die de public key van de App Manager identificeert waarmee de JWS geverifieerd kan worden. Indien aanwezig MOET dit veld een string zijn.
Samen met het iss
veld kan de App Manager public key worden opgezocht middels een proces dat kan verschillen per scope
(zie de "App manager public key lookup" sectie hieronder).
Voorbeeld:
{
"typ": "jwt",
"alg": "ES256",
"kid": "1"
}
.
{
"app_public_key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0Ejs8jmnIjkb+wMpaQugg6VY6j7bpgYQYGTt7Ji/RhSJvQKT9Q63RsfD0XtdIlgosZkV70ocwbYhL8HadgVulQ==",
"scope": "nl.vorijk.oauth_scope.blauwe_knop",
"iss": "00000001234567890000", // OIN
"iat": 1704063600,
"nbf": 1704063600,
"exp": 1735686000,
"bsn": "999991772",
"given_name": "Willeke Liselotte",
"family_name": "De Bruijn"
}
Voordat de App zich authenticeert aan een Verifier MOET hij controleren dat het Verifiable Credential volgens de timestamp in het exp
veld niet verlopen is, of gaat verlopen binnen één minuut. Als dat wel zo is, dan MOET de App het Verifiable Credential eerst verversen voordat hij aan het protocol begint.
Tijdens authenticatie ondertekent de App met diens private key (waarvan de public key in de app_public_key
claim in het Verifiable Credential staat) een willekeurige string, genaamd de nonce
, die gegenereerd is door de Verifier. Dit resulteert in een JWS waarin de volgende claims MOETEN staan:
Claim naam | Claim beschrijving |
---|---|
sub |
String die de waarde "challenge_response" MOET hebben |
aud |
OIN van de Verifier waar de App naar authenticeert |
nonce |
Willekeurige string gegenereerd door de Verifier |
Voorbeeld:
{
"typ": "jwt",
"alg": "ES256",
}
.
{
"sub": "challenge_response",
"aud": "00000001234567890001",
"nonce": "L9E4aGM9ZzhhC7WLVxa1XKOzie7bzKh0"
}
Vervolgens maakt de App een Verifiable Presentation, die in deze standaard een JSON object is met daarin de volgende velden:
Naam | Beschrijving |
---|---|
app_nonce_signature |
Bovenstaande JWS |
certificate_type |
MOET zijn ofwel "app_manager_jwt_certificate" ofwel "certificate_type_self_signed" |
certificate |
Het Verifiable Credential als certificate_type gelijk is aan "app_manager_jwt_certificate" ; of een self-signed JWT (zie hieronder) met daarin de app public key als certificate_type gelijk is aan "certificate_type_self_signed" |
session_aes_key |
AES key (Base64-encoded) voor versleuteling van verder verkeer |
Als certificate_type
gelijk is aan certificate_type_self_signed
, dan MOET het certificate
veld een JWS hebben van de volgende vorm:
{
"typ": "jwt",
"alg": "ES256",
}
.
{
"app_public_key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE16YiVsBE1K+I5iksaBbkc1Evfk6/0wPSO3Rwni1HVqOcmy5Hjr61/arQxibswOj+v/8uBCA/AzMFVvqFOI96Vg==",
"sub": "certificate_type_self_signed"
}
Deze JWS MOET getekend zijn met de private key waarvan de public key in het app_public_key
veld staat, en de JWS in het app_nonce_signature
MOET ook met deze private key getekend zijn.
Voorbeelden van Verifiable Presentations:
certificate_type
gelijk is aan app_manager_jwt_certificate
:{
"app_nonce_signature": "eyJ0eXAiOiJqd3QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJjaGFsbGVuZ2VfcmVzcG9uc2UiLCJhdWQiOiIwMDAwMDAwMTIzNDU2Nzg5MDAwMSIsIm5vbmNlIjoiTDlFNGFHTTlaemhoQzdXTFZ4YTFYS096aWU3YnpLaDAifQ.1TtHJXzBS3OGZzMKcalw5qoQb8H-WAwTIeOIWROM0hCDxKRKQdQyz_jESVycQSwnk9Ldgv87_e9KZr4SkOh_NQ",
"certificate_type": "app_manager_jwt_certificate",
"certificate": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBfcHVibGljX2tleSI6Ik1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTE2WWlWc0JFMUsrSTVpa3NhQmJrYzFFdmZrNi8wd1BTTzNSd25pMUhWcU9jbXk1SGpyNjEvYXJReGlic3dPait2Lzh1QkNBL0F6TUZWdnFGT0k5NlZnPT0iLCJzY29wZSI6Im5sLnZvcmlqay5vYXV0aF9zY29wZS5ibGF1d2Vfa25vcCIsImlzcyI6IjAwMDAwMDAxMjM0NTY3ODkwMDAwIiwiaWF0IjoxNzA0MDYzNjAwLCJuYmYiOjE3MDQwNjM2MDAsImV4cCI6MTczNTY4NjAwMCwiYnNuIjoiOTk5OTkxNzcyIiwiZ2l2ZW5fbmFtZSI6IldpbGxla2UgTGlzZWxvdHRlIiwiZmFtaWx5X25hbWUiOiJEZSBCcnVpam4ifQ.3r49YRPFBj6imBBMJHH9NHS-G9WZW6omC78wFlqC8Z3vV7TnKzdQBc3b3_2P6TgqwAjB4gZs9Gd-NNijygkv3w",
"session_aes_key": "/YyG+FNLinwQqZFgGUribQ=="
}
certificate_type
gelijk is aan certificate_type_self_signed
:{
"app_nonce_signature": "eyJ0eXAiOiJqd3QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJjaGFsbGVuZ2VfcmVzcG9uc2UiLCJhdWQiOiIwMDAwMDAwMTIzNDU2Nzg5MDAwMSIsIm5vbmNlIjoiTDlFNGFHTTlaemhoQzdXTFZ4YTFYS096aWU3YnpLaDAifQ.1TtHJXzBS3OGZzMKcalw5qoQb8H-WAwTIeOIWROM0hCDxKRKQdQyz_jESVycQSwnk9Ldgv87_e9KZr4SkOh_NQ",
"certificate_type": "certificate_type_self_signed",
"certificate": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBfcHVibGljX2tleSI6Ik1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTE2WWlWc0JFMUsrSTVpa3NhQmJrYzFFdmZrNi8wd1BTTzNSd25pMUhWcU9jbXk1SGpyNjEvYXJReGlic3dPait2Lzh1QkNBL0F6TUZWdnFGT0k5NlZnPT0iLCJzdWIiOiJjZXJ0aWZpY2F0ZV90eXBlX3NlbGZfc2lnbmVkIn0.N5ukfBIFd-17vx8I5SszixkZGW4Ay4Zq2-e1eXqmJ5l-kIC_TVWmc5b72-SB5e7yER2Kaq1GS3g4SFQjD9LuVA",
"session_aes_key": "/YyG+FNLinwQqZFgGUribQ=="
}
De Verifier MOET de volgende stappen uitvoeren bij het verifiëren van de Verifiable Presentation:
session_aes_key
in de Verifiable Presentation bestaat uit 16 bytes na Base64 decoding.certificate
veld een string is van de vorm van een JWS, en parse de header en body van de JWS (nog zonder de JWS te verifiëren).certificate_type
in de Verifiable Presentation een van de volgende twee waardes bevat."certificate_type_self_signed"
. Verifieer in dit geval de JWS in het certificate
veld als volgt.app_public_key
veld bevat, met daarin een Base64-encoded DER-encoded EC public key van een ondersteund algoritme en curve die geldig is (de coördinaten refereren naar een bestaand element van de elliptic curve).sub
veld met waarde "certificate_type_self_signed"
bevat."app_manager_jwt_certificate"
. Verifieer in dit geval de JWS in het certificate
veld als volgt.kid
veld uit de header (indien aanwezig), en de iss
en scope
velden uit de body, zoek de public key van de app manager op. (Dit proces kan verschillen per scope
, zie de "App manager public key lookup" sectie hieronder.)certificate
veld als JWS. Controleer hierbij ook de nbf
en exp
velden als in § 4.1 van RFC 7519.app_public_key
veld uit de JWS als Base64-encoded DER-encoded EC public key, en controleer dat hij van een ondersteund algoritme en curve is en dat hij geldig is (de coördinaten refereren naar een bestaand element van de elliptic curve).app_nonce_signature
in de Verifiable Presentation een geldige JWS is ten opzichte van de geparseerde app_public_key
uit de JWS van de vorige stap, met als claims:sub
met als vaste waarde challenge_response
,aud
veld,nonce
die de Verifier eerder naar de App heeft gestuurd.De bsn
, given_name
, en family_name
velden uit het Verifiable Credential (in het geval van app_manager_jwt_certificate
) of de app_public_key
uit het certificate
(in het geval van certificate_type_self_signed
) zijn nu gevalideerd en klaar voor gebruik.
Apps en organisaties moeten gegeven de scope
, de iss
en de kid
uit het Verifiable Credential de public key van de App Manager (de uitgever van het Verifiable Credential) kunnen opzoeken, waarmee het Verifiable Credential kan worden geverifieerd.
Hoe dit gedaan moet worden verschilt per scope
.
In het geval dat de scope
gelijk is aan nl.vorijk.oauth_scope.blauwe_knop
gaat dit proces als volgt.
Het protocol ziet er in een sequence diagram als volgt uit.
CreateSession
protocolEr volgt een expliciete beschrijving van de protocolberichten, die refereert aan de genummerde stappen in bovenstaand sequence diagram. Alle whitespace in JSON structuren is enkel bedoeld voor de leesbaarheid van deze standaard en dient niet te worden opgenomen in implementaties.
De App vraagt een nieuwe challenge op met een HTTP POST
request zonder body naar /v1/challenge
.
De Verifier genereert met een CSPRNG een verse nonce bestaande uit minimaal 16 willekeurige bytes.
De Verifier antwoordt op het HTTP request van de App uit stap 2 met een JWS, ondertekend met de root organization EC private key, en met de volgende claims:
sub
: een string die de waarde "challenge"
MOET hebben.nonce
: de nonce uit Stap 4, Base64-encoded.ephemeral_organization_ec_public_key
: de Base64-encoding van de DER-encoded ephemeral organization EC public key.De JWS MAG een kid
header gebruiken om de public key aan te duiden waarmee de JWS geverifieerd moet worden.
Voorbeeld:
{
"typ": "jwt",
"alg": "ES256",
"kid": "1"
}
.
{
"sub": "challenge",
"nonce": "BZpwbYf2TIYxvJEQY2FtAg==",
"ephemeral_organization_ec_public_key": "MFkw....Wzhw=="
}
De App:
sub
veld met waarde challenge
bevat.De App genereert met een CSPRNG een nieuwe willekeurige AES key van de juiste grootte (128 bits in het geval van AES128 en 256 bits in het geval van AES256).
De App maakt het Verifiable Presentation als in de "Verifiable Presentation" sectie hierboven, en versleutelt deze middels ECDH-ES in de vorm van een JWE naar de ephemeral organization EC public key van de Verifier.
De App stuurt middels een HTTP POST
naar /v1/complete
het volgende JSON object naar de Verifier:
nonce
: de nonce
die de Verifier eerder aanleverde.encrypted_challenge_result
: de Verifiable Presentation JWE uit de vorige stap.Voorbeeld:
{
"nonce": "BZpwbYf2TIYxvJEQY2FtAg==",
"encrypted_challenge_result": "eyBLyPjsJZs...5Y2tNYLRui"
}
De Verifier MOET de Verifiable Presentation verifiëren als in de "Verifiable Presentation" sectie hierboven.
De Verifier genereert met een CSPRNG een willekeurige alfanumerieke session token die minimaal 22 karakters MOET zijn (zodat hij minimaal 128 bit entropie heeft).
In deze variant maakt de app gebruik van het OpenID4VP protocol voor authenticeren richting een organisatie. Dit protocol is gebaseerd op OpenID Connect en OAuth (RFC 6749), met als belangrijkste verschil dat de eindgebruiker (de app) tijdens het protocol rechtstreeks en uitsluitend communiceert met de Relying Party/Verifier (de organisatie), zonder rechtstreekse betrokkenheid van de partij die de identiteit van de eindgebruiker attesteert (de App Manager). Tijdens het protocol authenticeert de app zich richting de organisatie middels een Verifiable Credential, in ons geval de App Manager JWS.
NB: het OpenID4VP protocol is in actieve ontwikkeling, en geregeld worden er nieuwe draft versies van uitgebracht. Deze sectie is gebaseerd op draft 20 van OpenID4VP. Inmiddels bestaan er een aantal nieuwe draft versies.
De eenvoudigste variant van dit protocol ziet er in het algemeen als volgt uit.
In het Authorization Request staan de gegevens die de Verifier van de App opvraagt, de nonce die de App moet ondertekenen met de private keys van de Verifiable Credentials die hij gebruikt, en de identiteit van de Verifier. De Authorization Response bevat de disclosure (de Verifiable Presentation(s)).
Dezelfde terminologie als in de Terminologie sectie, met daaraan toegevoegd:
Tijdens het protocol fungeert de Verifier als een OAuth Client ten opzichte van de App, die fungeert als een OAuth Authorization Server.
Voor de duidelijkheid volgt hier een overzicht van hoe de verschillende standaarden naar de twee partijen refereren.
OAuth | OpenID Connect | OpenID4VP | Deze standaard | |
---|---|---|---|---|
Verstuurt Authorization Request | Client | Client | Verifier | Verifier |
Verstuurt Authorization Response | Authorization Server | OP (OpenID Provider) | Wallet | App |
Bovenop de "Cryptografie" sectie geldt in deze sectie het volgende:
Elk versleuteld bericht in deze sectie MOET in de vorm van een JWE zijn (RFC 7516; zie ook de Cryptografie sectie hierboven).
Het Verifiable Credential die de App ontvangt van de App Manager en tijdens authenticatie richting de Verifier presenteert MOET een JWS zijn, met daarin de volgende velden (claims):
Claim naam | Claim beschrijving |
---|---|
iss |
Organisatie-identificatienummer (OIN) van de App Manager |
iat |
Issued At (tijdstip van uitgifte) |
nbf |
Not Before (niet geldig voor) |
exp |
Expiration Time (verloopdatum) |
cnf |
Public key van de App, als in RFC 7800 |
given_name |
Voornaam/voornamen |
family_name |
Achternaam/achternamen |
bsn |
Burgerservicenummer |
Hierin zijn alle claims behalve de laatste gestandaardiseerd in het IANA "JSON Web Token Claims" register.
De JWS MAG een kid
veld in de header bevatten die de public key van de App Manager identificeert waarmee de JWS geverifieerd kan worden.
Voorbeeld:
{
"typ": "jwt",
"alg": "ES256",
"kid": "1"
}
.
{
"iss": "00000001234567890000", // OIN
"iat": 1704063600,
"nbf": 1704063600,
"exp": 1735686000,
"cnf": {
"jwk": {
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"use": "sig",
"x": "2xJs8w74D4glCnAF5W8Ax9EJf6XGSLCTlotBDrMbRd0",
"y": "YQtuD1-gqVB6XDWIr76jRYL2xNr7YdnV1V7hGogwHgw"
},
},
"given_name": "Willeke Liselotte",
"family_name": "De Bruijn",
"bsn": "999991772"
}
Tijdens authenticatie van de App richting een Verifier ondertekent de App met diens private key (waarvan de public key in de cnf
claim in het Verifiable Credential staat) een willekeurige string, genaamd de nonce
, die gegenereerd is door de Verifier. Dit resulteert in een JWS waarin de volgende claims MOETEN staan:
Claim naam | Claim beschrijving |
---|---|
aud |
URL van de Verifier waar de App naar authenticeert |
nonce |
Willekeurige string gegenereerd door de Verifier |
Voorbeeld:
{
"typ": "jwt",
"alg": "ES256",
}
.
{
"aud": "https://openid4vp-authentication-process.example.com",
"nonce": "L9E4aGM9ZzhhC7WLVxa1XKOzie7bzKh0"
}
Vervolgens maakt de App een Verifiable Presentation, die in deze standaard een JSON object is waarin beide JWS MOETEN staan:
{
"vc": "eyJjc....5TRSJ9", // Verifiable Credential
"disclosure": "eyJjc....f7B-q2" // Bovenstaande JWS
}
De Verifier MOET de volgende stappen uitvoeren bij het verifiëren van de Verifiable Presentation:
vc
en disclosure
velden bevat, en dat dit strings zijn.vc
string als JWS tegen de public key(s) van de App Manager(s), eventueel met behulp van het iss
veld uit de JWS body en/of het kid
veld in de JWS header. Controleer hierbij ook dat het iss
veld de OIN bevat van een vertrouwde App Manager, en valideer de nbf
en exp
velden als in § 4.1 van RFC 7519.vc
JWS een cnf
veld heeft, met daarin een jwk
veld, en parse de public key die daarin staat.disclosure
string als JWS.disclosure
JWS een aud
veld heeft en dat de waarde daarvan de URL van de Verifier is.disclosure
JWS een nonce
heeft en dat de waarde daarvan overeenkomt met wat de Verifier eerder had gegenereerd.vc
JWS alle velden uit de tabel in voorgaande sectie heeft, voor zover niet al gecontroleerd in bovenstaande stappen.In deze sectie beschrijven we het protocol op hoog niveau door keuzes te nemen in bepaalde parameters die de OpenID4VP standaard aanbiedt, en door te beschrijven hoe we encryptie op applicatieniveau realiseren.
De OpenID4VP standaard biedt op een aantal plekken verschillende opties en varianten aan. Deze standaard maakt daarin de volgende keuzes.
GET
uit op de Request URI om het Request Object te verkrijgen.
Deze uitbreiding is optioneel in RFC 9101, maar MOET in deze standaard gebruikt worden.response_type
parameter van het Authorization Request (gedefinieerd in OAuth) MOET de waarde vp_token
hebben, gedefinieerd in § 5.4 van OpenID4VP.
Deze waarde bepaalt dat de App een Verifiable Presentation op moet nemen in een parameter genaamd vp_token
in de Authorization Response.client_id_scheme
parameter van het Authorization Request MOET pre-registered
zijn.
De waarde van de client_id
parameter van het Authorization Request MOET dus per de OpenID4VP standaard een identifier van de Verifier zijn die de App van tevoren al kent.
De App gebruikt deze identifier tijdens de sessie om de public keys van de Verifier op te zoeken waarmee het Request Object JWS geverifieerd kan worden en waarnaar de Authorization Response JWE versleuteld kan worden.scope
parameter van het Authorization Request MOET zijn nl.vorijk.oauth_scope.blauwe_knop.vp
. Per de OpenID4VP standaard MOETEN de presentation_definition
en presentation_definition_uri
parameters dus NIET opgenomen zijn in het Authorization Request.response_mode
toe aan het Authorization Request, die bepaalt hoe de App de Authorization Response terug moet geven. In deze standaard MOET de response_mode
parameter de waarde direct_post.jwt
hebben, zoals gespecificeerd in § 6.2 van OpenID4VP.
Deze waarde bepaalt het volgende:response_uri
parameter opnemen.response_uri
versturen, URL-encoded in een HTTP POST
naar de response_uri
.response_uri
endpoint van de Verifier MOET, als de Verifier de Authorization Response met succes heeft geverifieerd, antwoorden met een JSON object met daarin een redirect_uri
parameter.
Dit is gedefinieerd als optioneel in § 6.2 van OpenID4VP maar is verplicht in deze standaard.
(Deze redirect_uri
moet niet verward worden met de identiek genaamde redirect_uri
parameter die normaal gesproken in OAuth in het Authorization Request staat.)
Er MOET een vers willekeurige string gegenereerd met een CSPRNG in deze request_uri
parameter staan zodat deze uniek is per sessie.Het protocol eindigt met de App die van de Verifier de redirect_uri
genoemd in het laatste punt hierboven ontvangt. Normaal gesproken in OpenID4VP opent de App vervolgens deze redirect_uri
in een browser, zodat daarin de flow van de applicatie verder kan gaan. In deze standaard wordt in plaats daarvan de redirect_uri
gebruikt als drager van een access token, in de volgende vorm:
bk-connect://organization-discovery-url/?access-token=example-access-token&access-token-type=OpenID4VP
Het access token hierin verleent de App toegang tot geauthenticeerde endpoints van de Dienst (dat wil zeggen, endpoints die alleen geauthenticeerde gebruikers mogen aanroepen, voor bijvoorbeeld het ophalen van documenten die betrekking hebben op de gebruiker).
Alle verkeer tussen de App en de (infrastructuur van de) Verifier en/of Dienst MOET gebruik maken van minstens TLS 1.3.
In de praktijk eindigt de TLS-tunnel in de infrastructuur van de Verifier of Dienst vaak bij een reverse proxy, en is het gegevensverkeer dus in plain text zichtbaar voor die reverse proxy en mogelijke middleware die daarachter staat. Daarom gebruikt deze standaard ook encryptie op applicatieniveau (dat wil zeggen, bovenop de TLS-encryptie van de transportlaag) om de gegevens die verstuurd worden tijdens en na de OpenID4VP-sessie te beschermen als volgt.
De keuzes beschreven in bovenstaande sectie resulteren bij elkaar in een protocol die we in deze sectie verder uitwerken en expliciet maken.
Het protocol ziet er in een sequence diagram als volgt uit.
Er volgt een expliciete beschrijving van de protocolberichten, die refereert aan de genummerde stappen in bovenstaand sequence diagram. Alle whitespace in JSON structuren is enkel bedoeld voor de leesbaarheid van deze standaard en dient niet te worden opgenomen in implementaties.
De App voert een GET
uit op de request_uri
van de Verifier:
GET /request_uri HTTP/1.1
De Verifier genereert de Request Object JWS, met daarin de volgende velden in de payload:
client_id
: URL die de Verifier identificeert.response_uri
: URL waarnaar de App het Authorization Response dient te sturen.nonce
: een willekeurige string gebruik makend van de URL-safe Base64 karakterset, genereerd met een CSPRNG, die minimaal 22 karakters MOET zijn (zodat hij minimaal 128 bit entropie heeft).client_metadata
: JSON object met een jwks
veld erin, met een keys
veld daarin die de ephemeral verifier encryption public key uit Stap 2 bevat, als per RFC 7591 en RFC 7517.De Verifier MAG een kid
veld opnemen in de JWS header om aan te duiden met welke public key de JWS geverifieerd moet worden.
Voorbeeld:
{
"typ": "jwt",
"alg": "RS256",
"kid": "1",
}
.
{
"response_type": "vp_token",
"response_mode": "direct_post.jwt",
"client_id_scheme": "pre-registered",
"client_id": "https://organization.example.com",
"response_uri": "https://organization.example.com/response_uri",
"scope": "nl.vorijk.oauth_scope.blauwe_knop.vp",
"nonce": "L9E4aGM9ZzhhC7WLVxa1XKOzie7bzKh0",
"client_metadata": {
"jwks": {
"keys": [{
"kty": "EC",
"crv": "P-256",
"alg": "ECDH-ES",
"use": "enc",
"x": "GD006BBz6L9x1cQZQtihWeL_CBcJqjMerlFhGNHt6q8",
"y": "dUCZyFZXdDNDTC_mRRveNtY_qT8D-LCDGoaNwE8i8rU"
}]
}
}
}
De Verifier antwoordt het GET
request uit stap 1 met bovenstaande JWS als volgt:
HTTP/1.1 200 OK
Content-type: application/oauth-authz-req+jwt
eyJhb...x5Nz-g
Gegeven het Verifiable Credential produceert de App een Verifiable Presentation per de "Verifiable Presentation" sectie hierboven. Vervolgens plaatst de App de Verifiable Presentation in het vp_token
veld in de Authentication Response, samen met de ephemeral app encryption public key.
Voorbeeld:
{
"vp_token": {
"vc": "eyJjc....5TRSJ9",
"disclosure": "eyJjc....f7B-q2"
},
"presentation_submission": {
"id": "nl.vorijk.presentation_submission.blauwe_knop",
"definition_id": "nl.vorijk.presentation_definition.blauwe_knop",
"descriptor_map": [
{
"id": "nl.vorijk.descriptor_map.blauwe_knop",
"format": "jwt",
"path": "$",
"path_nested": {
"format": "jwt",
"path": "$.vc"
}
}
]
},
"jwks": {
"keys": [{
"kty": "oct",
"alg": "A128KW",
"k": "GawgguFyGrWKav7AX4VKUg"
}]
}
}
Hierin MOET presentation_submission
altijd bovenstaande waarde hebben, per § 6.1 van OpenID4VP.
Dit geeft aan de Verifier aan waar deze het Verifiable Credential kan vinden binnen de ontvangen Verifiable Presentation.
Het jwks
veld hierin maakt het mogelijk voor de app om een AES encryption key te versturen waarmee de app en de gebruikende applicatie data op applicatieniveau kunnen versleutelen, wanneer ze verkeer naar elkaar sturen nadat de OpenID4VP-sessie is afgerond.
Dit is een toevoeging van deze standaard en niet gedefinieerd in OpenID4VP.
De JSON-structuur van het jwks
veld is gedefinieerd in RFC 7591 en RFC 7517 en net als in het Request Object.
Bovenstaand voorbeeld maakt gebruik van de A128KW
waarde voor alg
, als gedefinieerd in § 4.1 van RFC 7518, welke aangeeft dat wanneer deze key gebruikt wordt om een JWE mee te maken deze JWE ook gebruik moet maken van A128KW
voor alg
.
Andere waardes uit deze RFC die gebruikt MOGEN worden:
A192KW
A256KW
dir
Na het produceren van bovenstaand Authorization Response versleutelt de App hem naar de ephemeral verifier encryption public key uit het Request Object, door het op te nemen als payload in een JWE als in § 6.3 van OpenID4VP, en stuurt het op naar de response_uri
uit het Request Object, als volgt:
POST /response_uri HTTP/1.1
Content-Type: application/x-www-form-urlencoded
response=eyJra...9t2LQ
De Verifier MOET de Verifiable Presentation in de Authorization Response valideren als in § 6.5 van OpenID4VP en de "Verifiable Presentation" sectie hierboven.
De Verifier genereert met een CSPRNG een willekeurige alfanumerieke access-token
die minimaal 32 karakters MOET zijn, en plaatst deze in een URI van de volgende vorm:
bk-connect://organization-discovery-url/?access-token=9YryEzKheqpx5iS2JsDNRHoydCBIsAKV&access-token-type=OpenID4VP
De Verifier antwoordt op de HTTP POST
uit stap 10 met bovenstaande URI als volgt:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"redirect_uri": "bk-connect://organization-discovery-url/?access-token=9YryEzKheqpx5iS2JsDNRHoydCBIsAKV&access-token-type=OpenID4VP"
}
Als het valideren van de Authorization Response faalt MOET de Verifier in plaats van bovenstaande antwoorden als in § 6.4 van OpenID4VP.
Dit protocol maakt gebruik van het CreateSession
protocol voor het uitgeven of verversen van een Verifiable Credential, met certificate_type_self_signed
als waarde voor de certificate_type
parameter. Hiermee wordt het volgende bereikt:
Vervolgens opent de App de browser om daarin de gebruiker te identificeren en authenticeren, middels DigiD. Indien dit slaagt wordt de app weer vanuit de browser geopend middels de Universal Link van de App, met een registration_token
als (versleutelde) parameter, die daarna ingewisseld kan worden bij de Credential Issuer voor het Verifiable Credential.
Nadat het Verifiable Credential is geregistreerd geldt de gebruiker als geregistreerd bij de Credential Issuer.
De App en de Credential Issuer slaan het registration_token
beiden op voor later hergebruik. De Credential Issuer slaat daarbij ook de attributen van de gebruiker op die hij eerder in het Verifiable Credential heeft uitgegeven.
Wanneer het Verifiable Credential van de App is verlopen, kan deze middels het eerder ontvangen registration_token
een vers Verifiable Credential opvragen bij de Credential Isser.
Het protocol ziet er in een sequence diagram als volgt uit.
Issuance
protocolEr volgt een expliciete beschrijving van de protocolberichten, die refereert aan de genummerde stappen in bovenstaand sequence diagram. Alle whitespace in JSON structuren is enkel bedoeld voor de leesbaarheid van deze standaard en dient niet te worden opgenomen in implementaties.
De App en de Credential Issuer voeren het CreateSession
protocol uit. De App MOET hierbij voor de certificate_type
parameter de waarde certificate_type_self_signed
gebruiken. Dit protocol resulteert in een session_token
en een AES key voor later gebruik.
De App navigeert naar https://credential-issuer.example.com/register_app?session_token=...
, waarbij het session_token
uit de vorige stap wordt ingevuld.
(NB: in andere protocollen in deze standaard wordt het session_token
uit het CreateSession
protocol verstuurd in de Authorization
HTTP header van een POST
request. Dat is in dit geval echter niet mogelijk. Daarom wordt het session_token
in dit geval middels een URL parameter in de URL van de issuer meegegeven.)
De eindgebruiker wordt geïndentificeerd middels DigiD want resulteert in diens BSN.
De Credential Issuer genereert met een CSPRNG een willekeurige alfanumerieke registration_token
die minimaal 22 karakters MOET zijn (zodat hij minimaal 128 bit entropie heeft).
De Credential Issuer maakt een JSON object met daarin de volgende velden:
sub
: MOET de vaste waarde registration_token
hebben.iss
: de URL van de Credential Issuer (dat wil zeggen, de URL waar de App naartoe navigeert in stap 3, zonder register_app?session_token=...
).registration_token
: het registration_token
uit stap 8.Voorbeeld:
{
"sub": "registration_token",
"iss": "https://credential-issuer.example.com/",
"registration_token": "LarRGSbmUPYtRYO6BQ4yn8"
}
Vervolgens versleutelt de Credential Issuer dit object met de session AES key in de vorm van een JWE (zie de Cryptografie sectie hierboven).
De Credential Issuer roept de Universal Link van de App aan, bijvoorbeeld door de Universal Link te plaatsen in een link op zijn website die de eindgebruiker moet gebruiken, of door een HTTP redirect naar de Universal Link. De Credential Issuer levert de volgende parameters URL-encoded mee in de Universal Link:
registration_token
: het registration token JWE uit de vorige stap.De app ontvangt de Universal Link en parseert daaruit de registration_token
.
Voorbeeld:
https://app.vorijk.nl/ul/issuance?registration_token=eyBwMry...
NB: de reden voor deze tussenstap waarin een registration_token
wordt gegeven aan de app, in plaats van rechtstreeks het Verifiable Credential, is om ervoor te zorgen dat het Verifiable Credential in een latere stap rechtstreeks door de App bij de Credential Issuer kan worden opgehaald, in plaats van dat het Verifiable Credential in deze stap ook nog eens door de browser heen zou gaan voordat hij bij de App terecht komt. Bovendien zou het encoderen van het Verifiable Credential in de Universal Link deze dermate groot kunnen maken dat hij door het mobiele besturingssysteem wordt afgekapt.
De App ontsleutelt de JWE met de session AES key, en valideert dat:
iss
en sub
de waardes zoals daar gespecificeerd.De App maakt een JSON object met daarin de volgende velden:
sub
: MOET de vaste waarde registration_token
hebben.iss
: MOET de vaste waarde bk-connect://app/
hebben.registration_token
: het registration_token
uit stap 8.Voorbeeld:
{
"sub": "registration_token",
"iss": "bk-connect://app/",
"registration_token": "LarRGSbmUPYtRYO6BQ4yn8"
}
Vervolgens versleutelt de App dit object met de session AES key in de vorm van een JWE (zie de Cryptografie sectie hierboven).
De App verstuurt het versleutelde registration_token
object naar de Credential Issuer, middels een HTTP POST
naar /credential
, met het session_token
uit Stap 2 in de Authorization
HTTP header.
Voorbeeld:
POST /credential HTTP/1.1
Host: credential-issuer.example.com
Authorization: tFNqe50B2nRSwo59ghKd6O
eyNaIioBb2_Jhb...x5Nz-g
NB: de reden dat de gebruiker het registration_token
eerst moet ontsleutelen in Stap 13 en dan opnieuw moet versleutelen in Stap 15, in plaats van dat de gebruiker de versleutelde JWE die hij ontvangt in stap 12 rechtstreeks in deze stap zou kunnen versturen, is om het onmogelijk te maken voor een aanvaller die binnenin de TLS tunnel zit, en die dus het verkeer tussen de App en Credential Issuer kan zien, om de versleutelde JWE in stap 12 op te vangen en die dan in deze stap te versturen in plaats van de echte gebruiker, om op die manier het Verifiable Credential te stelen.
De Credential Issuer ontsleutelt de JWE uit de HTTP POST
body van de App met de session AES key, en valideert dat:
iss
en sub
de waardes zoals daar gespecificeerd.De Credential Issuer tekent met zijn private key het Verifiable Credential, in de vorm van een JWS. Hierbij plaatst hij de root app EC public key in het app_public_key
veld van de Verifiable Credential JWS. Zie de Verifiable Credentials sectie hierboven voor alle velden en een voorbeeld.
De App kan zichzelf deregisteren bij de Credential Issuer om aan te geven dat hij geen vers Verifiable Credential meer zal opvragen middels het protocol gedocumenteerd in deze sectie.
Nadat de Credential Issuer dit protocol heeft uitgevoerd voor een zeker registration_token
, dan MOET de Credential Issuer eventuele latere aanroepingen van het /credential
HTTP endpoint in het uitgifteprotocol van bovenstaande sectie die gebruik maken van het betreffende registration_token
afbreken met een foutmelding.
Het protocol ziet er in een sequence diagram als volgt uit.
Deregistratie
protocolEr volgt een expliciete beschrijving van de protocolberichten, die refereert aan de genummerde stappen in bovenstaand sequence diagram. Alle whitespace in JSON structuren is enkel bedoeld voor de leesbaarheid van deze standaard en dient niet te worden opgenomen in implementaties.
De App en de Credential Issuer voeren het CreateSession
protocol uit. De App MOET hierbij voor de certificate_type
parameter de waarde app_manager_jwt_certificate
gebruiken. Dit protocol resulteert in een session_token
en een AES key voor later gebruik.
De App maakt een JSON object met daarin de volgende velden:
sub
: MOET de vaste waarde unregister_app
hebben.iss
: MOET de vaste waarde bk-connect://app/
hebben.registration_token
: het registration_token
van de App.Voorbeeld:
{
"sub": "unregister_app",
"iss": "bk-connect://app/",
"registration_token": "LarRGSbmUPYtRYO6BQ4yn8"
}
Vervolgens versleutelt de App bovenstaand object met de session AES key in de vorm van een JWE (zie de Cryptografie sectie hierboven).
De App verstuurt het JWE naar de Credential Issuer, middels een HTTP POST
naar /unregister_app
, waarbij het session_token
uit Stap 2 wordt meegegeven in de Authorization
HTTP header.
Voorbeeld:
POST /unregister_app HTTP/1.1
Host: credential-issuer.example.com
Authorization: tFNqe50B2nRSwo59ghKd6O
eyNaIioBb2_Jhb...x5Nz-g
De Credential Issuer ontsleutelt de JWE uit de HTTP POST
body van de App met de session AES key, en valideert dat:
iss
en sub
de waardes zoals daar gespecificeerd.De Credential Issuer verwijdert de registratie onder registration_token
, of markeert deze als zijnde inactief, zodat een eventuele latere aanroep van het /credential
HTTP endpoint in het uitgifteprotocol van voorgaande sectie die gebruik maakt van het betreffende registration_token
afbreekt met een foutmelding.
In deze variant maakt de app gebruik van het OpenID4VCI protocol voor het verkrijgen van een Verifiable Credential waarmee de app zich later kan authenticeren richting dienstverleners. Dit protocol kent twee varianten: de Authorized Code Flow en de Pre-Authorized Code Flow. Deze standaard maakt gebruik van de Authorized Code Flow. Deze flow kan gezien worden als een uitbreiding van OAuth (RFC 6749), en doet twee dingen:
NB: het OpenID4VCI protocol is in actieve ontwikkeling, en geregeld worden er nieuwe draft versies van uitgebracht. Deze sectie is gebaseerd op draft 13 van OpenID4VCI. Inmiddels bestaan er een aantal nieuwe draft versies.
Dezelfde terminologie als in de terminologie sectie, met daaraan toegevoegd:
In deze standaard ziet dit protocol er schematisch als volgt uit. Dit diagram toont in de tweede regel binnen de haakjes telkens de belangrijkste gegevens die verstuurd worden.
Dit kan gezien worden als een OAuth sessie die toegang verschaft tot het Credential endpoint onderaan, waarbij de app de rol speelt van zowel de User Agent als de OAuth Client.
In implementaties kunnen de Credential Issuer en de Authorization Server één en dezelfde server zijn die beide rollen vervult. In bovenstaand diagram worden ze apart getoond om verantwoordelijkheden te scheiden en om de gelijkenis met OAuth aan te tonen.
Refererend naar de genummerde stappen gebeurt het volgende in het protocol:
GET
op een JSON datastructuur die de issuer publiceert onder /.well-known/openid-credential-issuer
. De issuer publiceert daarin gegevens over zichzelf, onder andere de URL naar de Authorization Server die hij gebruikt. De app gebruikt vervolgens het OAuth metadata discovery mechanisme (RFC 8414) om de OAuth metadata van de Authorization Server te verkrijgen.GET
request van de browser. In de browser wordt de eindgebruiker geïdentificeerd en geauthenticeerd bijvoorbeeld middels gebruikersnaam/wachtwoord of een identificatieapp zoals DigiD. Als identificatie slaagt wordt de Authorization Response naar de app gestuurd, met daarin een authorization code. De app opent en parse't de authorization code uit de Authorization Response.c_nonce
. Het access token geeft toegang tot het Credential endpoint van de issuer, welke een OAuth 2.0 protected resource is als in RFC 6750.c_nonce
, resulterend in een proof of possession.c_nonce
aan de gebruiker gegeven is.c_nonce
. Zoja maakt hij een Verifiable Credential met daarin de public key uit het proof of possession en de attributen van de gebruiker, en stuurt deze naar de app.Elk versleuteld bericht in deze sectie MOET in de vorm van een JWE zijn (RFC 7516, zie ook de Cryptografie sectie hierboven).
In deze sectie beschrijven we het protocol op hoog niveau door keuzes te nemen in bepaalde parameters die de OpenID4VCI standaard aanbiedt.
De Credential Issuer MOET onder GET /.well-known/openid-credential-issuer
een JSON datastructuur zoals de volgende publiceren (zonder commentaar):
{
"credential_issuer": "https://credential-issuer.example.com",
"credential_endpoint": "https://credential-issuer.example.com/credential",
"credential_identifiers_supported": false,
"credential_response_encryption": {
"encryption_required": true, // (1)
"alg_values_supported" : [ "ECDH-ES" ], // (2)
"enc_values_supported" : [ "A128GCM", "A192GCM", "A256GCM" ] // (3)
},
"credential_configurations_supported": {
"nl.vorijk.vc.blauwe_knop": { // (4)
"scope": "nl.vorijk.oauth_scope.blauwe_knop.vc",
"format": "jwt",
"cryptographic_binding_methods_supported": [ "jwk" ],
"credential_signing_alg_values_supported": [ "ES256" ], // (5)
"proof_types_supported": {
"jwt": {
"proof_signing_alg_values_supported": [ "ES256" ] // (6)
}
}
}
}
}
Er volgt een korte uitleg van sommige van de parameters (voor meer informatie, zie § 11.2.3 van OpenID4VCI). Refererend naar de nummers hierboven:
encryption_required
kondigt aan dat de Credential Issuer het Verifiable Credential aan het eind van het protocol in versleutelde vorm zal geven aan de app, en dat de app daar voorafgaand bij het Credential Request daarom een public key zal moeten aanleveren waarnaar het Verifiable Credential versleuteld gaat worden.alg
JWE header parameter. Per de "Asymmetrische encryptie" sectie hierboven MOET deze array enkel ECDH-ES
bevatten.enc
JWE header parameter. Per de "Asymmetrische encryptie" sectie hierboven MOET dit A128GCM
, A192GCM
, A256GCM
zijn; het MAG ook een subset van die drie zijn als de Credential Issuer niet alledrie ondersteunt.De Credential Issuer metadata MAG volgens § 11.2.3 van OpenID4VCI een array genaamd authorization_servers
bevatten, welke URLs bevat van OAuth server(s) die de app mag gebruiken voor het versturen van de Authorization Request en Token Request. De afwezigheid daarvan betekent dat de Credential Issuer zelf fungeert als OAuth server, dat wil zeggen de Authorization en Token endpoints host. Deze moet dan ook een OAuth 2.0 metadata document publiceren onder .well-known/oauth-authorization-server
. Deze MOET minimaal als volgt zijn, maar MAG ook andere velden als gedefinieerd in RFC 8414 bevatten:
{
"issuer": "https://oauth-server.example.com",
"authorization_endpoint": "https://oauth-server.example.com/authorize",
"token_endpoint": "https://oauth-server.example.com/token",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code"],
"code_challenge_methods_supported": ["S256"]
}
De OpenID4VCI standaard biedt op een aantal plekken verschillende opties en varianten aan. Deze standaard maakt daarin de volgende keuzes.
authorization_details
parameter, als in § 5.1.1 van OpenID4VCI, aangeven welk Verifiable Credential hij geïssued wil krijgen:[{
"type": "openid_credential",
"credential_configuration_id": "nl.vorijk.vc.blauwe_knop"
}]
Het credential_configuration_id
refereert naar de corresponderende entry in de credential_configurations_supported
object in de Credential Issuer metadata. Dit object MOET ook worden opgenomen in het Token Response.c_nonce
parameter worden opgenomen, die een willekeurige string bevat.
Deze moet door de app ondertekend worden met de private key waarvan de public key in het Verifiable Credential moet komen, resulterend in een Proof of Possession.proof
parameter, en een credential_response_encryption
object waarin de app een ephemeral ECDH-ES
public key zet waarnaar de Credential Issuer het Verifiable Credential moet versleutelen.credential_response_encryption
object in het Credential Request.OpenID4VCI maakt het gebruik van PAR
(Pushed Authorization Requests,
RFC 9126)
optioneel voor het versturen van het Authorization Request.
In deze uitbreiding stuurt de OAuth Client het Authorization Request niet via de User Agent van de eindgebruiker naar de Authorization Server, maar stuurt de Client het Authorization Request rechtstreeks in een HTTP POST
naar de Authorization Server.
Deze antwoordt vervolgens met een referentie naar de sessie genaamd de request_uri
, die de eindgebruiker in de URL naar het Authorization Endpoint stopt wanneer hij daar naartoe navigeert.
De Authorization Server herkent de request_uri
en weet daardoor om welk Authorization Request het gaat.
Dit biedt in traditionele OAuth-gebaseerde protocollen drie voordelen:
Deze uitbreiding MAG NIET gebruikt worden in deze standaard. De reden hiervoor is dat het wel complexiteit en runtime kosten met zich meebrengt, terwijl beide bovenstaande voordelen die het biedt in traditionele OAuth in dit protocol niet van toepassing zijn:
De keuzes beschreven in bovenstaande sectie resulteren bij elkaar in een protocol die we in deze sectie verder uitwerken en expliciet maken.
Het protocol ziet er in een sequence diagram als volgt uit. In dit diagram wordt de rol van de Authorization Server en de Credential Issuer gespeeld door dezelfde partij, maar we voegen DigiD en BRP (Basisregistratie Personen) als partijen toe. Verder nemen we aan dat de Authorization, Token en Credential endpoints /authorize
, /token
en /credential
zijn respectievelijk. Net als in het vorige diagram tonen we telkens in de tweede regel binnen haakjes de belangrijkste gegevens die verstuurd worden. (Deze lijst is meestal niet compleet, dat wil zeggen dat er vaak meer wordt verstuurd dan wat in deze tweede regel staat; we tonen hier alleen de belangrijkste gegegvens.)
Er volgt een expliciete beschrijving van de protocolberichten, die refereert aan de genummerde stappen in bovenstaand sequence diagram. Alle whitespace in JSON structuren en HTTP requests is enkel bedoeld voor de leesbaarheid van deze standaard en dient niet te worden opgenomen in implementaties.
In onderstaand protocol wordt een aantal keer Base64-encodering gebruikt. Dit moet altijd de URL-safe variant zijn zonder padding.
De App gebruikt de OpenID4VCI en OAuth metadata discovery mechanismes door een GET
uit te voeren op /.well-known/openid-credential-issuer
en /.well-known/oauth-authorization-server
, respectievelijk. De server(s) antwoorden met JSON structuren zoals in de Credential Issuer metadata en OAuth metadata secties hierboven. De App parseert de JSON en ontdekt op die manier de URLs van de Authorization, Token en Credential endpoints.
In het vervolg van dit document nemen we aan dat deze endpoints als volgt zijn:
https://issuer.example.com/authorize
https://issuer.example.com/token
https://issuer.example.com/credential
De App genereert een PKCE verifier (genaamd code_verifier
in onderstaande HTTP berichten) door 32 willekeurige bytes te genereren met een CSPRNG en deze te Base64url-encoderen, resulterend in een PKCE verifier van 43 karakters. De App genereert vervolgens de bijbehorende PKCE challenge middels de volgende pseudocode:
code_challenge := BASE64URL-ENCODE(SHA256(code_verifier))
Zie ook § 4.1 en 4.2 van RFC 7636.
De App navigeert in de browser naar het Authorization endpoint van de Credential Issuer, met URL-encoded in de URL het Authorization Request als in § 5 van OpenID4VCI en § 4.1.1 van OAuth, met de volgende parameters:
response_type
: MOET de waarde code
hebben (refererend naar de authorization code grant type van § 4.1 van OAuth).client_id
: een identifier van de App die een vaste waarde MOET hebben over alle App instanties, bijvoorbeeld nl.vorijk.app
.code_challenge
: de PKCE challenge gegenereerd zoals hierboven.code_challenge_method
: MOET de waarde S256
hebben (per § 4.2 van de PKCE RFC).scope
: MOET de waarde nl.vorijk.oauth_scope.blauwe_knop.vc
hebben, welke refereert naar de corresponderende scope
uit de Credential Issuer Metadata.redirect_uri
: dit MOET een Universal Link (UL) zijn die de App opent. Nadat authenticatie en identificatie van de eindgebruiker is gelukt zal de gebruiker in Stap 10 naar deze link worden geredirect door de Credential Issuer, waardoor de App opent.state
: MAG aanwezig zijn en de waarde hiervan mag vrij bepaald worden door de App. Als state
gebruikt wordt dan ontvangt de App dezelfde state
ook terug in het Authorization Response, zie stap 10. De App kan deze parameter gebruiken voor het bijhouden van bepaalde state, bijvoorbeeld voor het tracken van verschillende sessies.Voorbeeld (met newlines voor leesbaarheid):
https://issuer.example.com/authorize?
response_type=code
&client_id=nl.vorijk.app
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&scope=nl.vorijk.oauth_scope.blauwe_knop.vc
&redirect_uri=https%3A%2F%2Fapp.vorijk.nl%2Ful%2Fissuance
&state=xyz
De eindgebruiker wordt geïndentificeerd middels DigiD want resulteert in diens BSN.
De Credential Issuer genereert met een CSPRNG een nieuwe authorization code, die een willekeurige string gebruik makend van de URL-safe Base64 karakterset MOET zijn, van minimaal 22 karakters (zodat hij minimaal 128 bit entropie heeft).
De Credential Issuer slaat de PKCE challenge en het BSN van de gebruiker op onder het authorization code voor later gebruik.
De Credential Issuer roept de redirect_uri
uit Stap 4 met daarin de Universal Link van de App aan, bijvoorbeeld door de Universal Link te plaatsen in een link op zijn website die de eindgebruiker moet gebruiken, of door een HTTP redirect naar de Universal Link. De Credential Issuer levert de volgende parameters URL-encoded mee in de Universal Link:
code
: de authorization code.state
: alleen aanwezig als de App in het Authorization Request (stap 4) een state
parameter heeft meegegeven, in dat geval met dezelfde waarde als in het Authorization Request.De app ontvangt de Universal Link en parseert daaruit de authorization code.
Voorbeeld:
https://app.vorijk.nl/ul/issuance?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
De App genereert een nieuw ephemeral EC keypair, en produceert daarmee een DPoP Proof of Possession per § 4.2 van RFC 9449, welke een JWS is als volgt:
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
"y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
"crv": "P-256"
}
}
.
{
"jti": "-BwC3ESc6acc2lTc",
"htm": "POST",
"htu": "https://issuer.example.com/token",
"iat": 1562262616
}
In deze JWS zijn de velden als volgt:
jwk
: de zojuist gegenereerde public key in JWK formaat (RFC 7517);jti
: De Base64url-encoding van minimaal 12 bytes aan willekeurige data gegenereerd met een CSPRNG;htm
: de HTTP methode waarbij deze DPoP in de volgende stap gebruikt wordt;htu
: de URL waarbij deze DPoP in de volgende stap gebruikt wordt;iat
: de Unix timestamp van het moment van creatie van deze JWS.De App doet een Token Request als in § 6 van OpenID4VCI en § 4.1.3 van OAuth bij de Credential Issuer, gebruik makend van de authorization code die hij eerder heeft ontvangen. De App stuurt hierbij ook bovenstaand DPoP Proof of Possession mee, per § 4.1 van RFC 9449.
De App verstuurt het Token Request URL-encoded in de HTTP POST
body, met de volgende parameters:
grant_type
: MOET de waarde authorization_code
hebben.client_id
en redirect_uri
: MOETEN dezelfde waardes als in het Authorization Request hebben.code
: de authorization code ontvangen in stap 10.code_verifier
: de PKCE verifier gegenereerd in stap 3.Voorbeeld (met newlines voor leesbaarheid):
POST /token HTTP/1.1
Host: issuer.example.com
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE
QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj
oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia
WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg
4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg
grant_type=authorization_code
&client_id=nl.vorijk.app
&redirect_uri=https%3A%2F%2Fapp.vorijk.nl%2Ful%2Fissuance
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
De Credential Issuer haalt middels de authorization code de PKCE challenge en het BSN van de gebruiker op.
De Credential Issuer verifieert de PKCE verifier per § 4.6 van RFC 7636, door vast te stellen dat het volgende geldt:
code_challenge == BASE64URL-ENCODE(SHA256(code_verifier))
Als deze gelijkheid niet geldt, dan MOET de Credential Issuer afbreken met de foutmelding invalid_grant
als per § 5.2 van OAuth.
De Credential Issuer verifieert de DPoP Proof of Possession uit de DPoP HTTP header middels de instructies in § 4.3 van RFC 9449.
Als de DPoP JWS niet geldig is, dan MOET de Credential Issuer afbreken met de foutmelding invalid_dpop_proof
als per § 5.2 van OAuth.
Als de DPoP JWS wel geldig is, dan parseert de Credential Issuer de DPoP public key in de DPoP JWS voor later gebruik.
De Credential Issuer genereert met een CSPRNG een nieuw access_token
en c_nonce
, die beiden willekeurige strings gebruik makend van de URL-safe Base64 karakterset MOETEN zijn, van minimaal 22 karakters (zodat ze minimaal 128 bit entropie hebben).
De Credential Issuer slaat het BSN van de gebruiker, de c_nonce
en de DPoP public key op onder het access_token
voor later gebruik.
De Credential Issuer antwoordt op het HTTP POST
request van de App uit stap 12 met een JSON object met daarin de volgende parameters:
access_token
: het access token gegenereerd in de vorige stap, die toegang geeft tot het Credential Endpoint (zie Stap 25).c_nonce
: de nonce gegenereerd in de vorige stap.token_type
: MOET de waarde DPoP
hebben per § 5 van RFC 9449, wat aangeeft aan de App dat DPoP gebruikt MOET worden bij het gebruik van het access token (dus bij het opvragen van het Verifiable Credential).expires_in
: MAG aanwezig zijn en MOET in dat geval in seconden aangeven hoe lang het access token geldig is.Voorbeeld:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "JhbGciOiJSUzI1NiIsInR5",
"c_nonce": "LarRGSbmUPYtRYO6BQ4yn8",
"token_type": "DPoP",
"expires_in": 60,
}
Als er niet al eerder een hardware-bound ECDSA keypair voor gebruik in het Verifiable Credential was aangemaakt doet de app dit nu.
De App genereert een Proof of Possession van de public key gegenereerd in de vorige stap als per § 7.2.1.1 van OpenID4VCI, in de vorm van een JWS met daarin de volgende velden:
typ
: MOET de waarde openid4vci-proof+jwt
zijn.alg
: MOET de waarde ES256
zijn.jwk
: de ECDSA public key in JWK formaat (RFC 7517).aud
: MOET de Credential Issuer Identifier zijn, dat wil zeggen de URL die /.well-known/openid-credential-issuer
host.iat
: Unix timestamp van het moment van creatie van deze JWS.nonce
: MOET de waarde van de c_nonce
hebben die de App ontving in Stap 20.Voorbeeld:
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "nUWAoAv3XZith8E7i19OdaxOLYFOwM-Z2EuM02TirT4",
"y": "HskHU8BjUi1U9Xqi7Swmj8gwAK_0xkcDjEW_71SosEY"
}
}
.
{
"aud": "https://issuer.example.com",
"iat": 1701960444,
"nonce": "LarRGSbmUPYtRYO6BQ4yn8"
}
De App genereert een nieuw ephemeral EC keypair voor encryptie. Deze wordt later door de Credential Issuer gebruikt in Stap 34 om het Verifiable Credential te versleutelen voordat hij naar de App verstuurd wordt.
De App genereert een nieuw DPoP JWS zoals in stap 11, middels hetzelfde EC keypair, met een vers willekeurig jti
veld, en dit keer inclusief een ath
veld in de JWS body, met als waarde de Base64url-encoding van de SHA256 hash van het access_token
, dus schematisch BASE64URL(SHA256(access_token))
.
Voorbeeld:
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"x":" l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
"y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
"crv": "P-256"
}
}
.
{
"jti": "e1j3V_bKic8-LAEB",
"htm": "POST",
"htu": "https://issuer.example.org/credential",
"iat": 1562262618,
"ath": "LnCUSTkzMbG3p5Bw35U4EAOl-pXwcgOdhXxgK6CFYY4"
}
De App stuurt een Credential Request naar het Credential endpoint van de Credential Issuer als in § 7 van OpenID4VCI, middels een HTTP POST
met de volgende HTTP headers:
Authorization
: het access token met DPoP
als authentication scheme.DPoP
: de DPoP JWS uit de vorige stap.In de HTTP body verstuurt de App JSON-encoded een object met de volgende parameters:
proof
: een object met daarin:proof_type
: MOET de waarde jwt
hebben.jwt
: de JWS uit stap 22.credential_response_encryption
: een object dat aangeeft hoe de Credential Issuer het Verifiable Credential moet versleutelen voordat het naar de App gestuurd wordt, middels de volgende velden:jwk
: de public key gegenereerd in Stap 23.alg
: het asymmetrische encryptie-algoritme die de app verwacht. MOET de waarde ECDH-ES
hebben.enc
: het symmetrische encryptie-algoritme die de app verwacht. MOET de waarde A128GCM
, A192GCM
, of A256GCM
hebben.Voorbeeld (newlines en whitespace enkel voor leesbaarheid):
POST /credential HTTP/1.1
Host: issuer.example.com
Content-Type: application/json
Authorization: DPoP JhbGciOiJSUzI1NiIsInR5
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE
QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIj
oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0Z
WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNF
c05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyJ9.2oW9RP35yRqzhrtNP86L-Ey71E
OptxRimPPToA1plemAgR6pxHF8y6-yqyVnmcw6Fy1dqd-jfxSYoMxhAJpLjA
{
"proof": {
"proof_type": "jwt",
"jwt":
"eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2IiwiandrI
jp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiblVXQW9BdjNYWml0aDhFN2k
xOU9kYXhPTFlGT3dNLVoyRXVNMDJUaXJUNCIsInkiOiJIc2tIVThCalVpMVU5WHFpN
1N3bWo4Z3dBS18weGtjRGpFV183MVNvc0VZIn19.eyJhdWQiOiJodHRwczovL2NyZW
RlbnRpYWwtaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNzAxOTYwNDQ0LCJub25j
ZSI6IkxhclJHU2JtVVBZdFJZTzZCUTR5bjgifQ.-a3EDsxClUB4O3LeDD5DVGEnNMT
01FCQW4P6-2-BNBqc_Zxf0Qw4CWayLEpqkAomlkLb9zioZoipdP-jvh1WlA"
},
"credential_response_encryption": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "gUOuh9dfi-44HT-2YL5HdEZ9opP_ItHrpp28JtQyaOY",
"y": "0UOvHfidVyKFN4gDz9bIstU0o0qWCaZrRaVhplgkm58"
},
"alg": "ECDH-ES",
"enc": "A128GCM"
}
}
De Credential Issuer haalt middels het access token het BSN van de gebruiker, de c_nonce
en de DPoP public key op.
De Credential Issuer verifieert de DPoP JWS uit de DPoP HTTP header net als in stap 16, dit keer met de volgende extra checks:
ath
veld hebben met daarin de Base64url-encoding van de SHA256 hash van het access token;De Credential Issuer verifieert de Proof of Possession in het jwt
veld in het proof
object van de Credential Response als volgt.
typ
veld in de JWS header de waarde openid4vci-proof+jwt
bevat.alg
veld in de JWS header de waarde ES256
bevat.jwk
veld in de JWS header een object bevat die een P-256 public key is geëncodeerd als JWK, en dat dit object niet een d
veld (een private key) in zich heeft.aud
veld in de JWS body de Credential Issuer Identifier bevat (de URL die /.well-known/openid-credential-issuer
host).iat
veld in de JWS body niet ouder is dan een bepaalde grens, bijvoorbeeld 60 seconden.nonce
in de JWS body gelijk is aan de c_nonce
die in Stap 20 aan de App is verstrekt.Middels het BSN worden de gegevens van de eindgebruiker die geïssued moeten worden als attributen in het Verifiable Credential opgehaald uit de BRP.
De Credential Issuer tekent met zijn private key het Verifiable Credential, in de vorm van een JWS. Hierbij plaatst hij de public key in het jwk
veld van het Proof of Possession (zie Stap 29) in het cnf
veld van de Verifiable Credential JWS. Zie de Verifiable Credentials sectie hierboven voor alle velden en een voorbeeld.
De Credential Issuer maakt het Credential Response, een JSON object met daarin enkel een credential
veld met als waarde de JWS uit de vorige stap:
{
"credential": "eyBRa..."
}
De Credential Issuer versleutelt dit object in de vorm van een JWE naar de public key in het jwk
veld in het credential_response_encryption
object in het Credential Request uit Stap 25.
De Credential Issuer verstuurt bovenstaand JWE naar de App.
De App controleert dat het Credential Response versleuteld in de vorm van een JWE is, en breekt af zoniet. Hij ontsleutelt de JWE met de private key gegenereerd in Stap 23, parseert de inhoud van de JWE als JSON object, en vindt daarin het Verifiable Credential in het credential
veld.
De App verifieert het Verifiable Credential tegen de public key van de Credential Issuer, eventueel gebruik makend van het kid
veld in de JWS header om de juiste public key te identificeren.
CreateSession
protocol (beknopte weergave)
CreateSession
protocol
Issuance
protocol
Deregistratie
protocol
Naast onderdelen die als niet normatief gemarkeerd zijn, zijn ook alle diagrammen, voorbeelden, en noten in dit document niet normatief. Verder is alles in dit document normatief.
De trefwoorden MAG, MOET, MOETEN en MOGEN in dit document moeten worden geïnterpreteerd als in BCP 14 [RFC2119] [RFC8174] als, en alleen als deze in hoofdletters zijn weergegeven, zoals hier getoond.