A self-hosted Public Key Infrastructure (PKI) and Certificate Authority (CA) gives you complete control over certificate issuance for your internal services, devices, and applications. While Let’s Encrypt and ACME-based tools handle public-facing certificates, a self-hosted CA is essential for internal TLS, mutual TLS (mTLS), client authentication, code signing, and IoT device identity. This guide compares three self-hosted CA solutions: Cloudflare’s cfssl, smallstep’s step-ca, and EJBCA.
Why Run Your Own Certificate Authority?
Self-hosting a CA solves several critical infrastructure needs:
- Internal TLS — issue certificates for internal services without depending on public CAs
- Mutual TLS (mTLS) — authenticate service-to-service communication in zero-trust architectures
- Client certificates — replace passwords with certificate-based authentication
- Code signing — sign internal software builds and verify authenticity
- IoT device identity — provision unique identities for embedded devices at scale
- Full lifecycle control — manage issuance, renewal, and revocation on your own terms
- Air-gapped environments — issue certificates in networks with no internet access
Comparison Table
| Feature | cfssl (Cloudflare) | step-ca (smallstep) | EJBCA |
|---|---|---|---|
| Primary focus | PKI toolkit / API | Developer-friendly CA | Enterprise PKI |
| Language | Go | Go | Java |
| Web UI | cfssl-ui (basic) | step-ca has no built-in UI | Full web admin console |
| REST API | Yes | Yes | Yes (enterprise edition) |
| ACME support | No | Yes (built-in ACME server) | Via plugin |
| Certificate types | Server, client, code signing | Server, client, SSH certs | Full X.509 + CMS |
| SCEP support | No | Via add-on | Yes (built-in) |
| EST support | No | Yes | Yes |
| LDAP integration | No | Via provisioners | Built-in |
| HSM support | Via PKCS#11 | Via PKCS#11 | Full HSM support |
| Docker Compose | Simple | Simple | Complex (multiple services) |
| Best for | Automated PKI via API | Developer teams, mTLS | Enterprise compliance |
cfssl: Cloudflare’s PKI Toolkit
cfssl is Cloudflare’s open-source PKI toolkit. It provides a certificate generation, signing, and management API that powers Cloudflare’s own certificate infrastructure. cfssl is ideal for teams that want programmatic PKI management with a clean JSON API.
Docker Compose for cfssl
| |
cfssl CA Configuration
| |
Issuing Certificates via cfssl API
| |
Generating the CA with cfssl
| |
step-ca: Developer-Friendly Certificate Authority
step-ca by smallstep is a modern, developer-friendly private certificate authority with built-in ACME support, SSH certificate issuance, and an excellent CLI. It is designed for teams that want a simple setup with powerful features.
Docker Compose for step-ca
| |
Initializing step-ca
| |
Using step-ca with ACME
One of step-ca’s standout features is built-in ACME support, meaning tools like certbot and Caddy can request certificates from your internal CA:
| |
Issuing SSH Certificates with step-ca
step-ca uniquely supports SSH certificate issuance — a powerful feature for replacing SSH keys with short-lived certificates:
| |
step-ca Configuration (ca.json)
| |
EJBCA: Enterprise PKI
EJBCA is a full-featured, enterprise-grade PKI system built on Java EE. It supports the widest range of certificate types, protocols (SCEP, EST, CMP), and compliance requirements, making it the choice for organizations with complex PKI needs.
Docker Compose for EJBCA
| |
EJBCA Key Features
- Multiple CA hierarchies — create root and intermediate CAs with flexible chain structures
- SCEP protocol — enroll devices using the Simple Certificate Enrollment Protocol (used by Cisco, Microsoft NDES)
- EST protocol — modern enrollment via Enrollment over Secure Transport (RFC 7030)
- Hardware Security Module (HSM) support — store CA keys in hardware for FIPS 140-2 compliance
- Certificate profiles — define custom certificate attributes, extensions, and validity periods
- Role-based administration — granular access control for CA operators, auditors, and administrators
- Audit logging — comprehensive logging of all CA operations for compliance
- CMS/PKCS#7 — support for Cryptographic Message Syntax for signed and encrypted data
EJBCA Certificate Enrollment via SCEP
| |
Choosing the Right CA for Your Needs
Choose cfssl when:
- You want a lightweight, API-driven PKI
- You need programmatic certificate issuance from CI/CD pipelines
- Your team is comfortable working with JSON APIs and CLI tools
- You want the same PKI toolkit that powers Cloudflare’s infrastructure
Choose step-ca when:
- You want a developer-friendly CA with excellent documentation
- You need ACME support for internal certificate automation
- You want SSH certificate issuance alongside X.509
- You value a simple setup with powerful defaults
Choose EJBCA when:
- You need enterprise-grade compliance (FIPS, Common Criteria)
- You require SCEP/EST/CMP protocol support
- You need HSM integration for key storage
- You manage complex CA hierarchies with multiple RAs
- Your organization has regulatory PKI requirements
PKI Best Practices
Root CA Protection
The root CA key is the most sensitive asset in your PKI. Follow these practices:
| |
Certificate Lifecycle Management
| |
For teams managing public-facing certificates alongside internal ones, see our guide on TLS certificate automation with certbot, acme.sh, and lego. For verifying TLS configuration across your infrastructure, check our SSL/TLS scanning tools comparison.
FAQ
What is the difference between a root CA and an intermediate CA?
A root CA is the top-level certificate authority in a PKI hierarchy. Its certificate is self-signed and serves as the trust anchor. An intermediate CA is signed by the root CA (or another intermediate) and is used for day-to-day certificate issuance. Best practice is to keep the root CA offline and only use intermediate CAs for signing end-entity certificates. This limits the impact if an intermediate CA key is compromised — you can revoke the intermediate certificate and issue a new one without replacing the root.
Can step-ca replace Let’s Encrypt for internal services?
Yes. step-ca includes a built-in ACME server, meaning any ACME-compatible client (certbot, Caddy, Traefik, nginx with acme.sh) can request certificates from your step-ca instance. This gives you Let’s Encrypt-like automation for internal domains, with the added benefit that you control the CA and can issue certificates for any domain name (including .internal, .local, or private TLDs that public CAs won’t sign).
Do I need an enterprise CA like EJBCA for a small team?
Probably not. EJBCA is designed for organizations with complex compliance requirements, multiple CA hierarchies, and protocol needs (SCEP for network devices, EST for IoT). For a small to medium team, step-ca or cfssl are simpler to deploy and maintain. Move to EJBCA when you need features that cfssl and step-ca cannot provide: SCEP, HSM integration, multi-RA workflows, or regulatory compliance certifications.
How do I distribute the root CA certificate to client machines?
For Linux: copy the root CA cert to /usr/local/share/ca-certificates/ and run update-ca-certificates. For macOS: add to the Keychain and set trust to “Always Trust” for SSL. For Windows: import via Group Policy or certutil -addstore -f "ROOT" ca.crt. For Docker containers: mount the root CA cert and update the CA bundle in your container image. For browsers: each browser has its own certificate store — consider deploying via MDM for enterprise environments.
How long should internal certificates be valid?
For server certificates: 1-2 years is common for internal CAs (longer than public CAs because you control revocation). For client certificates: 24 hours to 90 days, depending on your rotation capability. For code signing certificates: 1-3 years. step-ca supports short-lived certificates (as short as 1 hour) with automatic renewal, which is the gold standard for zero-trust architectures.
What happens when a certificate is compromised?
Immediately revoke the certificate using your CA’s revocation mechanism. The CA publishes the revocation via Certificate Revocation List (CRL) or Online Certificate Status Protocol (OCSP). Clients checking revocation will reject the compromised certificate. Then investigate the compromise, rotate any affected keys, and issue new certificates. With step-ca, use step ca revoke; with cfssl, use the /api/v1/cfssl/revoke endpoint; with EJBCA, use the web UI or RA API.