light-mode-image
Learn
VICAL

Overview

Introduction

A VICAL (Verified Issuer Certificate Authority List) is a mechanism defined in the ISO/IEC 18013-5 standard to support establishing trust in digital ecosystems where relying parties need to verify mDocs issued by numerous different issuers.

For example, consider the case of relying parties that need to verify Mobile Driver's Licenses (mDLs). Different states and/or provinces can issue their own mDLs, each signed by a Document Signer Certificate (DSC) that is itself signed by the state/province unique Issuing Authority Certificate Authority (IACA).

If you’re someone who needs to verify these mDLs (like a police officer or a business verifying identities), you would have to individually assess and trust each state/province IACA. This can become complicated, especially as the number of issuers grows, because you'd have to create and manage a lot of different trust relationships.

A VICAL solves this by collecting and validating IACAs from different issuing authorities, and then cryptographically signing them into a single list. Each IACA in the VICAL is associated with:

  • An issuing authority that can use this IACA as the root certificate when signing mDocs.
  • Credential types for which this IACA can serve as a root certificate.

When a relying party trusts a VICAL they can trust any presented mDoc, given that:

  • The credential was issued by an issuing authority that is included in the VICAL.
  • The root certificate (IACA) of the chain of certificates used to sign the credential matches the IACA that is associated with this issuing authority in the VICAL.
  • The credential type matches one of the credential types associated with this IACA in the VICAL.

This mechanism enables relying parties to verify mDocs (such as mDLs) from any issuing authority included in the VICAL without managing multiple separate trust relationships with each issuing authority. This can greatly simplify the process of verifying mDocs in complex ecosystems, such as verifying mDLs across various jurisdictions.

A VICAL can be considered a standardized approach to creating a Digital Trust Service (DTS).

VICAL roles

  • VICAL Provider: Operates the VICAL and provides it as a service to different ecosystem participants. The VICAL provider collects and validates information from relevant issuers, compiles it into a standardized VICAL format and distributes it to relying parties.
  • Issuers: Issue mDocs to holders while attesting the validity of claims included in these credentials.
  • Relying parties: Consume the VICAL and use issuers' information to verify presented mDocs.

VICAL components

  • VICAL metadata: This includes general information about the VICAL itself:
    • Version.
    • Provider.
    • Issuance date.
    • Unique identifier.
    • Next update.
  • VICAL records: Each record includes the following information that can be used by relying parties to establish trust in mDocs and the IACAs that are used as their root certificate:
    • A trusted IACA that can be used as a root certificate to sign mDocs.
    • Issuing authority that can issue mDocs with this IACA.
    • Credential types that this IACA can serve as a root certificate for, listed in the docType array. A single VICAL record can authorize an IACA for one or more docTypes.
    • Additional IACA information:
      • Validity period.
      • Unique identifier.
      • State/Province.
      • Country.
      • Public key info (algorithm, curve and value).
      • Signature info (algorithm and value).
      • Fingerprints.
      • Extensions used.

How it works

VICAL trust model

  1. The VICAL provider establishes its own root certificate with an associated Public Key Infrastructure (PKI) chain of certificates, based on the chain of trust model.

  2. The VICAL provider collects and validates IACAs from different issuing authorities. Each of these IACAs are vetted by the VICAL provider and trusted to issue mDocs of specific credential types.

  3. The VICAL provider uses their chain of trust end-entity certificate to sign the valid IACAs into a single list.

  4. Each issuing authority uses their own IACA and associated PKI chain of certificates to sign mDocs.

  5. Relying parties can consume the VICAL in one of two ways:

    • Download the VICAL directly from the provider’s website.
    • Retrieve the VICAL via an endpoint exposed by the provider as an API.
  6. When a relying party attempts to verify an mDoc, they validate its signature and referenced PKI certificate chain against the VICAL to ensure:

    • This issuing authority can use this IACA as a root certificate when signing mDocs.
    • This credential type can use this IACA as a root certificate.
  7. Upon successful validation, the relying party can verify the presented mDoc without having to create and manage a trust relationship directly with its issuing authority directly.

Want to see a live VICAL from the inside? Check out the MATTR Labs VICAL Viewer that can render existing VICAL .cbor files into a human-readable format.

Consuming a VICAL as a relying party

Once a VICAL provider has published a VICAL, relying parties need to retrieve it, verify its authenticity, and load the trusted IACAs into their verification solution. The steps below describe how to do that against a VICAL published by a MATTR-hosted ecosystem.

Retrieve the VICAL provider's root CA certificate

Call the Retrieve all public DTS root CA certificates endpoint to obtain the root certificate(s) used by the VICAL provider as the anchor of its signing chain. The endpoint is public and does not require authentication.

curl https://your-tenant.vii.au01.mattr.global/v1/ecosystems/public/certificates/ca

The response includes one or more PEM-encoded root certificates:

{
  "rootCertificates": [
    {
      "certificate": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAL5...\n-----END CERTIFICATE-----",
      "notBefore": "2025-10-22T00:00:00Z",
      "notAfter": "2030-10-22T00:00:00Z",
      "fingerprint": "f6cad6e579d70b3973efa60624af731a580d1a11a7579e70f2f10f059dc86172",
      "commonName": "Example VICAL Provider Root CA"
    }
  ]
}

Persist these certificates as your trust anchor for the VICAL provider. They only need to be refreshed when the provider rotates its root.

