Skip to content
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 61 additions & 10 deletions step-ca/templates.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Configuring `step-ca` Templates
html_title: Configuring open source step-ca Templates
description: Learn how to configure step-ca Templates
updated_at: March 26, 2025
updated_at: April 03, 2025
---

People use private CAs for all sorts of things, in many different contexts:
Expand Down Expand Up @@ -805,6 +805,7 @@ the following properties:

```json
{
...
"nameConstraints": {
"critical": false,
"permittedDNSDomains": ["doe.com"],
Expand All @@ -814,8 +815,9 @@ the following properties:
"permittedEmailAddresses": ["jane@doe.com"],
"excludedEmailAddresses": ["jane@doe.org"],
"permittedURIDomains": ["https://doe.com"],
"excludedURIDomains": ["https://doe.org"],
}
"excludedURIDomains": ["https://doe.org"]
}
...
}
```

Expand All @@ -824,10 +826,12 @@ write it as a string:

```json
{
...
"nameConstraints": {
"critical": true,
"permittedDNSDomains": "example.com"
}
...
}
```

Expand Down Expand Up @@ -861,16 +865,63 @@ also write a custom extension like:

```json
{
"extensions": [
{"id": "1.2.3.4", "critical": false, "value": "Y3VzdG9tIGV4dGVuc2lvbiB2YWx1ZQ=="}
]
...
"extensions": [
{"id": "1.2.3.4", "critical": false, "value": "Y3VzdG9tIGV4dGVuc2lvbiB2YWx1ZQ=="}
]
...
}
```

The value of the extension is the Base64 encoding of the
actual ASN.1 bytes that go into that extension.

For a more human-readable template,
you can also use [ASN.1 functions](#asn1-values) in the extension `value` field.
We'll do that in the next example.

#### Deep dive: Certificate Policies

X.509 Certificate Policies define policy constraints of a certificate.
They help relying parties determine the trustworthiness of a certificate,
and how to use it in practice.

For example, in the Web PKI, the Policy OID `2.23.140.1.2.1` is used to distinguish a certificate that meets the [CA/Browser Forum Baseline Requirements](https://cabforum.org/working-groups/server/baseline-requirements/requirements/) for Domain Validation.

In an internal PKI, a large organization might use a Certificate Policy to detail to a relying party how the CA verified the identity of the certificate requestor, or to approve a certificate for specific applications.
But, this sort of use case is very niche.

A Certificate Practices Statement (CPS) is type of Certificate Policy that references a document describing a CA's operational practices and security controls.

Here's [an example of a CPS from Let's Encrypt](https://letsencrypt.org/documents/isrg-cp-cps-v5.7/).

Let's add a policy extension with a CPS to an X.509 template.
We'll need to construct some ASN.1 for this.

```
{
...
"extensions": [
{
"id": "2.5.29.32",
"value": {{
asn1Seq
(asn1Seq
(asn1Enc "oid:1.3.6.1.4.1.99999.1.1.1")
(asn1Seq (asn1Seq (asn1Enc "oid:1.3.6.1.5.5.7.2.1") (asn1Enc "ia5:http://example.com/cps")))
)
| toJson
}}
}
]
...
}
```

The crux here is that the value of the extension is the Base64 encoding of the
actual bytes that go into that extension, so if you are encoding a structure
in your extension using the ASN.1 encoding, you will have to put the Base64
version of the encoded bytes.
First, the public OID `2.5.29.32` represents the Certificate Policies extension.
In the value for this extension, we have two policies.
The first references Policy OID `1.3.6.1.4.1.99999.1.1.1`, a custom policy OID defined by our example organization.
The second policy contains both the well-defined OID `1.3.6.1.5.5.7.2.1` [representing a CPS pointer](https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.4), and the CPS pointer `http://example.com/cps`.

#### X.509 OpenVPN certificates

Expand Down