123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- // mkbundle is a commandline tool for building certificate pool bundles.
- // All certificates in the input file paths are checked for revocation and bundled together.
- //
- // Usage:
- //
- // mkbundle -f bundle_file -nw number_of_workers certificate_file_path ...
- package main
- import (
- "crypto/x509"
- "encoding/pem"
- "flag"
- "os"
- "path/filepath"
- "sync"
- "github.com/cloudflare/cfssl/log"
- "github.com/cloudflare/cfssl/revoke"
- )
- // worker does all the parsing and validation of the certificate(s)
- // contained in a single file. It first reads all the data in the
- // file, then begins parsing certificates in the file. Those
- // certificates are then checked for revocation.
- func worker(paths chan string, bundler chan *x509.Certificate, pool *sync.WaitGroup) {
- defer (*pool).Done()
- for {
- path, ok := <-paths
- if !ok {
- return
- }
- log.Infof("Loading %s", path)
- fileData, err := os.ReadFile(path)
- if err != nil {
- log.Warningf("%v", err)
- continue
- }
- for {
- var block *pem.Block
- if len(fileData) == 0 {
- break
- }
- block, fileData = pem.Decode(fileData)
- if block == nil {
- log.Warningf("%s: no PEM data found", path)
- break
- } else if block.Type != "CERTIFICATE" {
- log.Info("Skipping non-certificate")
- continue
- }
- cert, err := x509.ParseCertificate(block.Bytes)
- if err != nil {
- log.Warningf("Invalid certificate: %v", err)
- continue
- }
- log.Infof("Validating %+v", cert.Subject)
- revoked, ok := revoke.VerifyCertificate(cert)
- if !ok {
- log.Warning("Failed to verify certificate.")
- } else if !revoked {
- bundler <- cert
- } else {
- log.Info("Skipping revoked certificate")
- }
- }
- }
- }
- // supervisor sets up the workers and signals the bundler that all
- // certificates have been processed.
- func supervisor(paths chan string, bundler chan *x509.Certificate, numWorkers int) {
- var workerPool sync.WaitGroup
- for i := 0; i < numWorkers; i++ {
- workerPool.Add(1)
- go worker(paths, bundler, &workerPool)
- }
- workerPool.Wait()
- close(bundler)
- }
- // makeBundle opens the file for writing, and listens for incoming
- // certificates. These are PEM-encoded and written to file.
- func makeBundle(filename string, bundler chan *x509.Certificate) {
- file, err := os.Create(filename)
- if err != nil {
- log.Errorf("%v", err)
- return
- }
- defer file.Close()
- var total int
- for {
- cert, ok := <-bundler
- if !ok {
- break
- }
- block := &pem.Block{
- Type: "CERTIFICATE",
- Bytes: cert.Raw,
- }
- err = pem.Encode(file, block)
- if err != nil {
- log.Errorf("Failed to write PEM block: %v", err)
- break
- }
- total++
- }
- log.Infof("Wrote %d certificates.", total)
- }
- // scanFiles walks the files listed in the arguments. These files may
- // be either certificate files or directories containing certificates.
- func scanFiles(paths chan string) {
- walker := func(path string, info os.FileInfo, err error) error {
- log.Infof("Found %s", path)
- if err != nil {
- return err
- }
- if info.Mode().IsRegular() {
- paths <- path
- }
- return nil
- }
- for _, path := range flag.Args() {
- err := filepath.Walk(path, walker)
- if err != nil {
- log.Errorf("Walk failed: %v", err)
- }
- }
- close(paths)
- }
- func main() {
- bundleFile := flag.String("f", "cert-bundle.crt", "path to store certificate bundle")
- numWorkers := flag.Int("nw", 4, "number of workers")
- flag.Parse()
- paths := make(chan string)
- bundler := make(chan *x509.Certificate)
- go supervisor(paths, bundler, *numWorkers)
- go scanFiles(paths)
- makeBundle(*bundleFile, bundler)
- }
|