Relying-Party Registration and the Trust List: The Plumbing Behind EUDI Wallet Acceptance

Most teams treat EUDI Wallet acceptance as an API integration they will switch on nearer the deadline. It is not. Before you can ask a wallet for a single attribute, you have to be a registered relying party, and every counterparty you deal with has to be verifiable against a trust list you did not build and do not control.

The European Digital Identity framework, set out in Regulation (EU) 2024/1183 amending the original eIDAS Regulation, is usually discussed at the level the vendors like: the citizen experience, the QR code, the age check. The part that actually determines whether your integration works is underneath all of that, and it is unglamorous. It is registration, key management and trust verification. Get those wrong and the polished front end never gets a valid response. This is a reading of that plumbing for the people who have to stand it up.

You cannot ask until you are registered

The framework draws a hard line: a relying party may only request the personal data it has declared, and it declares that by registering. Registration is not a courtesy. It is the precondition for the wallet to answer you at all. A wallet that receives a request from an entity it cannot verify as registered, for an attribute that entity is not registered to request, is designed to refuse or to warn the user prominently. There is no informal path around this.

Free · 4 minutes

Would you survive contact with a determined attacker — or an auditor?

Fourteen questions on access, patching, detection, and recovery — the basics that prevent most real incidents, and the ones most often assumed rather than verified. Banded finding on screen, full sheet by email.

The mechanics sit in Commission Implementing Regulation (EU) 2025/848 of 6 May 2025, which lays down the rules for registering wallet-relying parties. Each Member State has to establish and maintain a national register of relying parties established in its territory. You register where you are established, not once for the whole Union, and the register captures a specific set of facts about you: your official and trading names, your identifiers (LEI, EORI, VAT or national registration number as applicable), your address and website, contact and helpdesk details, the service you are providing, the attributes you intend to request for each intended use, and your entitlement classification. Whether you are a public-sector body is recorded, as are any intermediary relationships. The register then publishes this in two forms: human-readable, and machine-readable for automated processing, served over a single API with the data electronically signed or sealed. Records are retained for years, not months.

The consequence for your architecture is that your declared attribute set is a published, signed, auditable object. If your product asks for more than you registered for, that discrepancy is visible to the wallet, to the user, and to a supervisor after the fact. The registration is not paperwork you file and forget; it is a live constraint on what your software is allowed to do. Treat it as configuration that has to stay in lockstep with the code, because it is.

The certificates you did not know you needed

Registration produces cryptographic material, and this is where the timeline surprises people. Under Article 8 of the Implementing Regulation, Member States may authorise certificate authorities to issue registration certificates to relying parties on their register. Alongside those, the technical framework defines relying-party access certificates, governed by their own ETSI access-certificate policy. Between them they bind your registered identity and your permitted data requests to keys your software holds and uses at presentation time. The wallet checks the certificate your service presents, confirms the issuing authority is trusted, and confirms that what you are asking for matches what the certificate says you may ask for.

That means you are now running a small but real PKI dependency. You have private keys to generate, store in something that deserves the name of a key store, rotate on a schedule, and revoke when a device or environment is compromised. You have certificates with validity periods that will expire in the middle of a busy quarter unless someone owns the renewal calendar. None of this is exotic, but none of it is free, and it is the sort of work that takes weeks to stand up properly and continuous attention to maintain. If your organisation already runs disciplined signing and provenance in its pipeline — the same muscle described in generating, signing and attesting provenance — extend it here. If it does not, this is a reason to build that capability now rather than discover the gap at integration.

Trust does not come from a config file you hard-code

The other half of the plumbing runs the opposite way. When a wallet presents credentials, you have to decide whether the party who issued those credentials is genuine — that the person-identification data or attestation came from an authority the Union actually trusts. You do this against trusted lists, and the structure is layered. Each Member State publishes a national trusted list of the entities operating under its supervision. The European Commission publishes a List of Trusted Lists, the LOTL, which is a signed meta-list pointing to every national list together with the trust anchor needed to verify each one.

The engineering point that matters: you pin one thing and one thing only, the Commission’s LOTL trust anchor. Everything else is derived dynamically from that single pinned root. You fetch the LOTL, verify its signature against the pinned anchor, follow the pointer to the relevant national list, verify that list against the anchor the LOTL gave you, and only then check whether the certificate in front of you belongs to a service whose status is ‘granted’. A list that does not verify against its anchor is discarded, not parsed. Trust-on-first-use has no place anywhere in this chain.

Here is the shape of that fetch-and-validate routine. It follows the ETSI trusted-list structure the ecosystem uses, and it deliberately leaves the XML-signature check as an explicit, non-optional step rather than pretending it away.

