github.com/letsencrypt/boulder@v0.20251208.0/issuance/issuer.go (about) 1 package issuance 2 3 import ( 4 "crypto" 5 "crypto/ecdsa" 6 "crypto/elliptic" 7 "crypto/rsa" 8 "crypto/x509" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "math/big" 13 "os" 14 "slices" 15 "strings" 16 17 "github.com/jmhodges/clock" 18 19 "github.com/letsencrypt/boulder/core" 20 "github.com/letsencrypt/boulder/linter" 21 "github.com/letsencrypt/boulder/privatekey" 22 "github.com/letsencrypt/pkcs11key/v4" 23 ) 24 25 // ----- Name ID ----- 26 27 // NameID is a statistically-unique small ID which can be computed from 28 // both CA and end-entity certs to link them together into a validation chain. 29 // It is computed as a truncated hash over the issuer Subject Name bytes, or 30 // over the end-entity's Issuer Name bytes, which are required to be equal. 31 type NameID int64 32 33 // SubjectNameID returns the NameID (a truncated hash over the raw bytes of a 34 // Distinguished Name) of this issuer certificate's Subject. Useful for storing 35 // as a lookup key in contexts that don't expect hash collisions. 36 func SubjectNameID(ic *Certificate) NameID { 37 return truncatedHash(ic.RawSubject) 38 } 39 40 // IssuerNameID returns the IssuerNameID (a truncated hash over the raw bytes 41 // of the Issuer Distinguished Name) of the given end-entity certificate. 42 // Useful for performing lookups in contexts that don't expect hash collisions. 43 func IssuerNameID(ee *x509.Certificate) NameID { 44 return truncatedHash(ee.RawIssuer) 45 } 46 47 // truncatedHash computes a truncated SHA1 hash across arbitrary bytes. Uses 48 // SHA1 because that is the algorithm most commonly used in OCSP requests. 49 // PURPOSEFULLY NOT EXPORTED. Exists only to ensure that the implementations of 50 // SubjectNameID() and IssuerNameID() never diverge. Use those instead. 51 func truncatedHash(name []byte) NameID { 52 h := crypto.SHA1.New() 53 h.Write(name) 54 s := h.Sum(nil) 55 return NameID(big.NewInt(0).SetBytes(s[:7]).Int64()) 56 } 57 58 // ----- Issuer Certificates ----- 59 60 // Certificate embeds an *x509.Certificate and represents the added semantics 61 // that this certificate is a CA certificate. 62 type Certificate struct { 63 *x509.Certificate 64 // nameID is stored here simply for the sake of precomputation. 65 nameID NameID 66 } 67 68 // NameID is equivalent to SubjectNameID(ic), but faster because it is 69 // precomputed. 70 func (ic *Certificate) NameID() NameID { 71 return ic.nameID 72 } 73 74 // NewCertificate wraps an in-memory cert in an issuance.Certificate, marking it 75 // as an issuer cert. It may fail if the certificate does not contain the 76 // attributes expected of an issuer certificate. 77 func NewCertificate(ic *x509.Certificate) (*Certificate, error) { 78 if !ic.IsCA { 79 return nil, errors.New("certificate is not a CA certificate") 80 } 81 82 res := Certificate{ic, 0} 83 res.nameID = SubjectNameID(&res) 84 return &res, nil 85 } 86 87 func LoadCertificate(path string) (*Certificate, error) { 88 cert, err := core.LoadCert(path) 89 if err != nil { 90 return nil, fmt.Errorf("loading issuer certificate: %w", err) 91 } 92 return NewCertificate(cert) 93 } 94 95 // LoadChain takes a list of filenames containing pem-formatted certificates, 96 // and returns a chain representing all of those certificates in order. It 97 // ensures that the resulting chain is valid. The final file is expected to be 98 // a root certificate, which the chain will be verified against, but which will 99 // not be included in the resulting chain. 100 func LoadChain(certFiles []string) ([]*Certificate, error) { 101 if len(certFiles) < 2 { 102 return nil, errors.New( 103 "each chain must have at least two certificates: an intermediate and a root") 104 } 105 106 // Pre-load all the certificates to make validation easier. 107 certs := make([]*Certificate, len(certFiles)) 108 var err error 109 for i := range len(certFiles) { 110 certs[i], err = LoadCertificate(certFiles[i]) 111 if err != nil { 112 return nil, fmt.Errorf("failed to load certificate %q: %w", certFiles[i], err) 113 } 114 } 115 116 // Iterate over all certs except for the last, checking that their signature 117 // comes from the next cert in the list. 118 chain := make([]*Certificate, len(certFiles)-1) 119 for i := range len(certs) - 1 { 120 err = certs[i].CheckSignatureFrom(certs[i+1].Certificate) 121 if err != nil { 122 return nil, fmt.Errorf("failed to verify signature from %q to %q (%q to %q): %w", 123 certs[i+1].Subject, certs[i].Subject, certFiles[i+1], certFiles[i], err) 124 } 125 chain[i] = certs[i] 126 } 127 128 // Verify that the last cert is self-signed. 129 lastCert := certs[len(certs)-1] 130 err = lastCert.CheckSignatureFrom(lastCert.Certificate) 131 if err != nil { 132 return nil, fmt.Errorf( 133 "final cert in chain (%q; %q) must be self-signed (used only for validation): %w", 134 lastCert.Subject, certFiles[len(certFiles)-1], err) 135 } 136 137 return chain, nil 138 } 139 140 // ----- Issuers with Signers ----- 141 142 // IssuerConfig describes the constraints on and URLs used by a single issuer. 143 type IssuerConfig struct { 144 // Active determines if the issuer can be used to sign precertificates. All 145 // issuers, regardless of this field, can be used to sign final certificates 146 // (for which an issuance token is presented) and CRLs. 147 // All Active issuers of a given key type (RSA or ECDSA) are part of a pool 148 // and each precertificate will be issued randomly from a selected pool. 149 // The selection of which pool depends on the precertificate's key algorithm. 150 Active bool 151 152 // Profiles is the list of profiles for which this issuer is willing to issue. 153 // The names listed here must match the names of configured profiles (see 154 // cmd/ca/main.go's Config.Issuance.CertProfiles and issuance/cert.go's 155 // ProfileConfig). Can be empty if the issuer is not Active. 156 Profiles []string `validate:"required_if=Active true,dive,alphanum,min=1,max=32"` 157 158 IssuerURL string `validate:"required,url"` 159 CRLURLBase string `validate:"required,url,startswith=http://,endswith=/"` 160 161 // Number of CRL shards. Must be positive, but can be 1 for no sharding. 162 CRLShards int `validate:"required,min=1"` 163 164 Location IssuerLoc 165 } 166 167 // IssuerLoc describes the on-disk location and parameters that an issuer 168 // should use to retrieve its certificate and private key. 169 // Only one of File, ConfigFile, or PKCS11 should be set. 170 type IssuerLoc struct { 171 // A file from which a private key will be read and parsed. 172 File string `validate:"required_without_all=ConfigFile PKCS11"` 173 // A file from which a pkcs11key.Config will be read and parsed, if File is not set. 174 ConfigFile string `validate:"required_without_all=PKCS11 File"` 175 // An in-memory pkcs11key.Config, which will be used if ConfigFile is not set. 176 PKCS11 *pkcs11key.Config `validate:"required_without_all=ConfigFile File"` 177 // A file from which a certificate will be read and parsed. 178 CertFile string `validate:"required"` 179 // Number of sessions to open with the HSM. For maximum performance, 180 // this should be equal to the number of cores in the HSM. Defaults to 1. 181 NumSessions int 182 } 183 184 // Issuer is capable of issuing new certificates. 185 type Issuer struct { 186 // TODO(#7159): make Cert, Signer, and Linter private when all signing ops 187 // are handled through this package (e.g. the CA doesn't need direct access 188 // while signing CRLs anymore). 189 Cert *Certificate 190 Signer crypto.Signer 191 Linter *linter.Linter 192 193 keyAlg x509.PublicKeyAlgorithm 194 sigAlg x509.SignatureAlgorithm 195 active bool 196 197 // Used to set the Authority Information Access caIssuers URL in issued 198 // certificates. 199 issuerURL string 200 // Used to set the Issuing Distribution Point extension in issued CRLs 201 // and the CRL Distribution Point extension in issued certs. 202 crlURLBase string 203 204 crlShards int 205 206 // profiles is a list of the names of profiles that this issuer is willing to 207 // issue for. 208 profiles []string 209 210 clk clock.Clock 211 } 212 213 // newIssuer constructs a new Issuer from the in-memory certificate and signer. 214 // It exists as a helper for LoadIssuer to make testing simpler. 215 func newIssuer(config IssuerConfig, cert *Certificate, signer crypto.Signer, clk clock.Clock) (*Issuer, error) { 216 var keyAlg x509.PublicKeyAlgorithm 217 var sigAlg x509.SignatureAlgorithm 218 switch k := cert.PublicKey.(type) { 219 case *rsa.PublicKey: 220 keyAlg = x509.RSA 221 sigAlg = x509.SHA256WithRSA 222 case *ecdsa.PublicKey: 223 keyAlg = x509.ECDSA 224 switch k.Curve { 225 case elliptic.P256(): 226 sigAlg = x509.ECDSAWithSHA256 227 case elliptic.P384(): 228 sigAlg = x509.ECDSAWithSHA384 229 default: 230 return nil, fmt.Errorf("unsupported ECDSA curve: %q", k.Curve.Params().Name) 231 } 232 default: 233 return nil, errors.New("unsupported issuer key type") 234 } 235 236 if config.IssuerURL == "" { 237 return nil, errors.New("issuer URL is required") 238 } 239 if config.CRLURLBase == "" { 240 return nil, errors.New("crlURLBase is required") 241 } 242 if !strings.HasPrefix(config.CRLURLBase, "http://") { 243 return nil, fmt.Errorf("crlURLBase must use HTTP scheme, got %q", config.CRLURLBase) 244 } 245 if !strings.HasSuffix(config.CRLURLBase, "/") { 246 return nil, fmt.Errorf("crlURLBase must end with exactly one forward slash, got %q", config.CRLURLBase) 247 } 248 if config.CRLShards <= 0 { 249 return nil, errors.New("number of CRL shards is required") 250 } 251 252 // We require that all of our issuers be capable of both issuing certs and 253 // providing revocation information. 254 if cert.KeyUsage&x509.KeyUsageCertSign == 0 { 255 return nil, errors.New("end-entity signing cert does not have keyUsage certSign") 256 } 257 if cert.KeyUsage&x509.KeyUsageCRLSign == 0 { 258 return nil, errors.New("end-entity signing cert does not have keyUsage crlSign") 259 } 260 if cert.KeyUsage&x509.KeyUsageDigitalSignature == 0 { 261 return nil, errors.New("end-entity signing cert does not have keyUsage digitalSignature") 262 } 263 264 // If the issuer is active, it must have at least one profile configured. 265 if config.Active && len(config.Profiles) == 0 { 266 return nil, errors.New("active issuers must have at least one profile") 267 } 268 269 lintSigner, err := linter.New(cert.Certificate, signer) 270 if err != nil { 271 return nil, fmt.Errorf("creating fake lint signer: %w", err) 272 } 273 274 i := &Issuer{ 275 Cert: cert, 276 Signer: signer, 277 Linter: lintSigner, 278 keyAlg: keyAlg, 279 sigAlg: sigAlg, 280 active: config.Active, 281 issuerURL: config.IssuerURL, 282 crlURLBase: config.CRLURLBase, 283 crlShards: config.CRLShards, 284 profiles: config.Profiles, 285 clk: clk, 286 } 287 return i, nil 288 } 289 290 // KeyType returns either x509.RSA or x509.ECDSA, depending on whether the 291 // issuer has an RSA or ECDSA keypair. This is useful for determining which 292 // issuance requests should be routed to this issuer. 293 func (i *Issuer) KeyType() x509.PublicKeyAlgorithm { 294 return i.keyAlg 295 } 296 297 // IsActive is true if the issuer is willing to issue precertificates, and false 298 // if the issuer is only willing to issue final certificates and CRLs. 299 func (i *Issuer) IsActive() bool { 300 return i.active 301 } 302 303 // Name provides the Common Name specified in the issuer's certificate. 304 func (i *Issuer) Name() string { 305 return i.Cert.Subject.CommonName 306 } 307 308 // NameID provides the NameID of the issuer's certificate. 309 func (i *Issuer) NameID() NameID { 310 return i.Cert.NameID() 311 } 312 313 // Profiles returns the set of profiles that this issuer can issue for. 314 func (i *Issuer) Profiles() []string { 315 return slices.Clone(i.profiles) 316 } 317 318 // LoadIssuer constructs a new Issuer, loading its certificate from disk and its 319 // private key material from the indicated location. It also verifies that the 320 // issuer metadata (such as AIA URLs) is well-formed. 321 func LoadIssuer(config IssuerConfig, clk clock.Clock) (*Issuer, error) { 322 issuerCert, err := LoadCertificate(config.Location.CertFile) 323 if err != nil { 324 return nil, err 325 } 326 327 signer, err := loadSigner(config.Location, issuerCert.PublicKey) 328 if err != nil { 329 return nil, err 330 } 331 332 if !core.KeyDigestEquals(signer.Public(), issuerCert.PublicKey) { 333 return nil, fmt.Errorf("issuer key did not match issuer cert %q", config.Location.CertFile) 334 } 335 336 return newIssuer(config, issuerCert, signer, clk) 337 } 338 339 func loadSigner(location IssuerLoc, pubkey crypto.PublicKey) (crypto.Signer, error) { 340 if location.File == "" && location.ConfigFile == "" && location.PKCS11 == nil { 341 return nil, errors.New("must supply File, ConfigFile, or PKCS11") 342 } 343 344 if location.File != "" { 345 signer, _, err := privatekey.Load(location.File) 346 if err != nil { 347 return nil, err 348 } 349 return signer, nil 350 } 351 352 var pkcs11Config *pkcs11key.Config 353 if location.ConfigFile != "" { 354 contents, err := os.ReadFile(location.ConfigFile) 355 if err != nil { 356 return nil, err 357 } 358 pkcs11Config = new(pkcs11key.Config) 359 err = json.Unmarshal(contents, pkcs11Config) 360 if err != nil { 361 return nil, err 362 } 363 } else { 364 pkcs11Config = location.PKCS11 365 } 366 367 if pkcs11Config.Module == "" || 368 pkcs11Config.TokenLabel == "" || 369 pkcs11Config.PIN == "" { 370 return nil, fmt.Errorf("missing a field in pkcs11Config %#v", pkcs11Config) 371 } 372 373 numSessions := location.NumSessions 374 if numSessions <= 0 { 375 numSessions = 1 376 } 377 378 return pkcs11key.NewPool(numSessions, pkcs11Config.Module, 379 pkcs11Config.TokenLabel, pkcs11Config.PIN, pubkey) 380 }