Retrieve the latest VICAL

Call the Retrieve latest VICAL endpoint to download the current VICAL. The response is a binary CBOR file (application/cbor) encoded as a COSE_Sign1 structure, as defined in ISO/IEC 18013-5 Annex C.

curl -o vical-latest.cbor \
  https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/latest

Relying parties should poll this endpoint on a schedule that matches the VICAL provider's publishing cadence so they always operate against the latest set of trusted issuers.

Validate the VICAL signature and certificate chain

The VICAL is signed by a VICAL Signer certificate, which itself chains back to the VICAL provider's root CA retrieved in step 1. Before trusting any data in the VICAL, perform the following checks:

  1. Extract the VICAL Signer certificate from the COSE_Sign1 unprotected header (x5chain, COSE label 33).
  2. Verify the COSE_Sign1 signature using the public key from that signer certificate and the signature algorithm declared in the protected header (for example, ES256 / COSE algorithm -7).
  3. Build the certificate chain from the signer certificate up to the root CA from step 1, and validate each link: signature, validity period, key usage, and revocation status (CRL).
  4. Confirm the root of the chain matches one of the trust-anchor certificates you persisted in step 1.

If any of these checks fail, reject the VICAL and continue using the previously trusted version.

Decode the VICAL payload

Once the signature has been verified, decode the COSE_Sign1 payload to access the VICAL data. The decoded structure looks like this (values are illustrative):

{
  "protectedHeader": {
    "1": -7
  },
  "unprotectedHeader": {
    "33": {
      "type": "X.509 Certificate",
      "subject": "C=AU, CN=example-dts.vii.au01.mattr.global VICAL Signer, O=Example Provider",
      "issuer": "C=AU, CN=Example Provider Root CA, O=Example Provider",
      "validFrom": "2025-11-11T02:26:13.000Z",
      "validTo": "2029-02-10T02:26:13.000Z",
      "fingerprint": "68:58:45:41:5A:AF:10:3A:85:01:78:E2:40:C9:59:51:AB:9B:10:13",
      "pem": "-----BEGIN CERTIFICATE-----\nMIICnzCCAkagAwIBAgIK...\n-----END CERTIFICATE-----"
    }
  },
  "payload": {
    "version": "1.0",
    "vicalProvider": "Example Provider",
    "date": "2025-11-15T20:59:01.000Z",
    "vicalIssueID": 41,
    "certificateInfos": [
      {
        "certificate": {
          "type": "X.509 Certificate",
          "subject": "CN=Example IACA, O=Example DLD, ST=US-XX, C=US",
          "issuer": "CN=Example IACA, O=Example DLD, ST=US-XX, C=US",
          "validFrom": "2024-01-01T00:00:00.000Z",
          "validTo": "2034-01-01T00:00:00.000Z",
          "pem": "-----BEGIN CERTIFICATE-----\nMIICeDCCAh+gAwIBAgIQ...\n-----END CERTIFICATE-----",
          "isCertificateAuthority": true
        },
        "serialNumber": "5A5BB04A119A35796D2AD477D93A1ACC",
        "ski": "3C:4C:6C:DF:E7:82:34:2E:E1:14:E6:CE:AD:12:0A:39:FD:08:34:6B",
        "issuingCountry": "US",
        "stateOrProvinceName": "US-XX",
        "notBefore": "2024-01-01T00:00:00.000Z",
        "notAfter": "2034-01-01T00:00:00.000Z",
        "docType": ["org.iso.18013.5.1.mDL"],
        "issuingAuthority": "CN=Example IACA, O=Example DLD, ST=US-XX, C=US"
      }
    ]
  }
}

The payload.certificateInfos array contains one entry per trusted IACA. Each entry includes the PEM-encoded IACA, its validity period, the issuing authority's identifiers, and the array of docType values it is authorized to sign.

Load the trusted IACAs into your verification solution

Iterate over payload.certificateInfos and extract each certificate. In raw CBOR, certificate is the DER-encoded X.509 IACA byte string (the JSON above is a rendered view). These IACAs are the trust anchors you use when validating presented mDocs:

import { decode } from "cbor-x";

const vicalBytes = await fetch(
  "https://your-tenant.vii.au01.mattr.global/v1/ecosystems/{ecosystemId}/vicals/public/latest"
).then((r) => r.arrayBuffer());

// COSE_Sign1 structure: [protected, unprotected, payload, signature]
const coseSign1 = decode(new Uint8Array(vicalBytes));
const payload = decode(coseSign1[2]);

const trustAnchors = payload.certificateInfos.map((info) => ({
  certificateDer: info.certificate,
  issuingAuthority: info.issuingAuthority,
  docTypes: info.docType,
  validFrom: info.notBefore,
  validTo: info.notAfter,
}));

When verifying a presented mDoc:

  1. Look up the IACA referenced by the mDoc's Document Signer Certificate against your loaded trust anchors.
  2. Confirm the mDoc's docType is listed under that IACA's authorized docType array. If it is not, reject the credential even when the signature chain is otherwise valid.
  3. Validate the full PKI chain from the mDoc's signer up to the IACA as the trust anchor.

This mechanism lets you verify mDocs from any issuer represented in the VICAL without maintaining individual trust relationships with each issuing authority.

How would you rate this page?

Last updated on

On this page