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 }