crl-checker: a simple tool to validate public CRLs (#6381)

Add crl-checker, a simple tool which downloads, parses, lints,
and validates signatures on a list of CRLs. It takes its input in
the form of a JSON Array of Sharded CRL URLs, the exact
same format as we will be disclosing in CCADB.

We can add additional checks -- such as ensuring that a set of
known-revoked serials are present, or checking that all of the
downloaded CRLs are "recent enough" -- over time.
This commit is contained in:
Aaron Gable 2022-09-14 16:41:51 -07:00 committed by GitHub
parent d53c90a3bc
commit aed604294e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 3 deletions

View File

@ -19,6 +19,7 @@ import (
_ "github.com/letsencrypt/boulder/cmd/ceremony"
_ "github.com/letsencrypt/boulder/cmd/cert-checker"
_ "github.com/letsencrypt/boulder/cmd/contact-auditor"
_ "github.com/letsencrypt/boulder/cmd/crl-checker"
_ "github.com/letsencrypt/boulder/cmd/crl-storer"
_ "github.com/letsencrypt/boulder/cmd/crl-updater"
_ "github.com/letsencrypt/boulder/cmd/expiration-mailer"

81
cmd/crl-checker/main.go Normal file
View File

@ -0,0 +1,81 @@
package notmain
import (
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/crl/crl_x509"
"github.com/letsencrypt/boulder/issuance"
"github.com/letsencrypt/boulder/linter"
crlint "github.com/letsencrypt/boulder/linter/lints/crl"
)
func validateShard(url string, issuer *issuance.Certificate) error {
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("downloading crl: %w", err)
}
crlBytes, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("reading CRL bytes: %w", err)
}
crl, err := crl_x509.ParseRevocationList(crlBytes)
if err != nil {
return fmt.Errorf("parsing CRL: %w", err)
}
err = linter.ProcessResultSet(crlint.LintCRL(crl))
if err != nil {
return fmt.Errorf("linting CRL: %w", err)
}
err = crl.CheckSignatureFrom(issuer.Certificate)
if err != nil {
return fmt.Errorf("checking CRL signature: %w", err)
}
return nil
}
func main() {
urlFile := flag.String("crls", "", "path to a file containing a JSON Array of CRL URLs")
issuerFile := flag.String("issuer", "", "path to an issuer certificate on disk")
flag.Parse()
logger := cmd.NewLogger(cmd.SyslogConfig{StdoutLevel: 6, SyslogLevel: -1})
urlFileContents, err := os.ReadFile(*urlFile)
cmd.FailOnError(err, "Reading CRL URLs file")
var urls []string
err = json.Unmarshal(urlFileContents, &urls)
cmd.FailOnError(err, "Parsing JSON Array of CRL URLs")
issuer, err := issuance.LoadCertificate(*issuerFile)
cmd.FailOnError(err, "Loading issuer certificate")
errCount := 0
for _, url := range urls {
err = validateShard(url, issuer)
if err != nil {
errCount += 1
logger.Errf("CRL %q failed: %s\n", url, err)
}
}
if errCount != 0 {
cmd.Fail(fmt.Sprintf("Encountered %d errors", errCount))
}
logger.AuditInfo("All CRLs validated")
}
func init() {
cmd.RegisterCommand("crl-checker", main)
}

View File

@ -76,7 +76,7 @@ func (l Linter) Check(tbs *x509.Certificate, subjectPubKey crypto.PublicKey) err
return err
}
lintRes := zlint.LintCertificateEx(cert, l.registry)
return processResultSet(lintRes)
return ProcessResultSet(lintRes)
}
// CheckCRL signs the given RevocationList template using the Linter's fake
@ -88,7 +88,7 @@ func (l Linter) CheckCRL(tbs *crl_x509.RevocationList) error {
return err
}
lintRes := crllints.LintCRL(crl)
return processResultSet(lintRes)
return ProcessResultSet(lintRes)
}
func makeSigner(realSigner crypto.Signer) (crypto.Signer, error) {
@ -190,7 +190,7 @@ func makeLintCert(tbs *x509.Certificate, subjectPubKey crypto.PublicKey, issuer
return lintCert, nil
}
func processResultSet(lintRes *zlint.ResultSet) error {
func ProcessResultSet(lintRes *zlint.ResultSet) error {
if lintRes.NoticesPresent || lintRes.WarningsPresent || lintRes.ErrorsPresent || lintRes.FatalsPresent {
var failedLints []string
for lintName, result := range lintRes.Results {