github.com/letsencrypt/boulder@v0.20251208.0/cmd/ceremony/cert.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/sha256"
     6  	"crypto/x509"
     7  	"crypto/x509/pkix"
     8  	"encoding/asn1"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"math/big"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  )
    17  
    18  type policyInfoConfig struct {
    19  	OID string
    20  }
    21  
    22  // certProfile contains the information required to generate a certificate
    23  type certProfile struct {
    24  	// SignatureAlgorithm should contain one of the allowed signature algorithms
    25  	// in AllowedSigAlgs
    26  	SignatureAlgorithm string `yaml:"signature-algorithm"`
    27  
    28  	// CommonName should contain the requested subject common name
    29  	CommonName string `yaml:"common-name"`
    30  	// Organization should contain the requested subject organization
    31  	Organization string `yaml:"organization"`
    32  	// Country should contain the requested subject country code
    33  	Country string `yaml:"country"`
    34  
    35  	// NotBefore should contain the requested NotBefore date for the
    36  	// certificate in the format "2006-01-02 15:04:05". Dates will
    37  	// always be UTC.
    38  	NotBefore string `yaml:"not-before"`
    39  	// NotAfter should contain the requested NotAfter date for the
    40  	// certificate in the format "2006-01-02 15:04:05". Dates will
    41  	// always be UTC.
    42  	NotAfter string `yaml:"not-after"`
    43  
    44  	// CRLURL should contain the URL at which CRLs for this certificate
    45  	// can be found
    46  	CRLURL string `yaml:"crl-url"`
    47  	// IssuerURL should contain the URL at which the issuing certificate
    48  	// can be found, this is only required if generating an intermediate
    49  	// certificate
    50  	IssuerURL string `yaml:"issuer-url"`
    51  
    52  	// Policies should contain any OIDs to be inserted in a certificate
    53  	// policies extension. It should be empty for Root certs, and contain the
    54  	// BRs "domain-validated" Reserved Policy Identifier for Intermediates.
    55  	Policies []policyInfoConfig `yaml:"policies"`
    56  
    57  	// KeyUsages should contain the set of key usage bits to set
    58  	KeyUsages []string `yaml:"key-usages"`
    59  }
    60  
    61  // AllowedSigAlgs contains the allowed signature algorithms
    62  var AllowedSigAlgs = map[string]x509.SignatureAlgorithm{
    63  	"SHA256WithRSA":   x509.SHA256WithRSA,
    64  	"SHA384WithRSA":   x509.SHA384WithRSA,
    65  	"SHA512WithRSA":   x509.SHA512WithRSA,
    66  	"ECDSAWithSHA256": x509.ECDSAWithSHA256,
    67  	"ECDSAWithSHA384": x509.ECDSAWithSHA384,
    68  	"ECDSAWithSHA512": x509.ECDSAWithSHA512,
    69  }
    70  
    71  type certType int
    72  
    73  const (
    74  	rootCert certType = iota
    75  	intermediateCert
    76  	crossCert
    77  	requestCert
    78  )
    79  
    80  // Subject returns a pkix.Name from the appropriate certProfile fields
    81  func (profile *certProfile) Subject() pkix.Name {
    82  	return pkix.Name{
    83  		CommonName:   profile.CommonName,
    84  		Organization: []string{profile.Organization},
    85  		Country:      []string{profile.Country},
    86  	}
    87  }
    88  
    89  func (profile *certProfile) verifyProfile(ct certType) error {
    90  	if ct == requestCert {
    91  		if profile.NotBefore != "" {
    92  			return errors.New("not-before cannot be set for a CSR")
    93  		}
    94  		if profile.NotAfter != "" {
    95  			return errors.New("not-after cannot be set for a CSR")
    96  		}
    97  		if profile.SignatureAlgorithm != "" {
    98  			return errors.New("signature-algorithm cannot be set for a CSR")
    99  		}
   100  		if profile.CRLURL != "" {
   101  			return errors.New("crl-url cannot be set for a CSR")
   102  		}
   103  		if profile.IssuerURL != "" {
   104  			return errors.New("issuer-url cannot be set for a CSR")
   105  		}
   106  		if profile.Policies != nil {
   107  			return errors.New("policies cannot be set for a CSR")
   108  		}
   109  		if profile.KeyUsages != nil {
   110  			return errors.New("key-usages cannot be set for a CSR")
   111  		}
   112  	} else {
   113  		if profile.NotBefore == "" {
   114  			return errors.New("not-before is required")
   115  		}
   116  		if profile.NotAfter == "" {
   117  			return errors.New("not-after is required")
   118  		}
   119  		if profile.SignatureAlgorithm == "" {
   120  			return errors.New("signature-algorithm is required")
   121  		}
   122  	}
   123  	if profile.CommonName == "" {
   124  		return errors.New("common-name is required")
   125  	}
   126  	if profile.Organization == "" {
   127  		return errors.New("organization is required")
   128  	}
   129  	if profile.Country == "" {
   130  		return errors.New("country is required")
   131  	}
   132  
   133  	if ct == rootCert {
   134  		if len(profile.Policies) != 0 {
   135  			return errors.New("policies should not be set on root certs")
   136  		}
   137  	}
   138  
   139  	if ct == intermediateCert || ct == crossCert {
   140  		if profile.CRLURL == "" {
   141  			return errors.New("crl-url is required for subordinate CAs")
   142  		}
   143  		if profile.IssuerURL == "" {
   144  			return errors.New("issuer-url is required for subordinate CAs")
   145  		}
   146  
   147  		// BR 7.1.2.10.5 CA Certificate Certificate Policies
   148  		// OID 2.23.140.1.2.1 is CABF BRs Domain Validated
   149  		if len(profile.Policies) != 1 || profile.Policies[0].OID != "2.23.140.1.2.1" {
   150  			return errors.New("policy should be exactly BRs domain-validated for subordinate CAs")
   151  		}
   152  	}
   153  
   154  	return nil
   155  }
   156  
   157  func parseOID(oidStr string) (asn1.ObjectIdentifier, error) {
   158  	var oid asn1.ObjectIdentifier
   159  	for a := range strings.SplitSeq(oidStr, ".") {
   160  		i, err := strconv.Atoi(a)
   161  		if err != nil {
   162  			return nil, err
   163  		}
   164  		if i <= 0 {
   165  			return nil, errors.New("OID components must be >= 1")
   166  		}
   167  		oid = append(oid, i)
   168  	}
   169  	return oid, nil
   170  }
   171  
   172  var stringToKeyUsage = map[string]x509.KeyUsage{
   173  	"Digital Signature": x509.KeyUsageDigitalSignature,
   174  	"CRL Sign":          x509.KeyUsageCRLSign,
   175  	"Cert Sign":         x509.KeyUsageCertSign,
   176  }
   177  
   178  func generateSKID(pk []byte) ([]byte, error) {
   179  	var pkixPublicKey struct {
   180  		Algo      pkix.AlgorithmIdentifier
   181  		BitString asn1.BitString
   182  	}
   183  	if _, err := asn1.Unmarshal(pk, &pkixPublicKey); err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	// RFC 7093 Section 2 Additional Methods for Generating Key Identifiers: The
   188  	// keyIdentifier [may be] composed of the leftmost 160-bits of the SHA-256
   189  	// hash of the value of the BIT STRING subjectPublicKey (excluding the tag,
   190  	// length, and number of unused bits).
   191  	skid := sha256.Sum256(pkixPublicKey.BitString.Bytes)
   192  	return skid[0:20:20], nil
   193  }
   194  
   195  // makeTemplate generates the certificate template for use in x509.CreateCertificate
   196  func makeTemplate(randReader io.Reader, profile *certProfile, pubKey []byte, tbcs *x509.Certificate, ct certType) (*x509.Certificate, error) {
   197  	// Handle "unrestricted" vs "restricted" subordinate CA profile specifics.
   198  	if ct == crossCert && tbcs == nil {
   199  		return nil, fmt.Errorf("toBeCrossSigned cert field was nil, but was required to gather EKUs for the lint cert")
   200  	}
   201  
   202  	var crlDistributionPoints []string
   203  	if profile.CRLURL != "" {
   204  		crlDistributionPoints = []string{profile.CRLURL}
   205  	}
   206  	var issuingCertificateURL []string
   207  	if profile.IssuerURL != "" {
   208  		issuingCertificateURL = []string{profile.IssuerURL}
   209  	}
   210  
   211  	subjectKeyID, err := generateSKID(pubKey)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	serial := make([]byte, 16)
   217  	_, err = randReader.Read(serial)
   218  	if err != nil {
   219  		return nil, fmt.Errorf("failed to generate serial number: %s", err)
   220  	}
   221  
   222  	var ku x509.KeyUsage
   223  	for _, kuStr := range profile.KeyUsages {
   224  		kuBit, ok := stringToKeyUsage[kuStr]
   225  		if !ok {
   226  			return nil, fmt.Errorf("unknown key usage %q", kuStr)
   227  		}
   228  		ku |= kuBit
   229  	}
   230  	if ku == 0 {
   231  		return nil, errors.New("at least one key usage must be set")
   232  	}
   233  
   234  	cert := &x509.Certificate{
   235  		SerialNumber:          big.NewInt(0).SetBytes(serial),
   236  		BasicConstraintsValid: true,
   237  		IsCA:                  true,
   238  		Subject:               profile.Subject(),
   239  		CRLDistributionPoints: crlDistributionPoints,
   240  		IssuingCertificateURL: issuingCertificateURL,
   241  		KeyUsage:              ku,
   242  		SubjectKeyId:          subjectKeyID,
   243  	}
   244  
   245  	if ct != requestCert {
   246  		sigAlg, ok := AllowedSigAlgs[profile.SignatureAlgorithm]
   247  		if !ok {
   248  			return nil, fmt.Errorf("unsupported signature algorithm %q", profile.SignatureAlgorithm)
   249  		}
   250  		cert.SignatureAlgorithm = sigAlg
   251  		notBefore, err := time.Parse(time.DateTime, profile.NotBefore)
   252  		if err != nil {
   253  			return nil, err
   254  		}
   255  		notAfter, err := time.Parse(time.DateTime, profile.NotAfter)
   256  		if err != nil {
   257  			return nil, err
   258  		}
   259  		validity := notAfter.Add(time.Second).Sub(notBefore)
   260  		if ct == rootCert && validity >= 9132*24*time.Hour {
   261  			// The value 9132 comes directly from the BRs, where it is described
   262  			// as "approximately 25 years". It's equal to 365 * 25 + 7, to allow
   263  			// for some leap years.
   264  			return nil, fmt.Errorf("root cert validity too large: %s >= 25 years", validity)
   265  		} else if (ct == intermediateCert || ct == crossCert) && validity >= 8*365*24*time.Hour {
   266  			// Our CP/CPS states "at most 8 years", so we calculate that number
   267  			// in the most conservative way (i.e. not accounting for leap years)
   268  			// to give ourselves a buffer.
   269  			return nil, fmt.Errorf("subordinate CA cert validity too large: %s >= 8 years", validity)
   270  		}
   271  		cert.NotBefore = notBefore
   272  		cert.NotAfter = notAfter
   273  	}
   274  
   275  	switch ct {
   276  	// rootCert does not get EKU or MaxPathZero.
   277  	// 		BR 7.1.2.1.2 Root CA Extensions
   278  	// 		Extension 	Presence 	Critical 	Description
   279  	// 		extKeyUsage 	MUST NOT 	N 	-
   280  	case requestCert, intermediateCert:
   281  		// id-kp-serverAuth is included in intermediate certificates, as required by
   282  		// Section 7.1.2.10.6 of the CA/BF Baseline Requirements.
   283  		// id-kp-clientAuth is excluded, as required by section 3.2.1 of the Chrome
   284  		// Root Program Requirements.
   285  		cert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
   286  		cert.MaxPathLenZero = true
   287  	case crossCert:
   288  		cert.ExtKeyUsage = tbcs.ExtKeyUsage
   289  		cert.MaxPathLenZero = tbcs.MaxPathLenZero
   290  		// The SKID needs to match the previous SKID, no matter how it was computed.
   291  		cert.SubjectKeyId = tbcs.SubjectKeyId
   292  	}
   293  
   294  	for _, policyConfig := range profile.Policies {
   295  		x509OID, err := x509.ParseOID(policyConfig.OID)
   296  		if err != nil {
   297  			return nil, fmt.Errorf("failed to parse %s as OID: %w", policyConfig.OID, err)
   298  		}
   299  		cert.Policies = append(cert.Policies, x509OID)
   300  	}
   301  
   302  	return cert, nil
   303  }
   304  
   305  // failReader exists to be passed to x509.CreateCertificate which requires
   306  // a source of randomness for signing methods that require a source of
   307  // randomness. Since HSM based signing will generate its own randomness
   308  // we don't need a real reader. Instead of passing a nil reader we use one
   309  // that always returns errors in case the internal usage of this reader
   310  // changes.
   311  type failReader struct{}
   312  
   313  func (fr *failReader) Read([]byte) (int, error) {
   314  	return 0, errors.New("empty reader used by x509.CreateCertificate")
   315  }
   316  
   317  func generateCSR(profile *certProfile, signer crypto.Signer) ([]byte, error) {
   318  	csrDER, err := x509.CreateCertificateRequest(&failReader{}, &x509.CertificateRequest{
   319  		Subject: profile.Subject(),
   320  	}, signer)
   321  	if err != nil {
   322  		return nil, fmt.Errorf("failed to create and sign CSR: %s", err)
   323  	}
   324  	return csrDER, nil
   325  }