1 package main
2
3 import (
4 "bytes"
5 "crypto/rand"
6 "crypto/rsa"
7 "crypto/x509"
8 "crypto/x509/pkix"
9 "encoding/asn1"
10 "encoding/hex"
11 "errors"
12 "fmt"
13 "io/fs"
14 "math/big"
15 "testing"
16 "time"
17
18 "github.com/letsencrypt/boulder/pkcs11helpers"
19 "github.com/letsencrypt/boulder/test"
20 "github.com/miekg/pkcs11"
21 )
22
23
24
25 func samplePubkey() []byte {
26 pubKey, err := hex.DecodeString("3059301306072a8648ce3d020106082a8648ce3d03010703420004b06745ef0375c9c54057098f077964e18d3bed0aacd54545b16eab8c539b5768cc1cea93ba56af1e22a7a01c33048c8885ed17c9c55ede70649b707072689f5e")
27 if err != nil {
28 panic(err)
29 }
30 return pubKey
31 }
32
33 func realRand(_ pkcs11.SessionHandle, length int) ([]byte, error) {
34 r := make([]byte, length)
35 _, err := rand.Read(r)
36 return r, err
37 }
38
39 func TestParseOID(t *testing.T) {
40 _, err := parseOID("")
41 test.AssertError(t, err, "parseOID accepted an empty OID")
42 _, err = parseOID("a.b.c")
43 test.AssertError(t, err, "parseOID accepted an OID containing non-ints")
44 _, err = parseOID("1.0.2")
45 test.AssertError(t, err, "parseOID accepted an OID containing zero")
46 oid, err := parseOID("1.2.3")
47 test.AssertNotError(t, err, "parseOID failed with a valid OID")
48 test.Assert(t, oid.Equal(asn1.ObjectIdentifier{1, 2, 3}), "parseOID returned incorrect OID")
49 }
50
51 func TestMakeSubject(t *testing.T) {
52 profile := &certProfile{
53 CommonName: "common name",
54 Organization: "organization",
55 Country: "country",
56 }
57 expectedSubject := pkix.Name{
58 CommonName: "common name",
59 Organization: []string{"organization"},
60 Country: []string{"country"},
61 }
62 test.AssertDeepEquals(t, profile.Subject(), expectedSubject)
63 }
64
65 func TestMakeTemplateRoot(t *testing.T) {
66 s, ctx := pkcs11helpers.NewSessionWithMock()
67 profile := &certProfile{}
68 randReader := newRandReader(s)
69 pubKey := samplePubkey()
70 ctx.GenerateRandomFunc = realRand
71
72 profile.NotBefore = "1234"
73 _, err := makeTemplate(randReader, profile, pubKey, nil, rootCert)
74 test.AssertError(t, err, "makeTemplate didn't fail with invalid not before")
75
76 profile.NotBefore = "2018-05-18 11:31:00"
77 profile.NotAfter = "1234"
78 _, err = makeTemplate(randReader, profile, pubKey, nil, rootCert)
79 test.AssertError(t, err, "makeTemplate didn't fail with invalid not after")
80
81 profile.NotAfter = "2018-05-18 11:31:00"
82 profile.SignatureAlgorithm = "nope"
83 _, err = makeTemplate(randReader, profile, pubKey, nil, rootCert)
84 test.AssertError(t, err, "makeTemplate didn't fail with invalid signature algorithm")
85
86 profile.SignatureAlgorithm = "SHA256WithRSA"
87 ctx.GenerateRandomFunc = func(pkcs11.SessionHandle, int) ([]byte, error) {
88 return nil, errors.New("bad")
89 }
90 _, err = makeTemplate(randReader, profile, pubKey, nil, rootCert)
91 test.AssertError(t, err, "makeTemplate didn't fail when GenerateRandom failed")
92
93 ctx.GenerateRandomFunc = realRand
94
95 _, err = makeTemplate(randReader, profile, pubKey, nil, rootCert)
96 test.AssertError(t, err, "makeTemplate didn't fail with empty key usages")
97
98 profile.KeyUsages = []string{"asd"}
99 _, err = makeTemplate(randReader, profile, pubKey, nil, rootCert)
100 test.AssertError(t, err, "makeTemplate didn't fail with invalid key usages")
101
102 profile.KeyUsages = []string{"Digital Signature", "CRL Sign"}
103 profile.Policies = []policyInfoConfig{{}}
104 _, err = makeTemplate(randReader, profile, pubKey, nil, rootCert)
105 test.AssertError(t, err, "makeTemplate didn't fail with invalid (empty) policy OID")
106
107 profile.Policies = []policyInfoConfig{{OID: "1.2.3"}, {OID: "1.2.3.4"}}
108 profile.CommonName = "common name"
109 profile.Organization = "organization"
110 profile.Country = "country"
111 profile.OCSPURL = "ocsp"
112 profile.CRLURL = "crl"
113 profile.IssuerURL = "issuer"
114 cert, err := makeTemplate(randReader, profile, pubKey, nil, rootCert)
115 test.AssertNotError(t, err, "makeTemplate failed when everything worked as expected")
116 test.AssertEquals(t, cert.Subject.CommonName, profile.CommonName)
117 test.AssertEquals(t, len(cert.Subject.Organization), 1)
118 test.AssertEquals(t, cert.Subject.Organization[0], profile.Organization)
119 test.AssertEquals(t, len(cert.Subject.Country), 1)
120 test.AssertEquals(t, cert.Subject.Country[0], profile.Country)
121 test.AssertEquals(t, len(cert.OCSPServer), 1)
122 test.AssertEquals(t, cert.OCSPServer[0], profile.OCSPURL)
123 test.AssertEquals(t, len(cert.CRLDistributionPoints), 1)
124 test.AssertEquals(t, cert.CRLDistributionPoints[0], profile.CRLURL)
125 test.AssertEquals(t, len(cert.IssuingCertificateURL), 1)
126 test.AssertEquals(t, cert.IssuingCertificateURL[0], profile.IssuerURL)
127 test.AssertEquals(t, cert.KeyUsage, x509.KeyUsageDigitalSignature|x509.KeyUsageCRLSign)
128 test.AssertEquals(t, len(cert.PolicyIdentifiers), 2)
129 test.AssertEquals(t, len(cert.ExtKeyUsage), 0)
130
131 cert, err = makeTemplate(randReader, profile, pubKey, nil, intermediateCert)
132 test.AssertNotError(t, err, "makeTemplate failed when everything worked as expected")
133 test.Assert(t, cert.MaxPathLenZero, "MaxPathLenZero not set in intermediate template")
134 test.AssertEquals(t, len(cert.ExtKeyUsage), 2)
135 test.AssertEquals(t, cert.ExtKeyUsage[0], x509.ExtKeyUsageClientAuth)
136 test.AssertEquals(t, cert.ExtKeyUsage[1], x509.ExtKeyUsageServerAuth)
137 }
138
139 func TestMakeTemplateRestrictedCrossCertificate(t *testing.T) {
140 s, ctx := pkcs11helpers.NewSessionWithMock()
141 ctx.GenerateRandomFunc = realRand
142 randReader := newRandReader(s)
143 pubKey := samplePubkey()
144 profile := &certProfile{
145 SignatureAlgorithm: "SHA256WithRSA",
146 CommonName: "common name",
147 Organization: "organization",
148 Country: "country",
149 KeyUsages: []string{"Digital Signature", "CRL Sign"},
150 OCSPURL: "ocsp",
151 CRLURL: "crl",
152 IssuerURL: "issuer",
153 NotAfter: "2020-10-10 11:31:00",
154 NotBefore: "2020-10-10 11:31:00",
155 }
156
157 tbcsCert := x509.Certificate{
158 SerialNumber: big.NewInt(666),
159 Subject: pkix.Name{
160 Organization: []string{"While Eek Ayote"},
161 },
162 NotBefore: time.Now(),
163 NotAfter: time.Now().Add(365 * 24 * time.Hour),
164 KeyUsage: x509.KeyUsageDigitalSignature,
165 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
166 BasicConstraintsValid: true,
167 }
168
169 cert, err := makeTemplate(randReader, profile, pubKey, &tbcsCert, crossCert)
170 test.AssertNotError(t, err, "makeTemplate failed when everything worked as expected")
171 test.Assert(t, !cert.MaxPathLenZero, "MaxPathLenZero was set in cross-sign")
172 test.AssertEquals(t, len(cert.ExtKeyUsage), 1)
173 test.AssertEquals(t, cert.ExtKeyUsage[0], x509.ExtKeyUsageServerAuth)
174 }
175
176 func TestMakeTemplateOCSP(t *testing.T) {
177 s, ctx := pkcs11helpers.NewSessionWithMock()
178 ctx.GenerateRandomFunc = realRand
179 randReader := newRandReader(s)
180 profile := &certProfile{
181 SignatureAlgorithm: "SHA256WithRSA",
182 CommonName: "common name",
183 Organization: "organization",
184 Country: "country",
185 OCSPURL: "ocsp",
186 CRLURL: "crl",
187 IssuerURL: "issuer",
188 NotAfter: "2018-05-18 11:31:00",
189 NotBefore: "2018-05-18 11:31:00",
190 }
191 pubKey := samplePubkey()
192
193 cert, err := makeTemplate(randReader, profile, pubKey, nil, ocspCert)
194 test.AssertNotError(t, err, "makeTemplate failed")
195
196 test.Assert(t, !cert.IsCA, "IsCA is set")
197
198 test.AssertEquals(t, cert.KeyUsage, x509.KeyUsageDigitalSignature)
199
200 test.AssertEquals(t, len(cert.ExtKeyUsage), 1)
201 test.AssertEquals(t, cert.ExtKeyUsage[0], x509.ExtKeyUsageOCSPSigning)
202
203 hasExt := false
204 asnNULL := []byte{5, 0}
205 for _, ext := range cert.ExtraExtensions {
206 if ext.Id.Equal(oidOCSPNoCheck) {
207 if hasExt {
208 t.Error("template contains multiple id-pkix-ocsp-nocheck extensions")
209 }
210 hasExt = true
211 if !bytes.Equal(ext.Value, asnNULL) {
212 t.Errorf("id-pkix-ocsp-nocheck has unexpected content: want %x, got %x", asnNULL, ext.Value)
213 }
214 }
215 }
216 test.Assert(t, hasExt, "template doesn't contain id-pkix-ocsp-nocheck extensions")
217 }
218
219 func TestMakeTemplateCRL(t *testing.T) {
220 s, ctx := pkcs11helpers.NewSessionWithMock()
221 ctx.GenerateRandomFunc = realRand
222 randReader := newRandReader(s)
223 profile := &certProfile{
224 SignatureAlgorithm: "SHA256WithRSA",
225 CommonName: "common name",
226 Organization: "organization",
227 Country: "country",
228 OCSPURL: "ocsp",
229 CRLURL: "crl",
230 IssuerURL: "issuer",
231 NotAfter: "2018-05-18 11:31:00",
232 NotBefore: "2018-05-18 11:31:00",
233 }
234 pubKey := samplePubkey()
235
236 cert, err := makeTemplate(randReader, profile, pubKey, nil, crlCert)
237 test.AssertNotError(t, err, "makeTemplate failed")
238
239 test.Assert(t, !cert.IsCA, "IsCA is set")
240 test.AssertEquals(t, cert.KeyUsage, x509.KeyUsageCRLSign)
241 }
242
243 func TestVerifyProfile(t *testing.T) {
244 for _, tc := range []struct {
245 profile certProfile
246 certType []certType
247 expectedErr string
248 }{
249 {
250 profile: certProfile{},
251 certType: []certType{intermediateCert, crossCert},
252 expectedErr: "not-before is required",
253 },
254 {
255 profile: certProfile{
256 NotBefore: "a",
257 },
258 certType: []certType{intermediateCert, crossCert},
259 expectedErr: "not-after is required",
260 },
261 {
262 profile: certProfile{
263 NotBefore: "a",
264 NotAfter: "b",
265 },
266 certType: []certType{intermediateCert, crossCert},
267 expectedErr: "signature-algorithm is required",
268 },
269 {
270 profile: certProfile{
271 NotBefore: "a",
272 NotAfter: "b",
273 SignatureAlgorithm: "c",
274 },
275 certType: []certType{intermediateCert, crossCert},
276 expectedErr: "common-name is required",
277 },
278 {
279 profile: certProfile{
280 NotBefore: "a",
281 NotAfter: "b",
282 SignatureAlgorithm: "c",
283 CommonName: "d",
284 },
285 certType: []certType{intermediateCert, crossCert},
286 expectedErr: "organization is required",
287 },
288 {
289 profile: certProfile{
290 NotBefore: "a",
291 NotAfter: "b",
292 SignatureAlgorithm: "c",
293 CommonName: "d",
294 Organization: "e",
295 },
296 certType: []certType{intermediateCert, crossCert},
297 expectedErr: "country is required",
298 },
299 {
300 profile: certProfile{
301 NotBefore: "a",
302 NotAfter: "b",
303 SignatureAlgorithm: "c",
304 CommonName: "d",
305 Organization: "e",
306 Country: "f",
307 OCSPURL: "g",
308 },
309 certType: []certType{intermediateCert, crossCert},
310 expectedErr: "crl-url is required for subordinate CAs",
311 },
312 {
313 profile: certProfile{
314 NotBefore: "a",
315 NotAfter: "b",
316 SignatureAlgorithm: "c",
317 CommonName: "d",
318 Organization: "e",
319 Country: "f",
320 OCSPURL: "g",
321 CRLURL: "h",
322 },
323 certType: []certType{intermediateCert, crossCert},
324 expectedErr: "issuer-url is required for subordinate CAs",
325 },
326 {
327 profile: certProfile{
328 NotBefore: "a",
329 NotAfter: "b",
330 SignatureAlgorithm: "c",
331 CommonName: "d",
332 Organization: "e",
333 Country: "f",
334 OCSPURL: "g",
335 CRLURL: "h",
336 IssuerURL: "i",
337 },
338 certType: []certType{intermediateCert, crossCert},
339 expectedErr: "policy should be exactly BRs domain-validated for subordinate CAs",
340 },
341 {
342 profile: certProfile{
343 NotBefore: "a",
344 NotAfter: "b",
345 SignatureAlgorithm: "c",
346 CommonName: "d",
347 Organization: "e",
348 Country: "f",
349 OCSPURL: "g",
350 CRLURL: "h",
351 IssuerURL: "i",
352 Policies: []policyInfoConfig{{OID: "1.2.3"}, {OID: "4.5.6"}},
353 },
354 certType: []certType{intermediateCert, crossCert},
355 expectedErr: "policy should be exactly BRs domain-validated for subordinate CAs",
356 },
357 {
358 profile: certProfile{
359 NotBefore: "a",
360 NotAfter: "b",
361 SignatureAlgorithm: "c",
362 CommonName: "d",
363 Organization: "e",
364 Country: "f",
365 },
366 certType: []certType{rootCert},
367 },
368 {
369 profile: certProfile{
370 NotBefore: "a",
371 NotAfter: "b",
372 SignatureAlgorithm: "c",
373 CommonName: "d",
374 Organization: "e",
375 Country: "f",
376 IssuerURL: "g",
377 KeyUsages: []string{"j"},
378 },
379 certType: []certType{ocspCert},
380 expectedErr: "key-usages cannot be set for a delegated signer",
381 },
382 {
383 profile: certProfile{
384 NotBefore: "a",
385 NotAfter: "b",
386 SignatureAlgorithm: "c",
387 CommonName: "d",
388 Organization: "e",
389 Country: "f",
390 IssuerURL: "g",
391 CRLURL: "i",
392 },
393 certType: []certType{ocspCert},
394 expectedErr: "crl-url cannot be set for a delegated signer",
395 },
396 {
397 profile: certProfile{
398 NotBefore: "a",
399 NotAfter: "b",
400 SignatureAlgorithm: "c",
401 CommonName: "d",
402 Organization: "e",
403 Country: "f",
404 IssuerURL: "g",
405 OCSPURL: "h",
406 },
407 certType: []certType{ocspCert},
408 expectedErr: "ocsp-url cannot be set for a delegated signer",
409 },
410 {
411 profile: certProfile{
412 NotBefore: "a",
413 NotAfter: "b",
414 SignatureAlgorithm: "c",
415 CommonName: "d",
416 Organization: "e",
417 Country: "f",
418 IssuerURL: "g",
419 },
420 certType: []certType{ocspCert},
421 },
422 {
423 profile: certProfile{
424 NotBefore: "a",
425 NotAfter: "b",
426 SignatureAlgorithm: "c",
427 CommonName: "d",
428 Organization: "e",
429 Country: "f",
430 IssuerURL: "g",
431 KeyUsages: []string{"j"},
432 },
433 certType: []certType{crlCert},
434 expectedErr: "key-usages cannot be set for a delegated signer",
435 },
436 {
437 profile: certProfile{
438 NotBefore: "a",
439 NotAfter: "b",
440 SignatureAlgorithm: "c",
441 CommonName: "d",
442 Organization: "e",
443 Country: "f",
444 IssuerURL: "g",
445 CRLURL: "i",
446 },
447 certType: []certType{crlCert},
448 expectedErr: "crl-url cannot be set for a delegated signer",
449 },
450 {
451 profile: certProfile{
452 NotBefore: "a",
453 NotAfter: "b",
454 SignatureAlgorithm: "c",
455 CommonName: "d",
456 Organization: "e",
457 Country: "f",
458 IssuerURL: "g",
459 OCSPURL: "h",
460 },
461 certType: []certType{crlCert},
462 expectedErr: "ocsp-url cannot be set for a delegated signer",
463 },
464 {
465 profile: certProfile{
466 NotBefore: "a",
467 NotAfter: "b",
468 SignatureAlgorithm: "c",
469 CommonName: "d",
470 Organization: "e",
471 Country: "f",
472 IssuerURL: "g",
473 },
474 certType: []certType{crlCert},
475 },
476 {
477 profile: certProfile{
478 NotBefore: "a",
479 },
480 certType: []certType{requestCert},
481 expectedErr: "not-before cannot be set for a CSR",
482 },
483 {
484 profile: certProfile{
485 NotAfter: "a",
486 },
487 certType: []certType{requestCert},
488 expectedErr: "not-after cannot be set for a CSR",
489 },
490 {
491 profile: certProfile{
492 SignatureAlgorithm: "a",
493 },
494 certType: []certType{requestCert},
495 expectedErr: "signature-algorithm cannot be set for a CSR",
496 },
497 {
498 profile: certProfile{
499 OCSPURL: "a",
500 },
501 certType: []certType{requestCert},
502 expectedErr: "ocsp-url cannot be set for a CSR",
503 },
504 {
505 profile: certProfile{
506 CRLURL: "a",
507 },
508 certType: []certType{requestCert},
509 expectedErr: "crl-url cannot be set for a CSR",
510 },
511 {
512 profile: certProfile{
513 IssuerURL: "a",
514 },
515 certType: []certType{requestCert},
516 expectedErr: "issuer-url cannot be set for a CSR",
517 },
518 {
519 profile: certProfile{
520 Policies: []policyInfoConfig{{OID: "1.2.3"}},
521 },
522 certType: []certType{requestCert},
523 expectedErr: "policies cannot be set for a CSR",
524 },
525 {
526 profile: certProfile{
527 KeyUsages: []string{"a"},
528 },
529 certType: []certType{requestCert},
530 expectedErr: "key-usages cannot be set for a CSR",
531 },
532 } {
533 for _, ct := range tc.certType {
534 err := tc.profile.verifyProfile(ct)
535 if err != nil {
536 if tc.expectedErr != err.Error() {
537 t.Fatalf("Expected %q, got %q", tc.expectedErr, err.Error())
538 }
539 } else if tc.expectedErr != "" {
540 t.Fatalf("verifyProfile didn't fail, expected %q", tc.expectedErr)
541 }
542 }
543 }
544 }
545
546 func TestGenerateCSR(t *testing.T) {
547 profile := &certProfile{
548 CommonName: "common name",
549 Organization: "organization",
550 Country: "country",
551 }
552
553 signer, err := rsa.GenerateKey(rand.Reader, 1024)
554 test.AssertNotError(t, err, "failed to generate test key")
555
556 csrBytes, err := generateCSR(profile, &wrappedSigner{signer})
557 test.AssertNotError(t, err, "failed to generate CSR")
558
559 csr, err := x509.ParseCertificateRequest(csrBytes)
560 test.AssertNotError(t, err, "failed to parse CSR")
561 test.AssertNotError(t, csr.CheckSignature(), "CSR signature check failed")
562 test.AssertEquals(t, len(csr.Extensions), 0)
563
564 test.AssertEquals(t, csr.Subject.String(), fmt.Sprintf("CN=%s,O=%s,C=%s",
565 profile.CommonName, profile.Organization, profile.Country))
566 }
567
568 func TestLoadCert(t *testing.T) {
569 _, err := loadCert("../../test/hierarchy/int-e1.cert.pem")
570 test.AssertNotError(t, err, "should not have errored")
571
572 _, err = loadCert("/path/that/will/not/ever/exist/ever")
573 test.AssertError(t, err, "should have failed opening certificate at non-existent path")
574 test.AssertErrorIs(t, err, fs.ErrNotExist)
575
576 _, err = loadCert("../../test/hierarchy/int-e1.key.pem")
577 test.AssertError(t, err, "should have failed when trying to parse a private key")
578
579 _, err = loadCert("../../test/test-root.pubkey.pem")
580 test.AssertError(t, err, "should have failed when trying to parse a public key")
581 }
582
View as plain text