<!-- https://pkizerotohero.com/handbook/private-ca/ | Markdown mirror of the HTML page | PKI Zero to Hero, by Duyutech BV -->

> Build a private PKI: offline root and issuing CA, CP and CPS (RFC 3647), templates, name constraints, AD CS, private ACME, and when not to run your own CA.

Source: https://pkizerotohero.com/handbook/private-ca/ · Last reviewed: 2026-10-20

Part VI: Running PKI · Chapter 22

# A CA of your own: private two-tier PKI

**In one sentence:**

A private CA lets you issue certificates for internal systems under your own rules, built safely as an offline root plus an online issuing CA with clear policies and templates.

## Who is this for

- [Expert](https://pkizerotohero.com/start/#expert)
- [IT ops](https://pkizerotohero.com/start/#itops)
- [Developer](https://pkizerotohero.com/start/#developer)
- [Leader](https://pkizerotohero.com/start/#leader)
- [Beginner](https://pkizerotohero.com/start/#beginner)

Level: **Expert**

- [New to this? Start with The idea](https://pkizerotohero.com/handbook/private-ca/#the-idea)
- [Developer? Jump to How it really works](https://pkizerotohero.com/handbook/private-ca/#how-it-really-works)
- [IT ops? At work](https://pkizerotohero.com/handbook/private-ca/#at-work)
- [Leader? Why it matters](https://pkizerotohero.com/handbook/private-ca/#why-it-matters-to-the-business)

## The idea

- Beginner

Public CAs issue certificates that every browser trusts, but only for public names they can check, and only under the public rules of the CA/Browser Forum. Many certificates are not for the public at all. They protect internal websites, APIs between services, databases, VPN and Wi-Fi logins, devices and [mutual TLS](https://pkizerotohero.com/handbook/mutual-tls/) between machines.

For these, many organisations run a [private CA](https://pkizerotohero.com/handbook/glossary/#private-ca): a certificate authority of their own, whose root certificate they install on their own devices. Nobody else trusts it, and that is the point. Inside your organisation you decide the rules: which names, which lifetimes, which key types, who may ask for what. You also carry the whole responsibility. If your private CA is careless, everything that trusts it is exposed (see [Who do we trust?](https://pkizerotohero.com/handbook/trust-stores/)).

A well-built private PKI has three parts:

1. **A hierarchy.** An offline root that is used only rarely, and an online issuing CA that does the daily work. This is the classic [two-tier PKI](https://pkizerotohero.com/handbook/glossary/#two-tier-pki).
2. **Written rules.** A [certificate policy](https://pkizerotohero.com/handbook/glossary/#certificate-policy) (CP) says what must be true. A [certification practice statement](https://pkizerotohero.com/handbook/glossary/#cps) (CPS) says how your CA makes it true.
3. **Templates** or profiles, which turn the rules into settings: what each kind of certificate may contain and who may request it.

Done well, a private CA is as trustworthy as a public one for its purpose. Done badly, it is a quiet way to let anyone inside your network impersonate anyone else.

In the game

The Chancery

In the game, Rosa opens the Chancery, the kingdom’s own office for certificates. The Queen’s seal stays locked in the vault and is brought out only to authorise the Chancellor, whose seal is used every day under a written rulebook. Where it stops being true: trust in your CA exists only on devices you configured, so visitors from outside see an unknown issuer. And a rulebook enforces nothing by itself: only the templates and software settings do.

## How it really works

- Developer
- Expert

### Why two tiers?

Microsoft’s [AD CS documentation](https://learn.microsoft.com/en-us/windows-server/identity/ad-cs/certification-authority-role) puts the reason plainly: “If your root CA is compromised, all CAs in the hierarchy and all certificates issued from it are considered compromised. You can maximize the security of the root CA by keeping it disconnected from the network and by using subordinate CAs to issue certificates.” A disconnected root is called an offline root.

- **The root CA** is self-signed and long-lived. Its key sits in an HSM or on protected media in a safe (see [Guarding the keys](https://pkizerotohero.com/handbook/hsm-and-key-ceremonies/)). It signs only issuing CA certificates and its own CRLs, in short ceremonies.
- **The issuing CA** is online and signs end-entity certificates every day. Its certificate carries `pathLenConstraint` 0, which “indicates that no non-self-issued intermediate CA certificates may follow” it ([RFC 5280 section 4.2.1.9](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.9)), so it cannot create further CAs.

If the issuing CA is compromised, you revoke it from the root and build a new one. The root, and the trust you installed on every device, survive. Large organisations sometimes add a middle tier of policy CAs, but two tiers suit most.

One operational detail catches many teams out. The offline root still publishes a CRL for the CA certificates it issued, and that CRL has a `nextUpdate` date. If nobody brings the root out to sign a fresh CRL in time, clients that check revocation start to reject the whole hierarchy. Put the root’s CRL schedule in the calendar of the people who hold the keys.

*Diagram: An offline root CA, an HSM in a safe used rarely and only in ceremonies, signs an online issuing CA with pathlen 0, which issues every day, publishes CRLs and may be limited to your names. The issuing CA issues certificates to internal servers, users and clients, and devices. Your own devices trust the root because you installed it, marked with a tick.*

A two-tier private PKI: the root stays offline, the issuing CA does the work.

### What are the CP and the CPS?

Both follow the framework of RFC 3647, the same outline that public CAs and the CA/Browser Forum’s own requirements use. RFC 3647 defines them:

- A **certificate policy** is “a named set of rules that indicates the applicability of a certificate to a particular community and/or class of application with common security requirements.”
- A **certification practice statement** is “a statement of the practices that a certification authority employs in issuing, managing, revoking, and renewing or re-keying certificates.”

Section 3.5 sums up the difference: the CP establishes “what participants must do”, and the CPS discloses “how the participants perform their functions and implement controls”. Both use the same nine sections: introduction; publication and repository; identification and authentication; certificate life-cycle operations; facility, management and operational controls; technical security controls; certificate, CRL and OCSP profiles; compliance audit; and other business and legal matters. A small organisation often writes one combined CP/CPS. What matters is that it is written down, approved and followed.

*Diagram: Four layers, top to bottom, joined by arrows. Certificate Policy: what must be true. Practice Statement: how this CA does it. Profiles and templates: what each kind of certificate may contain. Issued certificates: they match the template, marked with a tick. A dashed line beside the stack says audits check every layer.*

Policy becomes practice, practice becomes templates, and templates shape every certificate.

### What belongs in a template or profile?

A template turns policy into settings for one kind of certificate:

- key type and size, and whether the key is generated by the requester or kept in hardware;
- lifetime;
- key usage and extended key usage: server authentication, client authentication or code signing, and nothing more than needed;
- **where the names come from**: from your directory, or typed in by the requester;
- who may enrol, and whether a person must approve each request;
- CRL and AIA addresses, so that clients can find the chain and the revocation status.

The name source is the most important line. A template that lets requesters choose any subject name can let them impersonate others, including administrators, wherever that CA is trusted. Restrict it. Take names from the directory wherever you can. If a template must accept names from the request, limit who may enrol, require a person to approve each request, and check every name before it is approved.

### How do name constraints limit a CA?

A CA certificate can carry a Name Constraints extension, which “MUST be used only in a CA certificate” and “indicates a name space within which all subject names in subsequent certificates in a certification path MUST be located” ([RFC 5280 section 4.2.1.10](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.10)). Permitted subtrees list what is allowed; excluded subtrees list what is forbidden. An issuing CA constrained to `corp.example` cannot produce a certificate for `www.example.com` that clients which enforce name constraints will accept, even if its key is misused. The RFC says CAs “MUST mark this extension as critical”; the Baseline Requirements allow non-critical marking only “if compatibility with certain legacy applications that do not support Name Constraints is necessary” (section 7.1.2.10.8). The lab below shows the effect.

### What does this look like in AD CS?

Active Directory Certificate Services (AD CS) is the most common private CA in Windows networks. The usual design is a standalone root CA that stays offline and an enterprise issuing CA joined to the domain. Microsoft notes that “Stand-alone CAs don’t require AD DS, and they don’t use certificate templates”, while enterprise CAs issue from templates and can enrol computers and users automatically.

Templates are where most AD CS risk sits. Microsoft’s guidance on [managing templates](https://learn.microsoft.com/en-us/windows-server/identity/ad-cs/manage-certificate-templates) says: “Prefer identity information from AD DS. If you enable **Supply in the request**, restrict enrollment and validate every permitted identity value because the requester supplies subject information.” The same page warns that a CA-wide setting, `EDITF_ATTRIBUTESUBJECTALTNAME2`, lets requesters add alternative names regardless of the template, so confirm that it is disabled. Microsoft Defender for Identity [calls the Supply in the request setting](https://learn.microsoft.com/en-us/defender-for-identity/security-posture-assessments/certificates) “one of the most common misconfigurations”. Its fixes include turning that setting off or turning on CA certificate manager approval for such templates.

Windows domain controllers have also tightened how they map certificates to accounts. Under [KB5014754](https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16), mappings “are considered strong if they are based on identifiers that you cannot reuse”, and since February 2025, domain controllers deny certificate sign-in when a certificate cannot be strongly mapped to an account. Certificates from older templates or third-party CAs may need the new mapping before they work for sign-in.

### Private ACME and short-lived certificates

A private CA does not have to mean manual requests. Many private CA products speak [ACME](https://pkizerotohero.com/handbook/acme/), so the same clients that renew public certificates can renew internal ones. smallstep’s step-ca, for example, issues certificates with “much shorter 24 hour lifetimes” by default. Short lifetimes suit internal PKI well: services renew automatically, and a stolen key stops working soon even where nobody checks revocation (see [Revocation](https://pkizerotohero.com/handbook/revocation/)).

Managed private CAs move the HSM, the availability and much of the auditing to a provider. AWS Private CA, for example, “enables creation of private certificate authority (CA) hierarchies, including root and subordinate CAs, without the investment and maintenance costs of operating an on-premises CA” ([AWS](https://docs.aws.amazon.com/privateca/latest/userguide/PcaWelcome.html)). Google Cloud offers a similar Certificate Authority Service. You still own the policy, the templates and the decision of who may request what.

### When should you not run your own CA?

- **For anything the public reaches.** Browsers and customers’ devices will not trust your root, and asking them to install it is a security risk in itself. Use a public CA with ACME.
- **When nobody will own it.** A private CA needs key custodians, ceremonies, a CRL schedule, monitoring, backups and documentation for years. If you cannot staff that, use a managed service.
- **To save money on public certificates.** Public DV certificates are free through ACME. A private CA is for names and uses that public CAs cannot or should not cover.
- **For partners and customers.** If they have to trust your root, constrain it to your names, or use publicly trusted certificates instead.

## At work

- Developer
- IT ops

### Build a lab two-tier PKI with name constraints

This lab uses OpenSSL with keys in files, which is fine for learning and wrong for production, where the root key belongs in an HSM and is created in a ceremony. The issuing CA’s extension file sets `pathlen:0` and limits it to `corp.example`:

```text
basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, keyCertSign, cRLSign
nameConstraints = critical, permitted;DNS:corp.example
```

Create the root, then let it sign the issuing CA:

```bash
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-384 -nodes \
  -keyout root.key -out root.pem -days 3650 -subj "/CN=Example Lab Root" \
  -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign"
openssl req -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
  -keyout issuing.key -out issuing.csr -subj "/CN=Example Lab Issuing CA"
openssl x509 -req -in issuing.csr -CA root.pem -CAkey root.key -CAcreateserial \
  -out issuing.pem -days 1825 -extfile issuing.ext
openssl x509 -in issuing.pem -noout -subject -issuer -ext basicConstraints,nameConstraints
```

```text
subject=CN=Example Lab Issuing CA
issuer=CN=Example Lab Root
X509v3 Basic Constraints: critical
    CA:TRUE, pathlen:0
X509v3 Name Constraints: critical
    Permitted:
      DNS:corp.example
```

The issuing CA then signed two server certificates in the same way: one for `app.corp.example` and one for `www.example.com`. Verifying both against the root shows the constraint at work:

```bash
openssl verify -CAfile root.pem -untrusted issuing.pem app.pem
openssl verify -CAfile root.pem -untrusted issuing.pem www.pem
```

```text
app.pem: OK
CN=www.example.com
error 47 at 0 depth lookup: permitted subtree violation
error www.pem: verification failed
```

The CA signed the second certificate without complaint. The client refused it, because the name lies outside the CA’s permitted space. Leave out `-CAfile root.pem`, so that the root is not trusted, and you get the most common private-CA error of all:

```text
CN=Example Lab Issuing CA
error 20 at 1 depth lookup: unable to get local issuer certificate
error app.pem: verification failed
```

The chain checker shows the same path step by step:

*Interactive tool: Chain checker (runs in the browser at [/tools/chain/](https://pkizerotohero.com/tools/chain/))*

### How do I distribute the root?

Every client that should trust your CA needs the root in its trust store. First compare its SHA-256 fingerprint with the one in the ceremony report. Then, for example:

```bash
# Debian and Ubuntu: the file must end in .crt
sudo cp root.pem /usr/local/share/ca-certificates/example-lab-root.crt
sudo update-ca-certificates
# Java (asks for the cacerts password)
keytool -importcert -cacerts -alias example-lab-root -file root.pem
```

```powershell
# Windows, as administrator; in a domain, use Group Policy instead
Import-Certificate -FilePath root.cer -CertStoreLocation Cert:\LocalMachine\Root
```

Node.js reads extra roots from `NODE_EXTRA_CA_CERTS`, and Python’s `requests` uses its own bundle unless told otherwise. Each runtime that is missing the root fails in its own words: [OpenSSL error 20](https://pkizerotohero.com/errors/openssl-verify-error-20/), [curl’s self-signed certificate in chain](https://pkizerotohero.com/errors/curl-self-signed-certificate-in-certificate-chain/), [Java’s PKIX path building failed](https://pkizerotohero.com/errors/java-pkix-path-building-failed/) and [Go’s unknown authority](https://pkizerotohero.com/errors/go-x509-unknown-authority/). The fix is always to add the root, never to switch verification off.

To inspect a root you were given before you install it, paste it into the decoder:

*Interactive tool: Certificate decoder (runs in the browser at [/tools/decoder/](https://pkizerotohero.com/tools/decoder/))*

## Common mistakes

- **“A private CA is less secure than a public one.”** It is exactly as secure as you run it. With an offline root, an HSM, clear templates and monitoring, it can be very strong. Without them, it is weak.
- **“The CP and the CPS are the same document.”** The CP says what must be true. The CPS says how this CA makes it true. They may be combined in one file, but both questions need answers.
- **“Keeping the root online is more convenient.”** It is, for attackers too. Keep it offline and bring it out only for ceremonies, including its CRL updates.
- **“Requesters can type any name; the CA will check.”** A CA signs what its template allows. Take names from the directory, or restrict enrolment and require approval.
- **“Internal services can skip verification.”** Distribute the root instead. Turning verification off removes the protection the CA exists to give.

## Why it matters to the business

- Leader

Why it matters to the business

Your own CA is your own trust anchor

A private CA decides who can prove to be whom inside your organisation: servers, users, devices and administrators. A well-run one makes internal encryption and strong sign-in cheap. A badly configured one can let any employee impersonate an administrator. Decide who owns the CA and its policy, fund the HSM, ceremonies and audits it needs, review its templates every year, and consider a managed private CA if you cannot staff it. Never use it for anything the public must trust.

## Check yourself

Check yourself

Why is the root CA in a two-tier PKI kept offline?

1. So that it can never expire
2. So that its key is rarely exposed; a compromised issuing CA can be revoked and replaced while the root survives
3. Because offline CAs issue certificates faster

Show answer

Answer B So that its key is rarely exposed; a compromised issuing CA can be revoked and replaced while the root survives

What happened

The root is used only to sign issuing CAs and its CRLs, in ceremonies.

Why

If the root is compromised, the whole hierarchy is. If an issuing CA is compromised, the root can revoke it.

The rule

Offline root, online issuing CA, and the root's CRL renewed on schedule.

Check yourself

A certificate template lets requesters type any subject name and issues without approval. What is the risk?

1. Certificates will be too long for some clients
2. Requesters can obtain certificates in other people's names and impersonate them
3. There is no risk, because the CA checks the requester's password

Show answer

Answer B Requesters can obtain certificates in other people's names and impersonate them

What happened

The CA signs whatever names the template allows the requester to supply.

Why

Systems that trust the CA accept those certificates as proof of identity.

The rule

Take names from the directory, or restrict enrolment and require approval.

Check yourself

An issuing CA is name-constrained to corp.example. It signs a certificate for www.example.com. What happens when a client that enforces name constraints verifies it?

1. The client accepts it, because the CA signed it
2. The client rejects it with a permitted subtree violation
3. The client asks the root CA for permission

Show answer

Answer B The client rejects it with a permitted subtree violation

What happened

The name lies outside the CA's permitted name space, so path validation fails.

Why

Name constraints in a CA certificate apply to every certificate below it in the chain.

The rule

Constrain issuing CAs to the names they really need.

## Go deeper

- [RFC 3647](https://www.rfc-editor.org/rfc/rfc3647): the framework for certificate policies and practice statements.
- [RFC 5280 sections 4.2.1.9 and 4.2.1.10](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.9): basic constraints, path length and name constraints.
- [Microsoft: Certification Authority role](https://learn.microsoft.com/en-us/windows-server/identity/ad-cs/certification-authority-role): offline roots, standalone and enterprise CAs.
- [Microsoft: Manage certificate templates](https://learn.microsoft.com/en-us/windows-server/identity/ad-cs/manage-certificate-templates): safe template settings.
- [KB5014754](https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16): strong certificate mapping on domain controllers.
- [Baseline Requirements](https://cabforum.org/working-groups/server/baseline-requirements/requirements/): a complete example of a CP structured by RFC 3647, written for public CAs.

The crash course covers private PKI in [lesson 9, Running PKI](https://pkizerotohero.com/course/running-pki/).

**Early edition.** Checked against the standards; an independent expert review is in progress. Found a mistake? [Tell us](https://pkizerotohero.com/contact/?topic=content-error&page=/handbook/private-ca/).

Last reviewed 20 October 2026Edition 0.9

## Keep going

Crash course: [Lesson 9: Running PKI](https://pkizerotohero.com/course/running-pki/)

### Error pages for this chapter

- [verify error:num=20:unable to get local issuer certificate](https://pkizerotohero.com/errors/openssl-verify-error-20/)
- [SSL certificate problem: self-signed certificate in certificate chain](https://pkizerotohero.com/errors/curl-self-signed-certificate-in-certificate-chain/)
- [PKIX path building failed … unable to find valid certification path to requested target](https://pkizerotohero.com/errors/java-pkix-path-building-failed/)
- [x509: certificate signed by unknown authority](https://pkizerotohero.com/errors/go-x509-unknown-authority/)

### How-to guides

- [How to trust a private root CA on Windows](https://pkizerotohero.com/guides/add-root-ca-windows/)
- [How to trust a private root CA on macOS](https://pkizerotohero.com/guides/add-root-ca-macos/)
- [How to trust a private root CA on Linux](https://pkizerotohero.com/guides/add-root-ca-linux/)
- [How to add a CA to a Java trust store (cacerts)](https://pkizerotohero.com/guides/add-root-ca-java/)
- [How to set up mutual TLS (nginx and curl)](https://pkizerotohero.com/guides/mutual-tls-setup/)
- [How to run a small private CA with step-ca (lab)](https://pkizerotohero.com/guides/private-ca-step-ca/)
- [How to build a test CA with OpenSSL (lab only)](https://pkizerotohero.com/guides/openssl-test-ca/)
- [How to use SSH certificates instead of known\_hosts](https://pkizerotohero.com/guides/ssh-certificates/)

### Tools

- [Chain checker](https://pkizerotohero.com/tools/chain/): Paste a chain and see which link is missing, expired or out of order.
- [Certificate decoder](https://pkizerotohero.com/tools/decoder/): Paste a PEM certificate and read every field in plain words.

### Topics

- [Private PKI](https://pkizerotohero.com/topics/private-pki/)
