github.com/letsencrypt/boulder@v0.20251208.0/cmd/ceremony/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "context" 6 "crypto" 7 "crypto/ecdsa" 8 "crypto/rsa" 9 "crypto/x509" 10 "crypto/x509/pkix" 11 "encoding/asn1" 12 "encoding/pem" 13 "errors" 14 "flag" 15 "fmt" 16 "log" 17 "os" 18 "slices" 19 "time" 20 21 "gopkg.in/yaml.v3" 22 23 zlintx509 "github.com/zmap/zcrypto/x509" 24 "github.com/zmap/zlint/v3" 25 26 "github.com/letsencrypt/boulder/goodkey" 27 "github.com/letsencrypt/boulder/linter" 28 "github.com/letsencrypt/boulder/pkcs11helpers" 29 "github.com/letsencrypt/boulder/revocation" 30 "github.com/letsencrypt/boulder/strictyaml" 31 ) 32 33 var kp goodkey.KeyPolicy 34 35 func init() { 36 var err error 37 kp, err = goodkey.NewPolicy(nil, nil) 38 if err != nil { 39 log.Fatal("Could not create goodkey.KeyPolicy") 40 } 41 } 42 43 type lintCert *x509.Certificate 44 45 // issueLintCertAndPerformLinting issues a linting certificate from a given 46 // template certificate signed by a given issuer and returns a *lintCert or an 47 // error. The lint certificate is linted prior to being returned. The public key 48 // from the just issued lint certificate is checked by the GoodKey package. 49 func issueLintCertAndPerformLinting(tbs, issuer *x509.Certificate, subjectPubKey crypto.PublicKey, signer crypto.Signer, skipLints []string) (lintCert, error) { 50 bytes, err := linter.Check(tbs, subjectPubKey, issuer, signer, skipLints) 51 if err != nil { 52 return nil, fmt.Errorf("certificate failed pre-issuance lint: %w", err) 53 } 54 lc, err := x509.ParseCertificate(bytes) 55 if err != nil { 56 return nil, err 57 } 58 err = kp.GoodKey(context.Background(), lc.PublicKey) 59 if err != nil { 60 return nil, err 61 } 62 63 return lc, nil 64 } 65 66 // postIssuanceLinting performs post-issuance linting on the raw bytes of a 67 // given certificate with the same set of lints as 68 // issueLintCertAndPerformLinting. The public key is also checked by the GoodKey 69 // package. 70 func postIssuanceLinting(fc *x509.Certificate, skipLints []string) error { 71 if fc == nil { 72 return fmt.Errorf("certificate was not provided") 73 } 74 parsed, err := zlintx509.ParseCertificate(fc.Raw) 75 if err != nil { 76 // If zlintx509.ParseCertificate fails, the certificate is too broken to 77 // lint. This should be treated as ZLint rejecting the certificate 78 return fmt.Errorf("unable to parse certificate: %s", err) 79 } 80 registry, err := linter.NewRegistry(skipLints) 81 if err != nil { 82 return fmt.Errorf("unable to create zlint registry: %s", err) 83 } 84 lintRes := zlint.LintCertificateEx(parsed, registry) 85 err = linter.ProcessResultSet(lintRes) 86 if err != nil { 87 return err 88 } 89 err = kp.GoodKey(context.Background(), fc.PublicKey) 90 if err != nil { 91 return err 92 } 93 94 return nil 95 } 96 97 type keyGenConfig struct { 98 Type string `yaml:"type"` 99 RSAModLength int `yaml:"rsa-mod-length"` 100 ECDSACurve string `yaml:"ecdsa-curve"` 101 } 102 103 var allowedCurves = map[string]bool{ 104 "P-256": true, 105 "P-384": true, 106 "P-521": true, 107 } 108 109 func (kgc keyGenConfig) validate() error { 110 if kgc.Type == "" { 111 return errors.New("key.type is required") 112 } 113 if kgc.Type != "rsa" && kgc.Type != "ecdsa" { 114 return errors.New("key.type can only be 'rsa' or 'ecdsa'") 115 } 116 if kgc.Type == "rsa" && (kgc.RSAModLength != 2048 && kgc.RSAModLength != 4096) { 117 return errors.New("key.rsa-mod-length can only be 2048 or 4096") 118 } 119 if kgc.Type == "rsa" && kgc.ECDSACurve != "" { 120 return errors.New("if key.type = 'rsa' then key.ecdsa-curve is not used") 121 } 122 if kgc.Type == "ecdsa" && !allowedCurves[kgc.ECDSACurve] { 123 return errors.New("key.ecdsa-curve can only be 'P-256', 'P-384', or 'P-521'") 124 } 125 if kgc.Type == "ecdsa" && kgc.RSAModLength != 0 { 126 return errors.New("if key.type = 'ecdsa' then key.rsa-mod-length is not used") 127 } 128 129 return nil 130 } 131 132 type PKCS11KeyGenConfig struct { 133 Module string `yaml:"module"` 134 PIN string `yaml:"pin"` 135 StoreSlot uint `yaml:"store-key-in-slot"` 136 StoreLabel string `yaml:"store-key-with-label"` 137 } 138 139 func (pkgc PKCS11KeyGenConfig) validate() error { 140 if pkgc.Module == "" { 141 return errors.New("pkcs11.module is required") 142 } 143 if pkgc.StoreLabel == "" { 144 return errors.New("pkcs11.store-key-with-label is required") 145 } 146 // key-slot is allowed to be 0 (which is a valid slot). 147 // PIN is allowed to be "", which will commonly happen when 148 // PIN entry is done via PED. 149 return nil 150 } 151 152 // checkOutputFile returns an error if the filename is empty, 153 // or if a file already exists with that filename. 154 func checkOutputFile(filename, fieldname string) error { 155 if filename == "" { 156 return fmt.Errorf("outputs.%s is required", fieldname) 157 } 158 if _, err := os.Stat(filename); !os.IsNotExist(err) { 159 return fmt.Errorf("outputs.%s is %q, which already exists", 160 fieldname, filename) 161 } 162 163 return nil 164 } 165 166 type rootConfig struct { 167 CeremonyType string `yaml:"ceremony-type"` 168 PKCS11 PKCS11KeyGenConfig `yaml:"pkcs11"` 169 Key keyGenConfig `yaml:"key"` 170 Outputs struct { 171 PublicKeyPath string `yaml:"public-key-path"` 172 CertificatePath string `yaml:"certificate-path"` 173 } `yaml:"outputs"` 174 CertProfile certProfile `yaml:"certificate-profile"` 175 SkipLints []string `yaml:"skip-lints"` 176 } 177 178 func (rc rootConfig) validate() error { 179 err := rc.PKCS11.validate() 180 if err != nil { 181 return err 182 } 183 184 // Key gen fields 185 err = rc.Key.validate() 186 if err != nil { 187 return err 188 } 189 190 // Output fields 191 err = checkOutputFile(rc.Outputs.PublicKeyPath, "public-key-path") 192 if err != nil { 193 return err 194 } 195 err = checkOutputFile(rc.Outputs.CertificatePath, "certificate-path") 196 if err != nil { 197 return err 198 } 199 200 // Certificate profile 201 err = rc.CertProfile.verifyProfile(rootCert) 202 if err != nil { 203 return err 204 } 205 206 return nil 207 } 208 209 type PKCS11SigningConfig struct { 210 Module string `yaml:"module"` 211 PIN string `yaml:"pin"` 212 SigningSlot uint `yaml:"signing-key-slot"` 213 SigningLabel string `yaml:"signing-key-label"` 214 } 215 216 func (psc PKCS11SigningConfig) validate() error { 217 if psc.Module == "" { 218 return errors.New("pkcs11.module is required") 219 } 220 if psc.SigningLabel == "" { 221 return errors.New("pkcs11.signing-key-label is required") 222 } 223 // key-slot is allowed to be 0 (which is a valid slot). 224 return nil 225 } 226 227 type intermediateConfig struct { 228 CeremonyType string `yaml:"ceremony-type"` 229 PKCS11 PKCS11SigningConfig `yaml:"pkcs11"` 230 Inputs struct { 231 PublicKeyPath string `yaml:"public-key-path"` 232 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 233 } `yaml:"inputs"` 234 Outputs struct { 235 CertificatePath string `yaml:"certificate-path"` 236 } `yaml:"outputs"` 237 CertProfile certProfile `yaml:"certificate-profile"` 238 SkipLints []string `yaml:"skip-lints"` 239 } 240 241 func (ic intermediateConfig) validate() error { 242 err := ic.PKCS11.validate() 243 if err != nil { 244 return err 245 } 246 247 // Input fields 248 if ic.Inputs.PublicKeyPath == "" { 249 return errors.New("inputs.public-key-path is required") 250 } 251 if ic.Inputs.IssuerCertificatePath == "" { 252 return errors.New("inputs.issuer-certificate is required") 253 } 254 255 // Output fields 256 err = checkOutputFile(ic.Outputs.CertificatePath, "certificate-path") 257 if err != nil { 258 return err 259 } 260 261 // Certificate profile 262 err = ic.CertProfile.verifyProfile(intermediateCert) 263 if err != nil { 264 return err 265 } 266 267 return nil 268 } 269 270 type crossCertConfig struct { 271 CeremonyType string `yaml:"ceremony-type"` 272 PKCS11 PKCS11SigningConfig `yaml:"pkcs11"` 273 Inputs struct { 274 PublicKeyPath string `yaml:"public-key-path"` 275 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 276 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 277 } `yaml:"inputs"` 278 Outputs struct { 279 CertificatePath string `yaml:"certificate-path"` 280 } `yaml:"outputs"` 281 CertProfile certProfile `yaml:"certificate-profile"` 282 SkipLints []string `yaml:"skip-lints"` 283 } 284 285 func (csc crossCertConfig) validate() error { 286 err := csc.PKCS11.validate() 287 if err != nil { 288 return err 289 } 290 if csc.Inputs.PublicKeyPath == "" { 291 return errors.New("inputs.public-key-path is required") 292 } 293 if csc.Inputs.IssuerCertificatePath == "" { 294 return errors.New("inputs.issuer-certificate is required") 295 } 296 if csc.Inputs.CertificateToCrossSignPath == "" { 297 return errors.New("inputs.certificate-to-cross-sign-path is required") 298 } 299 err = checkOutputFile(csc.Outputs.CertificatePath, "certificate-path") 300 if err != nil { 301 return err 302 } 303 err = csc.CertProfile.verifyProfile(crossCert) 304 if err != nil { 305 return err 306 } 307 308 return nil 309 } 310 311 type csrConfig struct { 312 CeremonyType string `yaml:"ceremony-type"` 313 PKCS11 PKCS11SigningConfig `yaml:"pkcs11"` 314 Inputs struct { 315 PublicKeyPath string `yaml:"public-key-path"` 316 } `yaml:"inputs"` 317 Outputs struct { 318 CSRPath string `yaml:"csr-path"` 319 } `yaml:"outputs"` 320 CertProfile certProfile `yaml:"certificate-profile"` 321 } 322 323 func (cc csrConfig) validate() error { 324 err := cc.PKCS11.validate() 325 if err != nil { 326 return err 327 } 328 329 // Input fields 330 if cc.Inputs.PublicKeyPath == "" { 331 return errors.New("inputs.public-key-path is required") 332 } 333 334 // Output fields 335 err = checkOutputFile(cc.Outputs.CSRPath, "csr-path") 336 if err != nil { 337 return err 338 } 339 340 // Certificate profile 341 err = cc.CertProfile.verifyProfile(requestCert) 342 if err != nil { 343 return err 344 } 345 346 return nil 347 } 348 349 type keyConfig struct { 350 CeremonyType string `yaml:"ceremony-type"` 351 PKCS11 PKCS11KeyGenConfig `yaml:"pkcs11"` 352 Key keyGenConfig `yaml:"key"` 353 Outputs struct { 354 PublicKeyPath string `yaml:"public-key-path"` 355 PKCS11ConfigPath string `yaml:"pkcs11-config-path"` 356 } `yaml:"outputs"` 357 } 358 359 func (kc keyConfig) validate() error { 360 err := kc.PKCS11.validate() 361 if err != nil { 362 return err 363 } 364 365 // Key gen fields 366 err = kc.Key.validate() 367 if err != nil { 368 return err 369 } 370 371 // Output fields 372 err = checkOutputFile(kc.Outputs.PublicKeyPath, "public-key-path") 373 if err != nil { 374 return err 375 } 376 377 return nil 378 } 379 380 type crlConfig struct { 381 CeremonyType string `yaml:"ceremony-type"` 382 PKCS11 PKCS11SigningConfig `yaml:"pkcs11"` 383 Inputs struct { 384 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 385 } `yaml:"inputs"` 386 Outputs struct { 387 CRLPath string `yaml:"crl-path"` 388 } `yaml:"outputs"` 389 CRLProfile struct { 390 ThisUpdate string `yaml:"this-update"` 391 NextUpdate string `yaml:"next-update"` 392 Number int64 `yaml:"number"` 393 RevokedCertificates []struct { 394 CertificatePath string `yaml:"certificate-path"` 395 RevocationDate string `yaml:"revocation-date"` 396 RevocationReason string `yaml:"revocation-reason"` 397 } `yaml:"revoked-certificates"` 398 } `yaml:"crl-profile"` 399 SkipLints []string `yaml:"skip-lints"` 400 } 401 402 func (cc crlConfig) validate() error { 403 err := cc.PKCS11.validate() 404 if err != nil { 405 return err 406 } 407 408 // Input fields 409 if cc.Inputs.IssuerCertificatePath == "" { 410 return errors.New("inputs.issuer-certificate-path is required") 411 } 412 413 // Output fields 414 err = checkOutputFile(cc.Outputs.CRLPath, "crl-path") 415 if err != nil { 416 return err 417 } 418 419 // CRL profile fields 420 if cc.CRLProfile.ThisUpdate == "" { 421 return errors.New("crl-profile.this-update is required") 422 } 423 if cc.CRLProfile.NextUpdate == "" { 424 return errors.New("crl-profile.next-update is required") 425 } 426 if cc.CRLProfile.Number == 0 { 427 return errors.New("crl-profile.number must be non-zero") 428 } 429 for _, rc := range cc.CRLProfile.RevokedCertificates { 430 if rc.CertificatePath == "" { 431 return errors.New("crl-profile.revoked-certificates.certificate-path is required") 432 } 433 if rc.RevocationDate == "" { 434 return errors.New("crl-profile.revoked-certificates.revocation-date is required") 435 } 436 if rc.RevocationReason == "" { 437 return errors.New("crl-profile.revoked-certificates.revocation-reason is required") 438 } 439 } 440 441 return nil 442 } 443 444 // loadCert loads a PEM certificate specified by filename or returns an error. 445 // The public key from the loaded certificate is checked by the GoodKey package. 446 func loadCert(filename string) (*x509.Certificate, error) { 447 certPEM, err := os.ReadFile(filename) 448 if err != nil { 449 return nil, err 450 } 451 log.Printf("Loaded certificate from %s\n", filename) 452 block, _ := pem.Decode(certPEM) 453 if block == nil { 454 return nil, fmt.Errorf("no data in cert PEM file %q", filename) 455 } 456 cert, err := x509.ParseCertificate(block.Bytes) 457 if err != nil { 458 return nil, err 459 } 460 goodkeyErr := kp.GoodKey(context.Background(), cert.PublicKey) 461 if goodkeyErr != nil { 462 return nil, goodkeyErr 463 } 464 465 return cert, nil 466 } 467 468 // publicKeysEqual determines whether two public keys are identical. 469 func publicKeysEqual(a, b crypto.PublicKey) (bool, error) { 470 switch ak := a.(type) { 471 case *rsa.PublicKey: 472 return ak.Equal(b), nil 473 case *ecdsa.PublicKey: 474 return ak.Equal(b), nil 475 default: 476 return false, fmt.Errorf("unsupported public key type %T", ak) 477 } 478 } 479 480 func openSigner(cfg PKCS11SigningConfig, pubKey crypto.PublicKey) (crypto.Signer, *hsmRandReader, error) { 481 session, err := pkcs11helpers.Initialize(cfg.Module, cfg.SigningSlot, cfg.PIN) 482 if err != nil { 483 return nil, nil, fmt.Errorf("failed to setup session and PKCS#11 context for slot %d: %s", 484 cfg.SigningSlot, err) 485 } 486 log.Printf("Opened PKCS#11 session for slot %d\n", cfg.SigningSlot) 487 signer, err := session.NewSigner(cfg.SigningLabel, pubKey) 488 if err != nil { 489 return nil, nil, fmt.Errorf("failed to retrieve private key handle: %s", err) 490 } 491 ok, err := publicKeysEqual(signer.Public(), pubKey) 492 if !ok { 493 return nil, nil, err 494 } 495 496 return signer, newRandReader(session), nil 497 } 498 499 func signAndWriteCert(tbs, issuer *x509.Certificate, lintCert lintCert, subjectPubKey crypto.PublicKey, signer crypto.Signer, certPath string) (*x509.Certificate, error) { 500 if lintCert == nil { 501 return nil, fmt.Errorf("linting was not performed prior to issuance") 502 } 503 // x509.CreateCertificate uses a io.Reader here for signing methods that require 504 // a source of randomness. Since PKCS#11 based signing generates needed randomness 505 // at the HSM we don't need to pass a real reader. Instead of passing a nil reader 506 // we use one that always returns errors in case the internal usage of this reader 507 // changes. 508 certBytes, err := x509.CreateCertificate(&failReader{}, tbs, issuer, subjectPubKey, signer) 509 if err != nil { 510 return nil, fmt.Errorf("failed to create certificate: %s", err) 511 } 512 pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) 513 log.Printf("Signed certificate PEM:\n%s", pemBytes) 514 cert, err := x509.ParseCertificate(certBytes) 515 if err != nil { 516 return nil, fmt.Errorf("failed to parse signed certificate: %s", err) 517 } 518 if tbs == issuer { 519 // If cert is self-signed we need to populate the issuer subject key to 520 // verify the signature 521 issuer.PublicKey = cert.PublicKey 522 issuer.PublicKeyAlgorithm = cert.PublicKeyAlgorithm 523 } 524 err = cert.CheckSignatureFrom(issuer) 525 if err != nil { 526 return nil, fmt.Errorf("failed to verify certificate signature: %s", err) 527 } 528 err = writeFile(certPath, pemBytes) 529 if err != nil { 530 return nil, fmt.Errorf("failed to write certificate to %q: %s", certPath, err) 531 } 532 log.Printf("Certificate written to %q\n", certPath) 533 534 return cert, nil 535 } 536 537 // loadPubKey loads a PEM public key specified by filename. It returns a 538 // crypto.PublicKey, the PEM bytes of the public key, and an error. If an error 539 // exists, no public key or bytes are returned. The public key is checked by the 540 // GoodKey package. 541 func loadPubKey(filename string) (crypto.PublicKey, []byte, error) { 542 keyPEM, err := os.ReadFile(filename) 543 if err != nil { 544 return nil, nil, err 545 } 546 log.Printf("Loaded public key from %s\n", filename) 547 block, _ := pem.Decode(keyPEM) 548 if block == nil { 549 return nil, nil, fmt.Errorf("no data in cert PEM file %q", filename) 550 } 551 key, err := x509.ParsePKIXPublicKey(block.Bytes) 552 if err != nil { 553 return nil, nil, err 554 } 555 err = kp.GoodKey(context.Background(), key) 556 if err != nil { 557 return nil, nil, err 558 } 559 560 return key, block.Bytes, nil 561 } 562 563 func rootCeremony(configBytes []byte) error { 564 var config rootConfig 565 err := strictyaml.Unmarshal(configBytes, &config) 566 if err != nil { 567 return fmt.Errorf("failed to parse config: %s", err) 568 } 569 log.Printf("Preparing root ceremony for %s\n", config.Outputs.CertificatePath) 570 err = config.validate() 571 if err != nil { 572 return fmt.Errorf("failed to validate config: %s", err) 573 } 574 session, err := pkcs11helpers.Initialize(config.PKCS11.Module, config.PKCS11.StoreSlot, config.PKCS11.PIN) 575 if err != nil { 576 return fmt.Errorf("failed to setup session and PKCS#11 context for slot %d: %s", config.PKCS11.StoreSlot, err) 577 } 578 log.Printf("Opened PKCS#11 session for slot %d\n", config.PKCS11.StoreSlot) 579 keyInfo, err := generateKey(session, config.PKCS11.StoreLabel, config.Outputs.PublicKeyPath, config.Key) 580 if err != nil { 581 return err 582 } 583 signer, err := session.NewSigner(config.PKCS11.StoreLabel, keyInfo.key) 584 if err != nil { 585 return fmt.Errorf("failed to retrieve signer: %s", err) 586 } 587 template, err := makeTemplate(newRandReader(session), &config.CertProfile, keyInfo.der, nil, rootCert) 588 if err != nil { 589 return fmt.Errorf("failed to create certificate profile: %s", err) 590 } 591 lintCert, err := issueLintCertAndPerformLinting(template, template, keyInfo.key, signer, config.SkipLints) 592 if err != nil { 593 return err 594 } 595 finalCert, err := signAndWriteCert(template, template, lintCert, keyInfo.key, signer, config.Outputs.CertificatePath) 596 if err != nil { 597 return err 598 } 599 err = postIssuanceLinting(finalCert, config.SkipLints) 600 if err != nil { 601 return err 602 } 603 log.Printf("Post issuance linting completed for %s\n", config.Outputs.CertificatePath) 604 605 return nil 606 } 607 608 func intermediateCeremony(configBytes []byte) error { 609 var config intermediateConfig 610 err := strictyaml.Unmarshal(configBytes, &config) 611 if err != nil { 612 return fmt.Errorf("failed to parse config: %s", err) 613 } 614 log.Printf("Preparing intermediate ceremony for %s\n", config.Outputs.CertificatePath) 615 err = config.validate() 616 if err != nil { 617 return fmt.Errorf("failed to validate config: %s", err) 618 } 619 pub, pubBytes, err := loadPubKey(config.Inputs.PublicKeyPath) 620 if err != nil { 621 return err 622 } 623 issuer, err := loadCert(config.Inputs.IssuerCertificatePath) 624 if err != nil { 625 return fmt.Errorf("failed to load issuer certificate %q: %s", config.Inputs.IssuerCertificatePath, err) 626 } 627 signer, randReader, err := openSigner(config.PKCS11, issuer.PublicKey) 628 if err != nil { 629 return err 630 } 631 template, err := makeTemplate(randReader, &config.CertProfile, pubBytes, nil, intermediateCert) 632 if err != nil { 633 return fmt.Errorf("failed to create certificate profile: %s", err) 634 } 635 template.AuthorityKeyId = issuer.SubjectKeyId 636 lintCert, err := issueLintCertAndPerformLinting(template, issuer, pub, signer, config.SkipLints) 637 if err != nil { 638 return err 639 } 640 finalCert, err := signAndWriteCert(template, issuer, lintCert, pub, signer, config.Outputs.CertificatePath) 641 if err != nil { 642 return err 643 } 644 // Verify that x509.CreateCertificate is deterministic and produced 645 // identical DER bytes between the lintCert and finalCert signing 646 // operations. If this fails it's mississuance, but it's better to know 647 // about the problem sooner than later. 648 if !bytes.Equal(lintCert.RawTBSCertificate, finalCert.RawTBSCertificate) { 649 return fmt.Errorf("mismatch between lintCert and finalCert RawTBSCertificate DER bytes: \"%x\" != \"%x\"", lintCert.RawTBSCertificate, finalCert.RawTBSCertificate) 650 } 651 err = postIssuanceLinting(finalCert, config.SkipLints) 652 if err != nil { 653 return err 654 } 655 log.Printf("Post issuance linting completed for %s\n", config.Outputs.CertificatePath) 656 657 return nil 658 } 659 660 func crossCertCeremony(configBytes []byte) error { 661 var config crossCertConfig 662 err := strictyaml.Unmarshal(configBytes, &config) 663 if err != nil { 664 return fmt.Errorf("failed to parse config: %s", err) 665 } 666 log.Printf("Preparing cross-certificate ceremony for %s\n", config.Outputs.CertificatePath) 667 err = config.validate() 668 if err != nil { 669 return fmt.Errorf("failed to validate config: %s", err) 670 } 671 pub, pubBytes, err := loadPubKey(config.Inputs.PublicKeyPath) 672 if err != nil { 673 return err 674 } 675 issuer, err := loadCert(config.Inputs.IssuerCertificatePath) 676 if err != nil { 677 return fmt.Errorf("failed to load issuer certificate %q: %s", config.Inputs.IssuerCertificatePath, err) 678 } 679 toBeCrossSigned, err := loadCert(config.Inputs.CertificateToCrossSignPath) 680 if err != nil { 681 return fmt.Errorf("failed to load toBeCrossSigned certificate %q: %s", config.Inputs.CertificateToCrossSignPath, err) 682 } 683 signer, randReader, err := openSigner(config.PKCS11, issuer.PublicKey) 684 if err != nil { 685 return err 686 } 687 template, err := makeTemplate(randReader, &config.CertProfile, pubBytes, toBeCrossSigned, crossCert) 688 if err != nil { 689 return fmt.Errorf("failed to create certificate profile: %s", err) 690 } 691 template.AuthorityKeyId = issuer.SubjectKeyId 692 lintCert, err := issueLintCertAndPerformLinting(template, issuer, pub, signer, config.SkipLints) 693 if err != nil { 694 return err 695 } 696 // Ensure that we've configured the correct certificate to cross-sign compared to the profile. 697 // 698 // Example of a misconfiguration below: 699 // ... 700 // inputs: 701 // certificate-to-cross-sign-path: int-e6.cert.pem 702 // certificate-profile: 703 // common-name: (FAKE) E5 704 // organization: (FAKE) Let's Encrypt 705 // ... 706 // 707 if !bytes.Equal(toBeCrossSigned.RawSubject, lintCert.RawSubject) { 708 return fmt.Errorf("mismatch between toBeCrossSigned and lintCert RawSubject DER bytes: \"%x\" != \"%x\"", toBeCrossSigned.RawSubject, lintCert.RawSubject) 709 } 710 // BR 7.1.2.2.1 Cross-Certified Subordinate CA Validity 711 // The earlier of one day prior to the time of signing or the earliest 712 // notBefore date of the existing CA Certificate(s). 713 if lintCert.NotBefore.Before(toBeCrossSigned.NotBefore) { 714 return fmt.Errorf("cross-signed subordinate CA's NotBefore predates the existing CA's NotBefore") 715 } 716 // BR 7.1.2.2.3 Cross-Certified Subordinate CA Extensions 717 // We want the Extended Key Usages of our cross-signs to be identical to those 718 // in the cert being cross-signed, for the sake of consistency. However, our 719 // Root CA Certificates do not contain any EKUs, as required by BR 7.1.2.1.2. 720 // Therefore, cross-signs of our roots count as "unrestricted" cross-signs per 721 // the definition in BR 7.1.2.2.3, and are subject to the requirement that 722 // the cross-sign's Issuer and Subject fields must either: 723 // - have identical organizationNames; or 724 // - have orgnaizationNames which are affiliates of each other. 725 // Therefore, we enforce that cross-signs with empty EKUs have identical 726 // Subject Organization Name fields... or allow one special case where the 727 // issuer is "Internet Security Research Group" and the subject is "ISRG" to 728 // allow us to migrate from the longer string to the shorter one. 729 if !slices.Equal(lintCert.ExtKeyUsage, toBeCrossSigned.ExtKeyUsage) { 730 return fmt.Errorf("lint cert and toBeCrossSigned cert EKUs differ") 731 } 732 if len(lintCert.ExtKeyUsage) == 0 { 733 if !slices.Equal(lintCert.Subject.Organization, issuer.Subject.Organization) && 734 !(slices.Equal(issuer.Subject.Organization, []string{"Internet Security Research Group"}) && slices.Equal(lintCert.Subject.Organization, []string{"ISRG"})) { 735 return fmt.Errorf("attempted unrestricted cross-sign of certificate operated by a different organization") 736 } 737 } 738 // Issue the cross-signed certificate. 739 finalCert, err := signAndWriteCert(template, issuer, lintCert, pub, signer, config.Outputs.CertificatePath) 740 if err != nil { 741 return err 742 } 743 // Verify that x509.CreateCertificate is deterministic and produced 744 // identical DER bytes between the lintCert and finalCert signing 745 // operations. If this fails it's mississuance, but it's better to know 746 // about the problem sooner than later. 747 if !bytes.Equal(lintCert.RawTBSCertificate, finalCert.RawTBSCertificate) { 748 return fmt.Errorf("mismatch between lintCert and finalCert RawTBSCertificate DER bytes: \"%x\" != \"%x\"", lintCert.RawTBSCertificate, finalCert.RawTBSCertificate) 749 } 750 err = postIssuanceLinting(finalCert, config.SkipLints) 751 if err != nil { 752 return err 753 } 754 log.Printf("Post issuance linting completed for %s\n", config.Outputs.CertificatePath) 755 756 return nil 757 } 758 759 func csrCeremony(configBytes []byte) error { 760 var config csrConfig 761 err := strictyaml.Unmarshal(configBytes, &config) 762 if err != nil { 763 return fmt.Errorf("failed to parse config: %s", err) 764 } 765 err = config.validate() 766 if err != nil { 767 return fmt.Errorf("failed to validate config: %s", err) 768 } 769 770 pub, _, err := loadPubKey(config.Inputs.PublicKeyPath) 771 if err != nil { 772 return err 773 } 774 775 signer, _, err := openSigner(config.PKCS11, pub) 776 if err != nil { 777 return err 778 } 779 780 csrDER, err := generateCSR(&config.CertProfile, signer) 781 if err != nil { 782 return fmt.Errorf("failed to generate CSR: %s", err) 783 } 784 csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrDER}) 785 err = writeFile(config.Outputs.CSRPath, csrPEM) 786 if err != nil { 787 return fmt.Errorf("failed to write CSR to %q: %s", config.Outputs.CSRPath, err) 788 } 789 log.Printf("CSR written to %q\n", config.Outputs.CSRPath) 790 791 return nil 792 } 793 794 func keyCeremony(configBytes []byte) error { 795 var config keyConfig 796 err := strictyaml.Unmarshal(configBytes, &config) 797 if err != nil { 798 return fmt.Errorf("failed to parse config: %s", err) 799 } 800 err = config.validate() 801 if err != nil { 802 return fmt.Errorf("failed to validate config: %s", err) 803 } 804 session, err := pkcs11helpers.Initialize(config.PKCS11.Module, config.PKCS11.StoreSlot, config.PKCS11.PIN) 805 if err != nil { 806 return fmt.Errorf("failed to setup session and PKCS#11 context for slot %d: %s", config.PKCS11.StoreSlot, err) 807 } 808 log.Printf("Opened PKCS#11 session for slot %d\n", config.PKCS11.StoreSlot) 809 if _, err = generateKey(session, config.PKCS11.StoreLabel, config.Outputs.PublicKeyPath, config.Key); err != nil { 810 return err 811 } 812 813 if config.Outputs.PKCS11ConfigPath != "" { 814 contents := fmt.Sprintf( 815 `{"module": %q, "tokenLabel": %q, "pin": %q}`, 816 config.PKCS11.Module, config.PKCS11.StoreLabel, config.PKCS11.PIN, 817 ) 818 err = writeFile(config.Outputs.PKCS11ConfigPath, []byte(contents)) 819 if err != nil { 820 return err 821 } 822 } 823 824 return nil 825 } 826 827 func crlCeremony(configBytes []byte) error { 828 var config crlConfig 829 err := strictyaml.Unmarshal(configBytes, &config) 830 if err != nil { 831 return fmt.Errorf("failed to parse config: %s", err) 832 } 833 err = config.validate() 834 if err != nil { 835 return fmt.Errorf("failed to validate config: %s", err) 836 } 837 838 issuer, err := loadCert(config.Inputs.IssuerCertificatePath) 839 if err != nil { 840 return fmt.Errorf("failed to load issuer certificate %q: %s", config.Inputs.IssuerCertificatePath, err) 841 } 842 signer, _, err := openSigner(config.PKCS11, issuer.PublicKey) 843 if err != nil { 844 return err 845 } 846 847 thisUpdate, err := time.Parse(time.DateTime, config.CRLProfile.ThisUpdate) 848 if err != nil { 849 return fmt.Errorf("unable to parse crl-profile.this-update: %s", err) 850 } 851 nextUpdate, err := time.Parse(time.DateTime, config.CRLProfile.NextUpdate) 852 if err != nil { 853 return fmt.Errorf("unable to parse crl-profile.next-update: %s", err) 854 } 855 856 var revokedCertificates []x509.RevocationListEntry 857 for _, rc := range config.CRLProfile.RevokedCertificates { 858 cert, err := loadCert(rc.CertificatePath) 859 if err != nil { 860 return fmt.Errorf("failed to load revoked certificate %q: %s", rc.CertificatePath, err) 861 } 862 if !cert.IsCA { 863 return fmt.Errorf("certificate with serial %d is not a CA certificate", cert.SerialNumber) 864 } 865 revokedAt, err := time.Parse(time.DateTime, rc.RevocationDate) 866 if err != nil { 867 return fmt.Errorf("unable to parse crl-profile.revoked-certificates.revocation-date") 868 } 869 revokedCert := x509.RevocationListEntry{ 870 SerialNumber: cert.SerialNumber, 871 RevocationTime: revokedAt, 872 } 873 reasonCode, err := revocation.StringToReason(rc.RevocationReason) 874 if err != nil { 875 return fmt.Errorf("looking up revocation reason: %w", err) 876 } 877 encReason, err := asn1.Marshal(reasonCode) 878 if err != nil { 879 return fmt.Errorf("failed to marshal revocation reason %d (%q): %s", reasonCode, rc.RevocationReason, err) 880 } 881 revokedCert.Extensions = []pkix.Extension{{ 882 Id: asn1.ObjectIdentifier{2, 5, 29, 21}, // id-ce-reasonCode 883 Value: encReason, 884 }} 885 revokedCertificates = append(revokedCertificates, revokedCert) 886 } 887 888 crlBytes, err := generateCRL(signer, issuer, thisUpdate, nextUpdate, config.CRLProfile.Number, revokedCertificates, config.SkipLints) 889 if err != nil { 890 return err 891 } 892 893 log.Printf("Signed CRL PEM:\n%s", crlBytes) 894 895 err = writeFile(config.Outputs.CRLPath, crlBytes) 896 if err != nil { 897 return fmt.Errorf("failed to write CRL to %q: %s", config.Outputs.CRLPath, err) 898 } 899 900 return nil 901 } 902 903 func main() { 904 configPath := flag.String("config", "", "Path to ceremony configuration file") 905 flag.Parse() 906 907 if *configPath == "" { 908 log.Fatal("--config is required") 909 } 910 configBytes, err := os.ReadFile(*configPath) 911 if err != nil { 912 log.Fatalf("Failed to read config file: %s", err) 913 } 914 var ct struct { 915 CeremonyType string `yaml:"ceremony-type"` 916 } 917 918 // We are intentionally using non-strict unmarshaling to read the top level 919 // tags to populate the "ct" struct for use in the switch statement below. 920 // Further strict processing of each yaml node is done on a case by case basis 921 // inside the switch statement. 922 err = yaml.Unmarshal(configBytes, &ct) 923 if err != nil { 924 log.Fatalf("Failed to parse config: %s", err) 925 } 926 927 switch ct.CeremonyType { 928 case "root": 929 err = rootCeremony(configBytes) 930 if err != nil { 931 log.Fatalf("root ceremony failed: %s", err) 932 } 933 case "cross-certificate": 934 err = crossCertCeremony(configBytes) 935 if err != nil { 936 log.Fatalf("cross-certificate ceremony failed: %s", err) 937 } 938 case "intermediate": 939 err = intermediateCeremony(configBytes) 940 if err != nil { 941 log.Fatalf("intermediate ceremony failed: %s", err) 942 } 943 case "cross-csr": 944 err = csrCeremony(configBytes) 945 if err != nil { 946 log.Fatalf("cross-csr ceremony failed: %s", err) 947 } 948 case "key": 949 err = keyCeremony(configBytes) 950 if err != nil { 951 log.Fatalf("key ceremony failed: %s", err) 952 } 953 case "crl": 954 err = crlCeremony(configBytes) 955 if err != nil { 956 log.Fatalf("crl ceremony failed: %s", err) 957 } 958 default: 959 log.Fatalf("unknown ceremony-type, must be one of: root, cross-certificate, intermediate, cross-csr, key, crl") 960 } 961 }