
Pure PQC two-tier PKI hierarchy using ML-DSA-65 (NIST FIPS 204) on EJBCA Community Edition — Root CA + Sub CA with CRL and OCSP
A hands-on implementation of NIST FIPS 204 (ML-DSA) in a two-tier PKI hierarchy using EJBCA Community Edition.
📖 Full walkthrough: Building a Post-Quantum PKI: How I Signed a Root CA and Sub CA Using ML-DSA on EJBCA
A complete pure PQC PKI hierarchy running across two separate virtual machines, each with its own EJBCA container:
VM 1 — Root CA
└─ ML-DSA-65 self-signed Root CA
└─ CRL published and reachable
VM 2 — Sub CA
└─ ML-DSA-65 Sub CA signed by Root CA
└─ CRL published and reachable
└─ OCSP responder active
Every certificate in the chain — Root CA, Sub CA — is signed exclusively with ML-DSA-65 (NIST FIPS 204). No RSA. No ECDSA. Pure PQC end to end.
ML-DSA (formerly CRYSTALS-Dilithium) is the NIST-standardized post-quantum digital signature algorithm under FIPS 204. The Dilithium3 parameter set (ML-DSA-65) was selected for this lab — it provides 128-bit post-quantum security and is the recommended general-purpose option for most deployments.
ejbca-pqc-lab/
├── README.md
├── root-ca/
│ └── setup-lab.sh # Run on VM1 — sets up the Root CA machine
└── sub-ca/
└── setup-lab.sh # Run on VM2 — sets up the Sub CA machine
chmod +x setup-lab.sh
sudo ./setup-lab.sh
Once complete, access the Admin UI at:
https://<VM1_IP>:8443/ejbca/adminweb/
Import superadmin.p12 from the container into your browser:
docker cp ejbca:/opt/ejbca/p12/superadmin.p12 ~/superadmin.p12
Default password: ejbca
Then follow the Medium article to configure the Root CA manually.
chmod +x setup-lab.sh
sudo ./setup-lab.sh
Once complete, access the Admin UI at:
https://<VM2_IP>:8443/ejbca/adminweb/
Import superadmin.p12 the same way, then follow the Medium article to configure the Sub CA.
ejbca-data volumedocker-compose.ymlAll EJBCA configuration is done manually through the Admin UI — certificate profiles, crypto tokens, CA creation, CRL settings, and OCSP. This is intentional. The script gets the platform running. The Medium article documents every manual decision and why it was made.
| Component | CA | Status |
|---|
ACME — The ACME protocol (RFC 8555) does not support ML-DSA for account key authentication. Only RSA and ECDSA are allowed. Including ACME would require a classical account key, breaking the pure PQC story. IETF is working on a PQC ACME draft. This lab will be updated when that is finalized.
Browser trust — No major browser trust store recognizes ML-DSA yet. This is a private PKI lab, not a publicly trusted hierarchy. That is expected and correct.
Mohammad Amin Kokash Security Engineer GitHub · Medium
MIT
| CRL | Root CA | ✅ |
| CRL | Sub CA | ✅ |
| OCSP | Sub CA | ✅ |
| OCSP | Root CA | ❌ Not applicable |