X Tutup
Skip to content

Commit 8dcd668

Browse files
Add OCSP signer generation to ceremony tool (letsencrypt#4813)
Initially this was going to just be a bool on the `intermediate` type, but there is enough different in terms of what is generated that I think it makes sense to add a completely separate type. Internally they share the same config, since basically everything else is the same (apart from a few constraints on what fields can be populated in the profile). This additionally fixes a bug where we weren't actually validating root/intermediate/key configs. Fixes letsencrypt#4741
1 parent a24da83 commit 8dcd668

File tree

5 files changed

+249
-40
lines changed

5 files changed

+249
-40
lines changed

cmd/ceremony/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ ceremony --config path/to/config.yml
99
`ceremony` operates in one of three modes
1010
* `root` - generates a signing key on HSM and creates a self-signed root certificate that uses the generated key, outputting a PEM public key, and a PEM certificate
1111
* `intermediate` - creates a intermediate certificate and signs it using a signing key already on a HSM, outputting a PEM certificate
12+
* `ocsp-signer` - creates a delegated OCSP signing certificate and signs it using a signing key already on a HSM, outputting a PEM certificate
1213
* `key` - generates a signing key on HSM, outputting a PEM public key
1314

1415
These modes are set in the `ceremony-type` field of the configuration file.
@@ -126,6 +127,56 @@ certificate-profile:
126127

127128
This config generates an intermediate certificate signed by a key in the HSM, identified by the object label `root signing key` and the object ID `ffff`. The subject key used is taken from `/home/user/intermediate-signing-pub.pem` and the issuer is `/home/user/root-cert.pem`, the resulting certificate is written to `/home/user/intermediate-cert.pem`.
128129

130+
### OCSP Signing Certificate ceremony
131+
132+
- `ceremony-type`: string describing the ceremony type, `ocsp-signer`.
133+
- `pkcs11`: object containing PKCS#11 related fields.
134+
| Field | Description |
135+
| --- | --- |
136+
| `module` | Path to the PKCS#11 module to use to communicate with a HSM. |
137+
| `pin` | Specifies the login PIN, should only be provided if the HSM device requires one to interact with the slot. |
138+
| `signing-key-slot` | Specifies which HSM object slot the signing key is in. |
139+
| `signing-key-label` | Specifies the HSM object label for the signing key. |
140+
| `signing-key-id` | Specifies the HSM object ID for the signing key. |
141+
- `inputs`: object containing paths for inputs
142+
| Field | Description |
143+
| --- | --- |
144+
| `public-key-path` | Path to PEM subject public key for certificate. |
145+
| `issuer-path` | Path to PEM issuer certificate. |
146+
- `outputs`: object containing paths to write outputs.
147+
| Field | Description |
148+
| --- | --- |
149+
| `certificate-path` | Path to store signed PEM certificate. |
150+
- `certificate-profile`: object containing profile for certificate to generate. Fields are documented [below](#Certificate-profile-format). The key-usages, ocsp-url, and crl-url fields must not be set.
151+
152+
When generating an OCSP signing certificate the key usages field will be set to just Digital Signature and an EKU extension will be included with the id-kp-OCSPSigning usage. Additionally an id-pkix-ocsp-nocheck extension will be included in the certificate.
153+
154+
Example:
155+
156+
```yaml
157+
ceremony-type: ocsp-signer
158+
pkcs11:
159+
module: /usr/lib/opensc-pkcs11.so
160+
signing-key-slot: 0
161+
signing-key-label: intermediate signing key
162+
signing-key-id: ffff
163+
inputs:
164+
public-key-path: /home/user/ocsp-signer-signing-pub.pem
165+
issuer-path: /home/user/intermediate-cert.pem
166+
outputs:
167+
certificate-path: /home/user/ocsp-signer-cert.pem
168+
certificate-profile:
169+
signature-algorithm: ECDSAWithSHA384
170+
common-name: CA OCSP signer
171+
organization: good guys
172+
country: US
173+
not-before: 2020-01-01 12:00:00
174+
not-after: 2040-01-01 12:00:00
175+
issuer-url: http://good-guys.com/root
176+
```
177+
178+
This config generates a delegated OCSP signing certificate signed by a key in the HSM, identified by the object label `intermediate signing key` and the object ID `ffff`. The subject key used is taken from `/home/user/ocsp-signer-signing-pub.pem` and the issuer is `/home/user/intermdiate-cert.pem`, the resulting certificate is written to `/home/user/ocsp-signer-cert.pem`.
179+
129180
### Key ceremony
130181

131182
- `ceremony-type`: string describing the ceremony type, `key`.

cmd/ceremony/cert.go

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,15 @@ var AllowedSigAlgs = map[string]x509.SignatureAlgorithm{
7777
"ECDSAWithSHA512": x509.ECDSAWithSHA512,
7878
}
7979

80-
func (profile *certProfile) verifyProfile(root bool) error {
80+
type certType int
81+
82+
const (
83+
rootCert certType = iota
84+
intermediateCert
85+
ocspCert
86+
)
87+
88+
func (profile *certProfile) verifyProfile(ct certType) error {
8189
if profile.NotBefore == "" {
8290
return errors.New("not-before is required")
8391
}
@@ -96,14 +104,29 @@ func (profile *certProfile) verifyProfile(root bool) error {
96104
if profile.Country == "" {
97105
return errors.New("country is required")
98106
}
99-
if !root && profile.OCSPURL == "" {
100-
return errors.New("ocsp-url is required for intermediates")
101-
}
102-
if !root && profile.CRLURL == "" {
103-
return errors.New("crl-url is required for intermediates")
107+
108+
if ct == intermediateCert {
109+
if profile.OCSPURL == "" {
110+
return errors.New("ocsp-url is required for intermediates")
111+
}
112+
if profile.CRLURL == "" {
113+
return errors.New("crl-url is required for intermediates")
114+
}
115+
if profile.IssuerURL == "" {
116+
return errors.New("issuer-url is required for intermediates")
117+
}
104118
}
105-
if !root && profile.IssuerURL == "" {
106-
return errors.New("issuer-url is required for intermediates")
119+
120+
if ct == ocspCert {
121+
if len(profile.KeyUsages) != 0 {
122+
return errors.New("key-usages cannot be set for a OCSP signer")
123+
}
124+
if profile.CRLURL != "" {
125+
return errors.New("crl-url cannot be set for a OCSP signer")
126+
}
127+
if profile.OCSPURL != "" {
128+
return errors.New("ocsp-url cannot be set for a OCSP signer")
129+
}
107130
}
108131
return nil
109132
}
@@ -139,6 +162,8 @@ type policyInformation struct {
139162
var (
140163
oidExtensionCertificatePolicies = asn1.ObjectIdentifier{2, 5, 29, 32}
141164
oidCPSQualifier = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 1}
165+
166+
oidOCSPNoCheck = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5}
142167
)
143168

144169
func buildPolicies(policies []policyInfoConfig) (pkix.Extension, error) {
@@ -164,7 +189,7 @@ func buildPolicies(policies []policyInfoConfig) (pkix.Extension, error) {
164189
}
165190

166191
// makeTemplate generates the certificate template for use in x509.CreateCertificate
167-
func makeTemplate(randReader io.Reader, profile *certProfile, pubKey []byte) (*x509.Certificate, error) {
192+
func makeTemplate(randReader io.Reader, profile *certProfile, pubKey []byte, ct certType) (*x509.Certificate, error) {
168193
dateLayout := "2006-01-02 15:04:05"
169194
notBefore, err := time.Parse(dateLayout, profile.NotBefore)
170195
if err != nil {
@@ -202,16 +227,19 @@ func makeTemplate(randReader io.Reader, profile *certProfile, pubKey []byte) (*x
202227
}
203228

204229
var ku x509.KeyUsage
205-
if len(profile.KeyUsages) == 0 {
206-
return nil, errors.New("key usages must be set")
207-
}
208230
for _, kuStr := range profile.KeyUsages {
209231
kuBit, ok := stringToKeyUsage[kuStr]
210232
if !ok {
211233
return nil, fmt.Errorf("unknown key usage %q", kuStr)
212234
}
213235
ku |= kuBit
214236
}
237+
if ct == ocspCert {
238+
ku = x509.KeyUsageDigitalSignature
239+
}
240+
if ku == 0 {
241+
return nil, errors.New("at least one key usage must be set")
242+
}
215243

216244
cert := &x509.Certificate{
217245
SignatureAlgorithm: sigAlg,
@@ -232,6 +260,14 @@ func makeTemplate(randReader io.Reader, profile *certProfile, pubKey []byte) (*x
232260
SubjectKeyId: subjectKeyID[:],
233261
}
234262

263+
if ct == ocspCert {
264+
cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}
265+
// ASN.1 NULL is 0x05, 0x00
266+
ocspNoCheckExt := pkix.Extension{Id: oidOCSPNoCheck, Value: []byte{5, 0}}
267+
cert.ExtraExtensions = append(cert.ExtraExtensions, ocspNoCheckExt)
268+
cert.IsCA = false
269+
}
270+
235271
if len(profile.Policies) > 0 {
236272
policyExt, err := buildPolicies(profile.Policies)
237273
if err != nil {

0 commit comments

Comments
 (0)
X Tutup