Skip to content

Commit a1e2343

Browse files
committed
Allow issuers to configure specific profiles
1 parent 221c9e7 commit a1e2343

File tree

8 files changed

+157
-230
lines changed

8 files changed

+157
-230
lines changed

ca/ca.go

Lines changed: 39 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"fmt"
1515
"math/big"
1616
mrand "math/rand/v2"
17+
"slices"
1718

1819
ct "github.com/google/certificate-transparency-go"
1920
cttls "github.com/google/certificate-transparency-go/tls"
@@ -63,15 +64,6 @@ type issuanceEvent struct {
6364
Certificate string `json:",omitempty"`
6465
}
6566

66-
// Two maps of keys to Issuers. Lookup by PublicKeyAlgorithm is useful for
67-
// determining the set of issuers which can sign a given (pre)cert, based on its
68-
// PublicKeyAlgorithm. Lookup by NameID is useful for looking up a specific
69-
// issuer based on the issuer of a given (pre)certificate.
70-
type issuerMaps struct {
71-
byAlg map[x509.PublicKeyAlgorithm][]*issuance.Issuer
72-
byNameID map[issuance.NameID]*issuance.Issuer
73-
}
74-
7567
// caMetrics holds various metrics which are shared between caImpl and crlImpl.
7668
type caMetrics struct {
7769
signatureCount *prometheus.CounterVec
@@ -123,11 +115,11 @@ func (m *caMetrics) noteSignError(err error) {
123115
// certificateAuthorityImpl represents a CA that signs certificates.
124116
type certificateAuthorityImpl struct {
125117
capb.UnsafeCertificateAuthorityServer
126-
sa sapb.StorageAuthorityCertificateClient
127-
sctClient rapb.SCTProviderClient
128-
pa core.PolicyAuthority
129-
issuers issuerMaps
130-
certProfiles map[string]*issuance.Profile
118+
sa sapb.StorageAuthorityCertificateClient
119+
sctClient rapb.SCTProviderClient
120+
pa core.PolicyAuthority
121+
issuers []*issuance.Issuer
122+
profiles map[string]*issuance.Profile
131123

132124
// The prefix is prepended to the serial number.
133125
prefix byte
@@ -141,60 +133,14 @@ type certificateAuthorityImpl struct {
141133

142134
var _ capb.CertificateAuthorityServer = (*certificateAuthorityImpl)(nil)
143135

144-
// makeIssuerMaps processes a list of issuers into a set of maps for easy
145-
// lookup either by key algorithm (useful for picking an issuer for a precert)
146-
// or by unique ID (useful for final certs and CRLs). If two issuers with
147-
// the same unique ID are encountered, an error is returned.
148-
func makeIssuerMaps(issuers []*issuance.Issuer) (issuerMaps, error) {
149-
issuersByAlg := make(map[x509.PublicKeyAlgorithm][]*issuance.Issuer, 2)
150-
issuersByNameID := make(map[issuance.NameID]*issuance.Issuer, len(issuers))
151-
for _, issuer := range issuers {
152-
if _, found := issuersByNameID[issuer.NameID()]; found {
153-
return issuerMaps{}, fmt.Errorf("two issuers with same NameID %d (%s) configured", issuer.NameID(), issuer.Name())
154-
}
155-
issuersByNameID[issuer.NameID()] = issuer
156-
if issuer.IsActive() {
157-
issuersByAlg[issuer.KeyType()] = append(issuersByAlg[issuer.KeyType()], issuer)
158-
}
159-
}
160-
if i, ok := issuersByAlg[x509.ECDSA]; !ok || len(i) == 0 {
161-
return issuerMaps{}, errors.New("no ECDSA issuers configured")
162-
}
163-
if i, ok := issuersByAlg[x509.RSA]; !ok || len(i) == 0 {
164-
return issuerMaps{}, errors.New("no RSA issuers configured")
165-
}
166-
return issuerMaps{issuersByAlg, issuersByNameID}, nil
167-
}
168-
169-
// makeCertificateProfilesMap processes a set of named certificate issuance
170-
// profile configs into a map from name to profile.
171-
func makeCertificateProfilesMap(profiles map[string]*issuance.ProfileConfig) (map[string]*issuance.Profile, error) {
172-
if len(profiles) <= 0 {
173-
return nil, fmt.Errorf("must pass at least one certificate profile")
174-
}
175-
176-
profilesByName := make(map[string]*issuance.Profile, len(profiles))
177-
178-
for name, profileConfig := range profiles {
179-
profile, err := issuance.NewProfile(profileConfig)
180-
if err != nil {
181-
return nil, err
182-
}
183-
184-
profilesByName[name] = profile
185-
}
186-
187-
return profilesByName, nil
188-
}
189-
190136
// NewCertificateAuthorityImpl creates a CA instance that can sign certificates
191137
// from any number of issuance.Issuers and for any number of profiles.
192138
func NewCertificateAuthorityImpl(
193139
sa sapb.StorageAuthorityCertificateClient,
194140
sctService rapb.SCTProviderClient,
195141
pa core.PolicyAuthority,
196-
boulderIssuers []*issuance.Issuer,
197-
certificateProfiles map[string]*issuance.ProfileConfig,
142+
issuers []*issuance.Issuer,
143+
profiles map[string]*issuance.Profile,
198144
serialPrefix byte,
199145
maxNames int,
200146
keyPolicy goodkey.KeyPolicy,
@@ -206,33 +152,27 @@ func NewCertificateAuthorityImpl(
206152
return nil, errors.New("serial prefix must be between 0x01 (1) and 0x7f (127)")
207153
}
208154

209-
if len(boulderIssuers) == 0 {
155+
if len(issuers) == 0 {
210156
return nil, errors.New("must have at least one issuer")
211157
}
212158

213-
certProfiles, err := makeCertificateProfilesMap(certificateProfiles)
214-
if err != nil {
215-
return nil, err
216-
}
217-
218-
issuers, err := makeIssuerMaps(boulderIssuers)
219-
if err != nil {
220-
return nil, err
159+
if len(profiles) == 0 {
160+
return nil, errors.New("must have at least one profile")
221161
}
222162

223163
return &certificateAuthorityImpl{
224-
sa: sa,
225-
sctClient: sctService,
226-
pa: pa,
227-
issuers: issuers,
228-
certProfiles: certProfiles,
229-
prefix: serialPrefix,
230-
maxNames: maxNames,
231-
keyPolicy: keyPolicy,
232-
log: logger,
233-
metrics: metrics,
234-
tracer: otel.GetTracerProvider().Tracer("github.com/letsencrypt/boulder/ca"),
235-
clk: clk,
164+
sa: sa,
165+
sctClient: sctService,
166+
pa: pa,
167+
issuers: issuers,
168+
profiles: profiles,
169+
prefix: serialPrefix,
170+
maxNames: maxNames,
171+
keyPolicy: keyPolicy,
172+
log: logger,
173+
metrics: metrics,
174+
tracer: otel.GetTracerProvider().Tracer("github.com/letsencrypt/boulder/ca"),
175+
clk: clk,
236176
}, nil
237177
}
238178

@@ -259,7 +199,7 @@ func (ca *certificateAuthorityImpl) IssueCertificate(ctx context.Context, req *c
259199
return nil, errors.New("IssueCertificate called with a nil SCT service")
260200
}
261201

262-
profile, ok := ca.certProfiles[req.CertProfileName]
202+
profile, ok := ca.profiles[req.CertProfileName]
263203
if !ok {
264204
return nil, fmt.Errorf("incapable of using a profile named %q", req.CertProfileName)
265205
}
@@ -276,7 +216,7 @@ func (ca *certificateAuthorityImpl) IssueCertificate(ctx context.Context, req *c
276216
return nil, err
277217
}
278218

279-
issuer, err := ca.pickIssuer(csr.PublicKeyAlgorithm)
219+
issuer, err := ca.pickIssuer(req.CertProfileName, csr.PublicKeyAlgorithm)
280220
if err != nil {
281221
return nil, err
282222
}
@@ -495,13 +435,22 @@ func (ca *certificateAuthorityImpl) IssueCertificate(ctx context.Context, req *c
495435
}
496436

497437
// pickIssuer returns an issuer which is willing to issue certificates for the
498-
// given public key algorithm. If no such issuer exists, it returns
438+
// given profile and public key algorithm. If no such issuer exists, it returns
499439
// an error. If multiple such issuers exist, it selects one at random.
500-
func (ca *certificateAuthorityImpl) pickIssuer(keyAlg x509.PublicKeyAlgorithm) (*issuance.Issuer, error) {
501-
pool, ok := ca.issuers.byAlg[keyAlg]
440+
func (ca *certificateAuthorityImpl) pickIssuer(profileName string, keyAlg x509.PublicKeyAlgorithm) (*issuance.Issuer, error) {
441+
var pool []*issuance.Issuer
442+
for _, issuer := range ca.issuers {
443+
if issuer.KeyType() != keyAlg {
444+
continue
445+
}
446+
if !slices.Contains(issuer.Profiles(), profileName) {
447+
continue
448+
}
449+
pool = append(pool, issuer)
450+
}
502451

503-
if !ok || len(pool) == 0 {
504-
return nil, fmt.Errorf("no issuer found for key algorithm %s", keyAlg)
452+
if len(pool) == 0 {
453+
return nil, fmt.Errorf("no issuer found for profile %q and key algorithm %s", profileName, keyAlg)
505454
}
506455

507456
return pool[mrand.IntN(len(pool))], nil

0 commit comments

Comments
 (0)