p.enthalabs

GitHub - Eginn-33/csca-masterlist-tools: Parse an ICAO/BSI CSCA Master List (.ml) and export every Country Signing CA certificate — PEM bundle, TSV manifest, wi

Parse an ICAO / BSI **CSCA Master List** (`.ml`) and export every Country Signing CA certificate inside it — as a PEM bundle, as a TSV manifest, or just as a count.

One file, one dependency, no framework.

python extract_masterlist.py DE_ML_2026-01-08.ml -o csca_bundle.pem -m manifest.tsv

``` master list : DE_ML_2026-01-08.ml (891,227 bytes) entries : 581 parsed : 581 (openssl fallback rescued 6) unparsable : 0 unique : 581 countries : 112

top issuers : CN=26, HU=21, BE=19, LU=12, TR=12, LV=11, AU=10, GR=10, MT=10, NL=10

PEM bundle -> csca_bundle.pem manifest -> manifest.tsv ```

Why this exists

[](https://github.com/Eginn-33/csca-masterlist-tools#why-this-exists)

A Master List is the trust anchor set for electronic passports: to validate the SOD on any eMRTD you need the issuing country's CSCA, and Master Lists are how states distribute them in bulk. The format is specified in **ICAO Doc 9303 Part 12** and **BSI TR-03129**:

``` ContentInfo └─ SignedData └─ encapContentInfo.eContent (OCTET STRING) └─ CscaMasterList ::= SEQUENCE { version INTEGER, certList SET OF Certificate } ```

That is not hard — but there is remarkably little public tooling that just _opens the file and gives you the certificates_. Most eMRTD code buries the parse inside a larger verification stack. This repo is the parse on its own.

The interesting part: six certificates Python won't read

[](https://github.com/Eginn-33/csca-masterlist-tools#the-interesting-part-six-certificates-python-wont-read) Run against a real Master List with `--strict` and six entries drop on the floor:

``` entries : 581 parsed : 575 unparsable : 6 ```

All six fail inside `cryptography`'s Rust ASN.1 parser with:

``` ParseError { kind: ExtraData, location: ["Certificate::tbs_cert", "TbsCertificate::signature_alg"] } ```

They are not junk. OpenSSL reads every one of them:

| # | Subject | | --- | --- | | 48, 49 | `C=AT, O=GV, OU=BMI, CN=CSCA-AUSTRIA` | | 61 | `C=AE, O=MOI, OU=EPASS, CN=UAE CSCA 02` | | 84, 85, 90 | `C=JP, O=Japanese Government, OU=The Ministry of Foreign Affairs, CN=e-passportCSCA` |

These are live, government-issued CSCAs carrying trailing bytes in the signature `AlgorithmIdentifier` that a strict DER parser rejects. Austria, the UAE and Japan are not edge cases you get to skip — silently dropping them means passports from those countries fail validation with a confusing "unknown issuer" instead of a real error.

So the default path falls back to `openssl x509` for anything `cryptography` refuses, and the manifest records which parser produced each row:

``` country subject ... parser AT CN=CSCA-AUSTRIA,OU=BMI,... ... openssl AD CN=CSCA-AND,OU=MJI,... ... cryptography ```

Use `--strict` if you want the lenient path off and the failures visible.

Install

[](https://github.com/Eginn-33/csca-masterlist-tools#install)

pip install -r requirements.txt # cryptography

`openssl` on `PATH` is optional; without it, `--strict` behaviour is the only behaviour and the six certificates above are reported as unparsable.

Usage

[](https://github.com/Eginn-33/csca-masterlist-tools#usage)

``` extract_masterlist.py MASTERLIST [-o BUNDLE.pem] [-m MANIFEST.tsv] [--keep-duplicates] [--strict] ```

| flag | effect | | --- | --- | | `-o`, `--out` | write the concatenated PEM trust bundle | | `-m`, `--manifest` | write a TSV row per certificate (country, subject, serial, validity, sig alg, SHA-256, size, parser) | | `--keep-duplicates` | keep every entry; default de-duplicates by SHA-256 | | `--strict` | no OpenSSL fallback |

With no output flags it prints statistics and exits — useful for diffing two Master List releases.

See `examples/manifest_sample.tsv` for the manifest shape.

Where to get a Master List

[](https://github.com/Eginn-33/csca-masterlist-tools#where-to-get-a-master-list)

- **ICAO PKD** — https://pkddownloadsg.icao.int/ (the authoritative source)

- **German BSI** — publishes a national Master List that aggregates a large share of participating states

No Master List is bundled here, and `.gitignore` refuses to commit one. Fetch your own and keep it fresh: CSCAs roll over, and a stale trust store is how you end up rejecting valid documents.

Related projects

[](https://github.com/Eginn-33/csca-masterlist-tools#related-projects)

- JMRTD — the long-running open-source eMRTD implementation (Java). Also publishes per-country CSCA certificates, useful when you want a handful of anchors rather than a whole Master List.

- Kinegram eMRTD Connector — open-source client for a commercial chip-verification service.

Neither of them will hand you the certificates out of a `.ml` file, which is what this repo is for.

Scope

[](https://github.com/Eginn-33/csca-masterlist-tools#scope)

This tool reads **public trust anchors** — the certificates states publish specifically so that anyone can validate the passports they issue. It handles no private keys, no chip communication, and no passport data. It is the boring, public half of eMRTD work, which is exactly why it can be open.

License

[](https://github.com/Eginn-33/csca-masterlist-tools#license) MIT