Skip to content

PKIOverheid G4 migration #4355

Open
5 of 9 issues completed
Open

Description

@reinkrul

https://www.logius.nl/onze-dienstverlening/toegang/pkioverheid/wees-voorbereid-de-nieuwe-generatie-pkioverheidcertificaten-komen-eraan

First analysis:

New G4 PKIoverheid Private certificates:

  • Can still be used as a client certificate
  • Are still valid for just as long as the current certificates
  • Require support for RSA-PSS as cryptographic algorithm, in line with NCSC (mainly concerns network infrastructure, such as proxies)
  • Require the inclusion of G4 Certificate Authorities in the trust store (action required from Nuts node administrators at software vendors), otherwise a requesting party may be unable to establish a
    connection with a data source holder.
  • It is not yet clear to me from when the G4 certificates will be available

We need to find out what it means for the UZI server-certificate-backed credentials.

TODO:

  • Check CPS statement: what attributes will it contain, how does the CPS differ from the old one
  • Update java-didx509-toolkit if needed (not needed, verified; test in uzi-did-x509-issuer-java#5)
  • Update nuts-node if needed (no production change; tests in test: cover did:x509 with PKIoverheid G4-style certificate chains #4595)
  • Write migration path for community

did:x509 ca-fingerprints for G4 UZI server certificates

Pin the issuing (TSP) CA, not the shared G4 Root Priv G-TLS: KPN, DigiCert and Digidentity issue under the same root. The ca-fingerprint is the base64url-encoded, unpadded SHA-256 over the DER-encoded CA certificate.

Production Test (acceptatie)
Issuing CA CN=UZI Server - G4 PKIo Priv G-TLS SYS - 2025, organizationIdentifier=NTRNL-50000535, O=CIBG, C=NL CN=ACCEPTATIE UZI Server - G4 Priv G-TLS SYS - 2025, organizationIdentifier=NTRNL-50000535, O=CIBG, C=NL
Chain Staat der Nederlanden - G4 Root Priv G-TLS - 2024 → Staat der Nederlanden - G4 Intm Priv G-TLS SYS - 2024 → issuing CA ACCEPTATIE Zorg CSP - G4 Root Priv G-TLS - 2024 → ACCEPTATIE Zorg CSP - G4 Intm Priv G-TLS SYS - 2024 → issuing CA
Valid 2025-07-17 to 2039-05-18 2026-04-12 to 2039-05-18
Certificate http://cert.pkioverheid.nl/UZIServerG4PKIoPrivGTLSSYS2025.cer http://www.uzi-register-test.nl/cacerts/acceptatie_uzi_server-g4_priv_g-tls_sys-2025.cer
SHA-256 (hex) b034cbfffcafe784edaef49fefad354edbc7823d8d6522d3da6f8e91ff28aa42 b2f2b3371852d3b57574a91e91639957477a028effaaf8ea5de816077d988d91
SHA-1 (hex) 758b611fafbc4d178a88cb4048677ad1456cfb3c 9da8fcc902210f0934382dd6d351d7f0f20c8775
did:x509 prefix did:x509:0:sha256:sDTL__yv54TtrvSf7601TtvHgj2NZSLT2m-Okf8oqkI did:x509:0:sha256:svKzNxhS07V1dKkekWOZV0d6Ao7_qvjqXegWB32YjZE
Policy / discovery pattern ^did:x509:0:sha256:sDTL__yv54TtrvSf7601TtvHgj2NZSLT2m-Okf8oqkI::.*$ ^did:x509:0:sha256:svKzNxhS07V1dKkekWOZV0d6Ao7_qvjqXegWB32YjZE::.*$

UZI Server - G4 PKIo Priv G-TLS SYS - 2024 (vzLz0CQ4GMpgjboDI2aaEDA9mrKoEGiv_jc1ANaUaHo) was revoked on 2025-10-29 and must not be used.

Verify yourself:

curl -sO http://cert.pkioverheid.nl/UZIServerG4PKIoPrivGTLSSYS2025.cer
openssl dgst -sha256 -binary UZIServerG4PKIoPrivGTLSSYS2025.cer | base64 | tr '+/' '-_' | tr -d '='
# sDTL__yv54TtrvSf7601TtvHgj2NZSLT2m-Okf8oqkI

Sources: production CA list https://cert.pkioverheid.nl/ and https://www.uziregister.nl/softwareleveranciers/ca-certificaten-g4 (publishes the root thumbprints, which match); test CA list https://acceptatie.zorgcsp.nl/ca-certificaten.

Migration path for use cases

Every discovery definition and access policy that pins the G1 UZI-register Private Server CA G1 must accept the G4 UZI Server - G4 PKIo Priv G-TLS SYS - 2025 CA next to it until 12-11-2028. Pattern to use (production; the test hierarchy uses the test fingerprints from the table above):

^did:x509:0:sha256:(vdhg746H4rLH67NN1unhdxo6PF3shQunCA4-KQTb2Jc|sDTL__yv54TtrvSf7601TtvHgj2NZSLT2m-Okf8oqkI)[\w\-.:%]*$

Order, per use case:

  1. Update the definition and policy files in the use-case repository (add G4 next to G1, never replace).
  2. The discovery server operator deploys the updated definition and restarts.
  3. Every participant deploys the updated discovery definition and access policy and restarts (both are read at startup). A node running the old files does not see, and does not issue tokens to, participants that use a G4 credential.
  4. Only then may participants switch to a G4 certificate: issue a new X509Credential with the toolkit, load it in the wallet, re-register on the discovery service. Their did:x509 changes.
  5. Add the G4 chain to the truststores: tls.truststorefile (when the gRPC network is active) and the reverse proxy's client-certificate trust for the gRPC and FHIR endpoints. Truststores and instructions: https://nuts.nl/certs (Publish PKIoverheid truststores (G1 + G4) on nuts.nl/certs #4603).

Tracking per use case:

Nuts spec v1 use cases (truststore only)

These use cases run on the gRPC network (did:nuts) and use no X509Credential, so their definitions do not change. Their participants only add the G4 chain to their truststores (step 5 above): tls.truststorefile and the reverse proxy's client-certificate trust. This must be done network-wide before 12-11-2026, when CIBG starts issuing G4 certificates; otherwise a node with a G4 certificate cannot connect to a node that does not trust the G4 chain.

  • ANW: participants informed
  • eOverdracht: participants informed
  • KIK-V: participants informed

Activity

  1. self-assigned this
    on Jun 16, 2026
  2. reinkrul commented on Sep 25, 2026

    @reinkrul
    MemberAuthor

    PKIoverheid G4: concrete changes for UZI server certificates

    Source: CIBG "CA model, Pasmodel, Certificaat- en CRL-profielen Zorg CSP G4" v1.1 (table 29) vs. G1 profile v10.5.2 (table 25).

    What changes (G1 → G4)

    1. Issuing CA and chain. Issuer becomes CN=UZI Server - G4 PKIo Priv G-TLS SYS - 2024, O=CIBG, C=NL, under intermediate Staat der Nederlanden - G4 Intm Priv G-TLS SYS - 2024 and root Staat der Nederlanden - G4 Root Priv G-TLS - 2024. Chain depth stays 4. The G1 issuer's organizationIdentifier=NTRNL-50000535 is no longer in the issuer DN.
    2. Signature algorithm. RSASSA-PSS (OID 1.2.840.113549.1.1.10) with SHA-512, MGF1, 64-byte salt, on the certificate, the whole chain and the CRL. G1 used SHA256 with PKCS#1 v1.5.
    3. Certificate policy OIDs. 2.16.528.1.1003.1.2.44.15.35.11 (PKIo G4 Priv TLS Gen Sys Organization Validated Server) plus ETSI NCP 0.4.0.2042.1.1. CPS URI: https://www.zorgcsp.nl/certification-practice-statement-cps.
    4. Revocation endpoints. CRL: http://www.csp.uzi-register.nl/cdp/uzi_server-g4_pkio_priv_g-tls_sys-2024.crl. CA issuers AIA: http://cert.pkioverheid.nl/UZIServerG4PKIoPrivGTLSSYS2024.cer. OCSP unchanged: http://ocsp.uzi-register.nl.
    5. Issuance window. Test certificates available since 23 April 2026. G4-only issuance from 12 November 2026. G1 certificates invalid after 12 November 2028.

    What does not change

    • Subject: C, ST, L, O (subscriber name), serialNumber (fresh UZI number per issuance), CN (FQDN). No OU, no organizationIdentifier.
    • SAN: dNSName (same FQDN), otherName 2.5.5.5 with value 2.16.528.1.1003.1.3.5.5.5-1-<UZI-nr>-S-<URA>-00.000-<AGB>, and the permanentIdentifier otherName (1.3.6.1.5.5.7.8.3) carrying the URA with assigner 2.16.528.1.1007.3.3.
    • keyUsage digitalSignature + keyEncipherment, EKU serverAuth + clientAuth, RSA 4096 leaf key, 3-year validity.

    What this means for Nuts

    • Every did:x509 DID derived from a UZI server certificate gets a new CA fingerprint. Presentation Definition / policy regexes must accept both the G1 and the G4 CIBG TSP CA fingerprints until November 2028. Anchor on the CIBG TSP CA, not on the shared G-TLS root (that root also covers KPN/DigiCert/Digidentity server certificates).
    • The san:otherName and subject:O policies, the URA regex, and the go-didx509-toolkit parsing need no change.
    • The only technical risk is RSASSA-PSS/SHA-512 verification of chain and CRL. Go's crypto/x509 supports it (salt length equals hash length, which CIBG uses) and pki/validator.go verifies CRLs via CheckSignatureFrom, but nuts-node has no test coverage for it: all test chains are SHA256-RSA 2048. Add a PSS/SHA-512/4096 test chain to the did:x509 resolver and CRL tests, and run the e2e once against a CIBG G4 test certificate.
    • New policy OIDs and CRL URL need no code change: nuts-node does not check certificatePolicies and reads the CRL URL from the certificate.
  3. reinkrul commented on Sep 25, 2026

    @reinkrul
    MemberAuthor

    Supplementary findings to the change list above, in response to a claim "under G4 issuers can but whatever attribute they want in the certificate, making the X509Credential ecosystem unsafe".

    What CIBG is allowed to put in a UZI server certificate

    PKIoverheid Programme of Requirements v5.0 §7.1.4.2 is a closed list: "Certificates SHALL NOT contain any Subject Distinguished Name fields other than those described" and the same for subjectAltName. So a TSP cannot invent attributes; it can only choose the value layout inside an approved otherName.

    Field G4 rule (PoR v5.0) CIBG G4 server profile
    C Required Present
    O Required for legal entities Present (subscriber name)
    CN Required, SHOULD NOT for serverAuth certs Present (FQDN)
    organizationIdentifier Required for legal entities (NTRNL-<KvK> or OIN/HRN) Absent
    serialNumber Optional for legal entities, must uniquely identify subject Present (UZI number, new per issuance)
    L, ST, OU Only permitted under G1/G3 roots L and ST present, no OU
    SAN otherName type-id MSUPN, IA5String (2.5.5.5 allowed) with <Assigner-OID>-<Subject-string>, or PermanentIdentifier Both 2.5.5.5 and permanentIdentifier present
    SAN dNSName serverAuth certs, FQDN, no wildcards Present

    CIBG's profile diverges from the generic G4 rules on two points (keeps L/ST, omits organizationIdentifier). The CIBG profile document is what gets issued; noted here only in case a later profile revision aligns it with the PoR.

    Issuer

    CIBG remains the TSP, operating UZI Server - G4 PKIo Priv G-TLS SYS - 2024 under the shared G4 Root Priv G-TLS. KPN, DigiCert and Digidentity issue ordinary private server certificates under the same root, so the did:x509 anchor must be the CIBG TSP CA, not the root. No sign that server certificate issuance moves to a commercial TSP.

    Toolkit / nuts-node gaps (not caused by G4, but relevant)

    • go-didx509-toolkit can emit san:permanentIdentifier.value / san:permanentIdentifier.assigner policies (URA + assigner 2.16.528.1.1007.3.3), but nuts-node rejects those keys in vdr/didx509/validation.go (unknown policy key). The URA is the only stable identifier across renewals (the UZI number in the otherName is regenerated per issuance), so supporting this would let a policy pin the URA directly.
    • nuts-node has no subject:organizationIdentifier (2.5.4.97) policy. Not needed for CIBG certificates, but it is the mandatory organisation identifier for every other G4 legal-entity certificate.

    Sources

  4. reinkrul commented on Sep 29, 2026

    @reinkrul
    MemberAuthor

    Toolkit and node checks against the G4 profile (details in #4589):

    • go-didx509-toolkit: no change needed. The :main image issues a PS256 X509Credential from a chain that follows the CIBG G4 server profile (RSA 4096, RSASSA-PSS/SHA-512, UZI otherName + permanentIdentifier SANs); DID anchored on the issuing CA with the expected san:otherName and subject:O,L policies.
    • uzi-did-x509-issuer-java: no change needed. BouncyCastle parses the PSS chain, the DID anchors on the issuing CA, the permanentIdentifier otherName is ignored as before, PS256 signing works with the 4096-bit key. Test added in test: generate credential from a PKIoverheid G4-style certificate chain uzi-did-x509-issuer-java#5.
    • nuts-node: no production change needed. Unit tests for a PSS/4096 chain and PSS-signed CRL, plus the did:x509 e2e regenerated in the G4 profile, in test: cover did:x509 with PKIoverheid G4-style certificate chains #4595 (CI green, e2e passes). The real production G4 chain and the PSS-signed root/intermediate CRLs from Logius verify with Go's crypto/x509.

    Operational note for trust policies: there are already two UZI server TSP CAs under G4, and the did:x509 fingerprint must be pinned per TSP CA (not the shared G-TLS root):

    • UZI Server - G4 PKIo Priv G-TLS SYS - 2025: did:x509:0:sha256:sDTL__yv54TtrvSf7601TtvHgj2NZSLT2m-Okf8oqkI
    • UZI Server - G4 PKIo Priv G-TLS SYS - 2024: did:x509:0:sha256:vzLz0CQ4GMpgjboDI2aaEDA9mrKoEGiv_jc1ANaUaHo revoked on 29 October 2025, do not pin it. Only the 2025 CA is currently valid.

    Still open: repeat issuance and the e2e flow with a real CIBG G4 test server certificate (request in progress, #4589).

  5. reinkrul commented on Sep 30, 2026

    @reinkrul
    MemberAuthor

    Recorded the did:x509 ca-fingerprints for G4 UZI server certificates in the issue body (production and test environment), with a one-line verification command.

    • Production, UZI Server - G4 PKIo Priv G-TLS SYS - 2025: did:x509:0:sha256:sDTL__yv54TtrvSf7601TtvHgj2NZSLT2m-Okf8oqkI
    • Test, ACCEPTATIE UZI Server - G4 Priv G-TLS SYS - 2025: did:x509:0:sha256:svKzNxhS07V1dKkekWOZV0d6Ao7_qvjqXegWB32YjZE

    Double-checked: each hash computed by three independent implementations (Go crypto/sha256, OpenSSL, Python hashlib) on files freshly downloaded from cert.pkioverheid.nl and uzi-register-test.nl; both CA certificates verify against their root, and the production root's SHA-1/SHA-256 thumbprints match those published by CIBG; encoding is the same as go-didx509-toolkit uses to build the DID.

  6. changed the title [-]Find out what PKIOverheid G4 means for Nuts[/-] [+]PKIOverheid G4 migration[/+] on Sep 30, 2026
  7. reinkrul commented on Oct 7, 2026

    @reinkrul
    MemberAuthor

    X509Credential issuance with the real CIBG G4 test server certificate (not only the synthetic chain), both toolkits:

    • go-didx509-toolkit (:main): OK, see Test issuance and usage of X509Credential backed by a G4 certificate #4589. Issuer did:x509:0:sha256:svKzNxhS07V1dKkekWOZV0d6Ao7_qvjqXegWB32YjZE::san:otherName:2.16.528.1.1007.99.2110-1-900034846-S-90000380-00.000-11223344::subject:L:...:O:..., PS256, 4-cert x5c. Credential then passed the nuts-node e2e flow.
    • uzi-did-x509-issuer-java (main + the test from uzi-did-x509-issuer-java#5): OK, run locally against the same chain and key. Same fingerprint and otherName, valid PS256 JWT. Not committed: the real certificate and key stay out of the repo.

    Observation, independent of G4: the two toolkits produce a different DID for the same certificate. Go includes subject:L and subject:O by default, Java includes all of C, ST, L, O, CN (and OU when present), e.g. ...::subject:ST:Zuid%2DHolland:C:NL:CN:node%2Enuts%2Dservices%2Enl:L:...:O:.... The issuer patterns in the definitions ([\w\-.:%]*$ after the fingerprint) accept both, so this is harmless today, but an organisation that switches toolkit gets a new did:x509.

  8. reinkrul commented on Oct 7, 2026

    @reinkrul
    MemberAuthor

    Also ran the nuts-node e2e flow (oauth-flow/didx509, image built from #4595) with the X509Credential issued by uzi-did-x509-issuer-java from the real CIBG G4 test certificate instead of the Go toolkit: credential accepted in the wallet, discovery registration found, access token issued and introspected with organization_ura 90000380, name and city from the certificate. Same result as with the Go toolkit (#4589). The Java DID carries the extra subject:C, subject:ST and subject:CN policies, which the node validated against the certificate without issue.

    Both toolkits are therefore verified against the real G4 test certificate, both for issuance and for use in the node. Done once; not kept as a permanent test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

Priority

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions