Rather that having your step ca issue certificates that only reflect what is in the CSR, you can use certificate templates in order to dynamically add content to the x509 certificates being issued.
CA Configuration
In your /usr/local/etc/step/ca/config/ca.json configuration file you need to add some options to your provisioner. Let’s assume you have an existing ACME provisioner that looks as follows:
{
"type": "ACME",
"name": "24h",
"claims": {
"maxTLSCertDuration": "24h0m0s",
"defaultTLSCertDuration": "24h0m0s"
}
},
After the claims section you need to insert an options block. As usual, don’t forget the comma after the curly braces that end the claims section.
{
"type": "ACME",
"name": "24h",
"claims": {
"maxTLSCertDuration": "24h0m0s",
"defaultTLSCertDuration": "24h0m0s"
},
"options": {
"x509": {
"templateFile": "/usr/local/etc/step/ca/templates/certs/x509/acme.tpl",
"templateData": {
"TDCountry": "DE",
"TDStateOrProvince": "Saxony",
"TDLocality": "Dresden",
"TDStreetAddress": "Musterstrasse 1, 01234 Dresden, Germany",
"TDOrganization": "Tinkivity",
"TDOrganizationalUnit": "web server team"
}
}
}
},
Line 10 points to a template file that we will look at in just a few seconds. Line 11 introduces a data section that we use to inject some dynamic data. The data items (line 12 until line 17) make more sense when looking at the actual acme.tpl template file as referenced in line 11.
Template file
Below shows our acme.tpl template file. I will not explain the complete template but just some of the most important aspects.
{
"subject": {
{{- if .Insecure.CR.Subject.CommonName }}
"commonName": "{{ .Insecure.CR.Subject.CommonName }}",
{{- else }}
"commonName": "{{ (index .SANs 0).Value }}",
{{- end }}
"country": "{{ .TDCountry }}",
"province": "{{ .TDStateOrProvince }}",
"locality": "{{ .TDLocality }}",
"streetAddress": "{{ .TDStreetAddress }}",
"organization": "{{ .TDOrganization }}",
"organizationalUnit": "{{ .TDOrganizationalUnit }}"
},
"sans": {{ toJson .SANs }},
{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }}
"keyUsage": ["keyEncipherment", "digitalSignature"],
{{- else }}
"keyUsage": ["digitalSignature"],
{{- end }}
"extKeyUsage": ["serverAuth", "clientAuth"]
}
Lines 3 until 7 check if the CSR contains a common name in the subject. If the common name exists it will be applied, otherwise the first subject alternative name is used for the common name in the certificate subject. The reason for that logic is that certbot in my case seems to sometimes omit the common name ;-(
Lines 8 until 13 contain a reference to the injected template data and set the according subject fields for the certificate that is being issued.
Certificate issued with applied template
Let’s check how a certificate obtained via certbot looks like after the template is applied.
andreas@testserver ➜ ~ sudo openssl x509 -noout -text -in /usr/local/etc/letsencrypt/live/testserver/fullchain.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
9f:95:31:ed:1f:0b:b8:99:39:e1:64:02:73:89:d1:db
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = DE, ST = Saxony, O = Tinkivity, OU = Tinkivity Intermediate Certificate Authority, CN = Smallstep Intermediate CA, emailAddress = xxx@xxx.com
Validity
Not Before: Dec 7 18:18:07 2020 GMT
Not After : Dec 8 18:19:07 2020 GMT
Subject: C = DE, ST = Saxony, L = Dresden, street = "Musterstrasse 1, 01234 Dresden, Germany", O = Tinkivity, OU = web server team, CN = testserver
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:c4:74:01:28:bb:26:20:2f:a1:6b:30:44:9e:9b:
...
<< REDACTED >>
...
04:57
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Subject Key Identifier:
81:7C:BB:71:7F:02:52:06:71:CF:E2:87:3B:CA:8A:09:6C:81:65:46
X509v3 Authority Key Identifier:
keyid:87:32:28:49:63:29:06:79:96:13:DE:47:14:9F:EF:C0:DD:EC:4D:C3
X509v3 Subject Alternative Name:
DNS:testserver, DNS:testserver.local
1.3.6.1.4.1.37476.9000.64.1:
0
.....24h..
Signature Algorithm: sha256WithRSAEncryption
b7:74:16:a4:a1:5a:fb:df:a0:ea:42:5c:cd:70:fc:16:2d:8b:
...
<< REDACTED >>
...
c9:9e:db:00:40:56:61:a1
