Skip to content

Commit f7b79d0

Browse files
authored
Generate self-signed lint certs when linting roots (#6994)
Our linting system uses a throwaway key to sign an untrusted version of the to-be-signed cert, then runs the lints over that. But this means that, when linting a self-signed cert, the signature no longer matches the embedded public key. This in turn causes a bunch of zlint's checks to think they're linting a Subordinate CA cert, rather than a Root CA cert. Change our linting system to make the lint cert appear self-signed when the input cert is intended to be self-signed.
1 parent 158f62b commit f7b79d0

File tree

3 files changed

+30
-25
lines changed

3 files changed

+30
-25
lines changed

linter/linter.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/zmap/zlint/v3"
1414
"github.com/zmap/zlint/v3/lint"
1515

16+
"github.com/letsencrypt/boulder/core"
1617
"github.com/letsencrypt/boulder/crl/crl_x509"
1718

1819
_ "github.com/letsencrypt/boulder/linter/lints/cabf_br"
@@ -25,15 +26,18 @@ var ErrLinting = fmt.Errorf("failed lint(s)")
2526

2627
// Check accomplishes the entire process of linting: it generates a throwaway
2728
// signing key, uses that to create a throwaway cert, and runs a default set
28-
// of lints (everything except for the ETSI and EV lints) against it. This is
29+
// of lints (everything except for the ETSI and EV lints) against it. If the
30+
// subjectPubKey and realSigner indicate that this is a self-signed cert, the
31+
// throwaway cert will have its pubkey replaced to also be self-signed. This is
2932
// the primary public interface of this package, but it can be inefficient;
3033
// creating a new signer and a new lint registry are expensive operations which
31-
// performance-sensitive clients may want to cache.
34+
// performance-sensitive clients may want to cache via linter.New().
3235
func Check(tbs *x509.Certificate, subjectPubKey crypto.PublicKey, realIssuer *x509.Certificate, realSigner crypto.Signer, skipLints []string) error {
3336
linter, err := New(realIssuer, realSigner, skipLints)
3437
if err != nil {
3538
return err
3639
}
40+
3741
_, err = linter.Check(tbs, subjectPubKey)
3842
return err
3943
}
@@ -52,9 +56,10 @@ func CheckCRL(tbs *crl_x509.RevocationList, realIssuer *x509.Certificate, realSi
5256
// public key matches the throwaway private key, and then running the resulting
5357
// throwaway certificate through a registry of zlint lints.
5458
type Linter struct {
55-
issuer *x509.Certificate
56-
signer crypto.Signer
57-
registry lint.Registry
59+
issuer *x509.Certificate
60+
signer crypto.Signer
61+
registry lint.Registry
62+
realPubKey crypto.PublicKey
5863
}
5964

6065
// New constructs a Linter. It uses the provided real certificate and signer
@@ -75,23 +80,37 @@ func New(realIssuer *x509.Certificate, realSigner crypto.Signer, skipLints []str
7580
if err != nil {
7681
return nil, err
7782
}
78-
return &Linter{lintIssuer, lintSigner, reg}, nil
83+
return &Linter{lintIssuer, lintSigner, reg, realSigner.Public()}, nil
7984
}
8085

8186
// Check signs the given TBS certificate using the Linter's fake issuer cert and
8287
// private key, then runs the resulting certificate through all non-filtered
83-
// lints. It returns an error if any lint fails. On success it also returns the
84-
// DER bytes of the linting certificate.
88+
// lints. If the subjectPubKey is identical to the public key of the real signer
89+
// used to create this linter, then the throwaway cert will have its pubkey
90+
// replaced with the linter's pubkey so that it appears self-signed. It returns
91+
// an error if any lint fails. On success it also returns the DER bytes of the
92+
// linting certificate.
8593
func (l Linter) Check(tbs *x509.Certificate, subjectPubKey crypto.PublicKey) ([]byte, error) {
86-
lintCertBytes, cert, err := makeLintCert(tbs, subjectPubKey, l.issuer, l.signer)
94+
lintPubKey := subjectPubKey
95+
selfSigned, err := core.PublicKeysEqual(subjectPubKey, l.realPubKey)
96+
if err != nil {
97+
return nil, err
98+
}
99+
if selfSigned {
100+
lintPubKey = l.signer.Public()
101+
}
102+
103+
lintCertBytes, cert, err := makeLintCert(tbs, lintPubKey, l.issuer, l.signer)
87104
if err != nil {
88105
return nil, err
89106
}
107+
90108
lintRes := zlint.LintCertificateEx(cert, l.registry)
91109
err = ProcessResultSet(lintRes)
92110
if err != nil {
93111
return nil, err
94112
}
113+
95114
return lintCertBytes, nil
96115
}
97116

test/cert-ceremonies/root-ceremony-ecdsa.yaml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,5 @@ certificate-profile:
2121
- Cert Sign
2222
- CRL Sign
2323
skip-lints:
24-
- e_ext_authority_key_identifier_missing
25-
- e_ext_authority_key_identifier_no_key_identifier
26-
- e_sub_ca_aia_missing
27-
- e_sub_ca_certificate_policies_missing
28-
- e_sub_ca_crl_distribution_points_missing
24+
# Our roots don't sign OCSP, so they don't need the Digital Signature KU.
2925
- n_ca_digital_signature_not_set
30-
- n_mp_allowed_eku
31-
- n_sub_ca_eku_missing
32-
- w_sub_ca_aia_does_not_contain_issuing_ca_url

test/cert-ceremonies/root-ceremony-rsa.yaml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,5 @@ certificate-profile:
2121
- Cert Sign
2222
- CRL Sign
2323
skip-lints:
24-
- e_ext_authority_key_identifier_missing
25-
- e_ext_authority_key_identifier_no_key_identifier
26-
- e_sub_ca_aia_missing
27-
- e_sub_ca_certificate_policies_missing
28-
- e_sub_ca_crl_distribution_points_missing
24+
# Our roots don't sign OCSP, so they don't need the Digital Signature KU.
2925
- n_ca_digital_signature_not_set
30-
- n_mp_allowed_eku
31-
- n_sub_ca_eku_missing
32-
- w_sub_ca_aia_does_not_contain_issuing_ca_url

0 commit comments

Comments
 (0)