#How to Issue Mobile Driver’s Licenses (ISO/IEC 18013-5 mDLs) via OID4VCI with walt.id
TL;DR
Use the walt.id Issuer API to onboard ISO/IEC 18013-5 trust anchors and issue mobile driver’s licenses via OID4VCI, from PKI setup to distributing credential offers.
What you’ll learn
Generate IACA root certificates and keys that anchor trust for mDL issuance.
Provision document signer certificates with EKU and CRL metadata required by ISO/IEC 18013-5.
Populate mandatory mDL claims and invoke the walt.id Issuer API’s OID4VCI endpoint to sign the mdoc.
Construct and distribute credential offer URLs so compliant wallets can request the issued credential.
OID4VCI – protocol for issuing verifiable credentials to wallets.
This guide provides a comprehensive walkthrough for issuing an mDL credential based on
the ISO/IEC 18103-5 standard using the walt.id Issuer API.
The issuance process will utilize the
OID4VCI protocol.
The mobile driver's license (mDL) ecosystem builds on established PKI principles that employ X.509 digital
certificates, while introducing domain-specific roles and data models. Its two main components are:
IACA — Issuing Authority Certification Authority: This is an authority that serves as the root of trust
vouching for authorized credential issuers and is represented by a self-signed X.509 digital certificate that is
published to verifiers.
DS — Document Signer (Credential Issuer): This is the entity that issues and cryptographically signs mDL
credential(s). It holds a private key and an X.509 digital certificate issued by an IACA.
Typically, DS certificates are short-lived and are rotated periodically.
The mDL Claims: The mDL is a verifiable credential that contains claims about the subject (holder), such as
name, birthdate and driving privileges. The ISO/IEC 18103-5 standard document
defines a set of mandatory fields (e.g., family_name, birth_date) that must be included in every mDL, as well
as a set of optional fields (e.g., age_over_NN attestations, height, weight, sex).
Note: mDLs are encoded in CBOR, which is a binary format and is shown in JSON representation above for readability
purposes. Claims values that are encoded as JSON integer arrays, e.g., the portrait claim's value, represent byte
arrays.
In this section, we'll walk through the steps required to successfully issue an mDL by generating signing keys (for
the IACA & the DS) which we store ourselves. For production
environments, we recommend using an external KMS provider for key management due to the enhanced security.
Learn more about the different types of keys and the storage options here.
Note: At the moment, you can only use secp256r1 keys to onboard IACAs and DSs and to issue mDLs.
Every mobile driver’s license ecosystem starts with a trusted root certificate of an IACA. The IACA serves as the
cryptographic authority that issues DS certificates that are deemed trustworthy.
🔧 What this step does
Create a self-signed X.509 root certificate (CA=true).
Encode in it identity info (e.g., country, organization) for the IACA.
Include optional extensions (e.g., IACA alternative names, CRL distribution URIs).
email: String - RFC822 name for contacting the IACA.
Example Response
The IACA onboarding endpoint returns an object with:
iacaKey – generated signing key of the IACA in JWK format.
certificatePEM – PEM-encoded, JSON-stringified self-signed X.509 certificate of the IACA.
certificateData – object containing the data that is encoded in the generated certificate (for convenience – useful for the DS onboarding endpoint below).
A local (jwk) secp256r1 key is automatically created.
Validity defaults to 15 years, unless otherwise specified.
As we've used the local (jwk) key type, it's important to note that we need to save the returned values ourselves
for future reference. The API doesn't save any information about created keys.
Remember to save the value of the IACA's PEM-encoded X.509 certificate (certificatePEM field in the response) as
it is required for verification of
mDLs.
Document signers are the entities responsible for signing mDL credentials. These entities are identified by an X.509
digital certificate that is signed by an IACA and contains, among others, a special purpose extension that marks it
as valid for mDL issuance.
🔧 What this step does
Issue a DS certificate signed by the input IACA private key.
Embed an Extended Key Usage (EKU) value marking it as suitable for mDL signing.
Include CRL distribution info and various other optional fields.
A local (jwk) secp256r1 key is automatically created.
Validity defaults to 457 days, unless otherwise specified.
As we've used the local (jwk) key type, it's important to note that we need to save the returned values ourselves
for future reference. The API doesn't save any information about created keys.
Remember to save the value of the DS PEM-encoded X.509 certificate (certificatePEM field in the response) as
it is required for the next (issuance) step.
Use the document signer's key (obtained previously).
Utilize the OID4VCI protocol to facilitate the issuance of the mDL from the document signer (issuer) to the holder.
Generate a credential offer URL that can be accepted by any compliant wallet to receive the credential.
The credential offer URL specifies the credentials to be issued. This includes details such as
the URL of the issuer and information about the credential's format and type.
statusCallbackUri: URL - Receive updates on the created issuance process, e.g. when a credential was successfully
claimed. The parameter expects a URL which can accept a JSON POST request. The URL can also hold a $id, which will
be
replaced by the issuance session id. For example: https://myurl.com/$id, https://myurl.com
or https://myurl.com/test/$id
Expand To Learn More
Body
The data send to the provided URL will contain a JSON body:
id : String - the issuance session id
type: String - the event type
data: JsonObject - the data for the event
Event Types
Possible events (event types) and their data are:
resolved_credential_offer with the credential offer as JSON (in our Web Wallet: called when the issuance offer is
entered into the wallet, but not processing / accepted yet)
requested_token with the issuance request for the token as json object (called for the token required to receive the
credentials)
Credential issuance (called for every credential that's issued (= requested from wallet))
jwt_issue with jwt being the issued jwt
sdjwt_issue with sdjwt being the issued sdjwt
batch_jwt_issue with jwt being the issued jwt
batch_sdjwt_issue with sdjwt being the issued sdjwt
generated_mdoc with mdoc being the CBOR (HEX) of the signed mdoc
To allow for secure business logic flows, if a callback URL is set, and it cannot be reached, the issuance will not
commence further (after that point). If no callback URL is set, the issuance logic does not change in any way.
issuerKey: JSON - A JWK or reference object to a key stored in an external KMS to sign the mDL with.
Supported algorithms: secp256r1. JWK Format:{"type": "jwk", "jwk": Here JSON Web Key Object}. Can be provided as String Or JWK object to "
issuerKey". KMS Key: Please refer to the Key Management Section and the KMS you want to
use for more details on the structure
of the reference object.
mdocData: JSON - Claims to be added to the mDL. In the provided example above, all mandatory claims are included.
x5Chain: JSON Array - Must contain a single entry that contains the PEM-encoded, JSON stringified X.509
certificate of the DS (as output by the respective onboarding endpoint).
Example Response
The issuer endpoint will respond with Credential Offer URL.