mkbundle.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // mkbundle is a commandline tool for building certificate pool bundles.
  2. // All certificates in the input file paths are checked for revocation and bundled together.
  3. //
  4. // Usage:
  5. //
  6. // mkbundle -f bundle_file -nw number_of_workers certificate_file_path ...
  7. package main
  8. import (
  9. "crypto/x509"
  10. "encoding/pem"
  11. "flag"
  12. "os"
  13. "path/filepath"
  14. "sync"
  15. "github.com/cloudflare/cfssl/log"
  16. "github.com/cloudflare/cfssl/revoke"
  17. )
  18. // worker does all the parsing and validation of the certificate(s)
  19. // contained in a single file. It first reads all the data in the
  20. // file, then begins parsing certificates in the file. Those
  21. // certificates are then checked for revocation.
  22. func worker(paths chan string, bundler chan *x509.Certificate, pool *sync.WaitGroup) {
  23. defer (*pool).Done()
  24. for {
  25. path, ok := <-paths
  26. if !ok {
  27. return
  28. }
  29. log.Infof("Loading %s", path)
  30. fileData, err := os.ReadFile(path)
  31. if err != nil {
  32. log.Warningf("%v", err)
  33. continue
  34. }
  35. for {
  36. var block *pem.Block
  37. if len(fileData) == 0 {
  38. break
  39. }
  40. block, fileData = pem.Decode(fileData)
  41. if block == nil {
  42. log.Warningf("%s: no PEM data found", path)
  43. break
  44. } else if block.Type != "CERTIFICATE" {
  45. log.Info("Skipping non-certificate")
  46. continue
  47. }
  48. cert, err := x509.ParseCertificate(block.Bytes)
  49. if err != nil {
  50. log.Warningf("Invalid certificate: %v", err)
  51. continue
  52. }
  53. log.Infof("Validating %+v", cert.Subject)
  54. revoked, ok := revoke.VerifyCertificate(cert)
  55. if !ok {
  56. log.Warning("Failed to verify certificate.")
  57. } else if !revoked {
  58. bundler <- cert
  59. } else {
  60. log.Info("Skipping revoked certificate")
  61. }
  62. }
  63. }
  64. }
  65. // supervisor sets up the workers and signals the bundler that all
  66. // certificates have been processed.
  67. func supervisor(paths chan string, bundler chan *x509.Certificate, numWorkers int) {
  68. var workerPool sync.WaitGroup
  69. for i := 0; i < numWorkers; i++ {
  70. workerPool.Add(1)
  71. go worker(paths, bundler, &workerPool)
  72. }
  73. workerPool.Wait()
  74. close(bundler)
  75. }
  76. // makeBundle opens the file for writing, and listens for incoming
  77. // certificates. These are PEM-encoded and written to file.
  78. func makeBundle(filename string, bundler chan *x509.Certificate) {
  79. file, err := os.Create(filename)
  80. if err != nil {
  81. log.Errorf("%v", err)
  82. return
  83. }
  84. defer file.Close()
  85. var total int
  86. for {
  87. cert, ok := <-bundler
  88. if !ok {
  89. break
  90. }
  91. block := &pem.Block{
  92. Type: "CERTIFICATE",
  93. Bytes: cert.Raw,
  94. }
  95. err = pem.Encode(file, block)
  96. if err != nil {
  97. log.Errorf("Failed to write PEM block: %v", err)
  98. break
  99. }
  100. total++
  101. }
  102. log.Infof("Wrote %d certificates.", total)
  103. }
  104. // scanFiles walks the files listed in the arguments. These files may
  105. // be either certificate files or directories containing certificates.
  106. func scanFiles(paths chan string) {
  107. walker := func(path string, info os.FileInfo, err error) error {
  108. log.Infof("Found %s", path)
  109. if err != nil {
  110. return err
  111. }
  112. if info.Mode().IsRegular() {
  113. paths <- path
  114. }
  115. return nil
  116. }
  117. for _, path := range flag.Args() {
  118. err := filepath.Walk(path, walker)
  119. if err != nil {
  120. log.Errorf("Walk failed: %v", err)
  121. }
  122. }
  123. close(paths)
  124. }
  125. func main() {
  126. bundleFile := flag.String("f", "cert-bundle.crt", "path to store certificate bundle")
  127. numWorkers := flag.Int("nw", 4, "number of workers")
  128. flag.Parse()
  129. paths := make(chan string)
  130. bundler := make(chan *x509.Certificate)
  131. go supervisor(paths, bundler, *numWorkers)
  132. go scanFiles(paths)
  133. makeBundle(*bundleFile, bundler)
  134. }