"""Fetch the EU List of Trusted Lists (LOTL), walk to a Member State's
trusted list, and confirm an issuer or relying-party certificate is
'granted'. The Commission LOTL anchor is pinned; every other anchor is
derived from it. Signatures are verified, never assumed."""

import base64
import requests
from lxml import etree
from cryptography import x509

TSL = {"tsl": "http://uri.etsi.org/02231/v2#"}
GRANTED = "http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/granted"
LOTL_URL = "https://ec.europa.eu/tools/lotl/eu-lotl.xml"
LOTL_ANCHOR = b"...pinned Commission LOTL trust anchor, DER-encoded..."


def _b64(text):
    return base64.b64decode("".join(text.split()))


def _fetch(url):
    resp = requests.get(url, timeout=10)
    resp.raise_for_status()
    return etree.fromstring(resp.content)


def _verify(root, anchor_der):
    # Verify the enveloped XML-DSig against the pinned anchor using a real
    # signature library (signxml/xmlsec). A list that fails to validate is
    # discarded here; parsing an unverified list is the whole vulnerability.
    if not verify_xmldsig(root, anchor_der):        # noqa: F821
        raise ValueError("Trusted list signature did not validate")


def national_list(lotl, country):
    for ptr in lotl.iterfind(".//tsl:OtherTSLPointer", TSL):
        terr = ptr.findtext(".//tsl:SchemeTerritory", namespaces=TSL)
        loc = ptr.findtext("tsl:TSLLocation", namespaces=TSL)
        anchor = ptr.findtext(".//tsl:X509Certificate", namespaces=TSL)
        if terr == country and loc:
            return loc, _b64(anchor) if anchor else None
    return None, None


def is_granted(tsl, cert):
    ski = x509.SubjectKeyIdentifier.from_public_key(cert.public_key()).digest
    for svc in tsl.iterfind(".//tsl:TSPService", TSL):
        status = svc.findtext(".//tsl:ServiceStatus", namespaces=TSL)
        for listed_b64 in svc.iterfind(".//tsl:X509Certificate", TSL):
            listed = x509.load_der_x509_certificate(_b64(listed_b64.text))
            listed_ski = x509.SubjectKeyIdentifier.from_public_key(
                listed.public_key()).digest
            if listed_ski == ski and status == GRANTED:
                return True
    return False


def verify_status(cert_pem, country):
    lotl = _fetch(LOTL_URL)
    _verify(lotl, LOTL_ANCHOR)                       # pinned root of trust
    url, anchor = national_list(lotl, country)
    if url is None:
        raise LookupError(f"No trusted list published for {country}")
    tsl = _fetch(url)
    _verify(tsl, anchor)                             # anchor from the LOTL
    cert = x509.load_pem_x509_certificate(cert_pem)
    return is_granted(tsl, cert)

In production you would cache the lists with respect to their next-update and validity fields, handle the historical status entries so a certificate valid at signing time is not wrongly rejected later, and fail closed when a list is stale or unreachable. But the spine is exactly this: pinned anchor, verified chain, explicit status check.

Why this is a schedule risk, not a sprint task

The wallets are arriving. Member States are standing up national wallet provision, and the acceptance obligation for private-sector relying parties — banks and the larger platforms among them — is expected to bite around late 2027; verify the exact date against your transposing national measures as they land. The number people fixate on is that acceptance deadline. The number that will actually hurt is how long the registration and cryptographic machinery takes to stand up before you can even begin meaningful testing.

Registration in your Member State is an administrative process with its own lead time. Obtaining registration and access certificates depends on a certificate authority being authorised and operational in your jurisdiction, which may not be true on the day you are ready. The trust-verification path has to be built, tested against real lists, and operated with the discipline PKI demands. If you scope this as a two-week API job in the quarter before the deadline, you will find that the acceptance you promised the board depends on plumbing that was never laid. That is the same failure mode I described in the companion piece on the relying-party integration every bank and platform faces, and it is why firms treating the wallet purely as an authentication upgrade — the ground covered in wallet-based strong customer authentication — keep underestimating the work beneath the login.

The wallet does not care how good your front end is. It answers registered relying parties presenting valid certificates, verified against a trust list, and it refuses everyone else. Build the plumbing first, or the polished acceptance flow is a demo that never gets a real reply.

Free interactive tool

Website compliance checklist

What your site has to do, based on what it actually does

Answer as much or as little as you like — the list builds as you go. Nothing is stored against your name and no email is required.

Can you trust the architecture you have?

Architecture diagrams rarely show the reality of how systems actually operate. An independent review establishes what is really there.