func main() { flagAddr := flag.String("a", ":8888", "listening address") flagRootFile := flag.String("roots", "", "configuration file specifying root keys") flagDefaultLabel := flag.String("l", "", "specify a default label") flagEndpointCert := flag.String("tls-cert", "", "server certificate") flagEndpointKey := flag.String("tls-key", "", "server private key") flag.IntVar(&log.Level, "loglevel", log.LevelInfo, "log level (0 = DEBUG, 4 = ERROR)") flag.Parse() if *flagRootFile == "" { log.Fatal("no root file specified") } roots, err := config.Parse(*flagRootFile) if err != nil { log.Fatalf("%v", err) } for label, root := range roots { s, err := parseSigner(root) if err != nil { log.Criticalf("%v", err) } signers[label] = s if root.ACL != nil { whitelists[label] = root.ACL } log.Info("loaded signer ", label) } defaultLabel = *flagDefaultLabel initStats() infoHandler, err := info.NewMultiHandler(signers, defaultLabel) if err != nil { log.Criticalf("%v", err) } var localhost = whitelist.NewBasic() localhost.Add(net.ParseIP("127.0.0.1")) localhost.Add(net.ParseIP("::1")) metrics, err := whitelist.NewHandlerFunc(dumpMetrics, metricsDisallowed, localhost) if err != nil { log.Criticalf("failed to set up the metrics whitelist: %v", err) } http.HandleFunc("/api/v1/cfssl/authsign", dispatchRequest) http.Handle("/api/v1/cfssl/info", infoHandler) http.Handle("/api/v1/cfssl/metrics", metrics) if *flagEndpointCert == "" && *flagEndpointKey == "" { log.Info("Now listening on ", *flagAddr) log.Fatal(http.ListenAndServe(*flagAddr, nil)) } else { log.Info("Now listening on https:// ", *flagAddr) log.Fatal(http.ListenAndServeTLS(*flagAddr, *flagEndpointCert, *flagEndpointKey, nil)) } }
// serverMain is the command line entry point to the API server. It sets up a // new HTTP server to handle sign, bundle, and validate requests. func serverMain(args []string, c cli.Config) error { conf = c // serve doesn't support arguments. if len(args) > 0 { return errors.New("argument is provided but not defined; please refer to the usage by flag -h") } bundler.IntermediateStash = conf.IntDir var err error if err = ubiquity.LoadPlatforms(conf.Metadata); err != nil { return err } log.Info("Initializing signer") if s, err = sign.SignerFromConfig(c); err != nil { log.Warningf("couldn't initialize signer: %v", err) } if ocspSigner, err = ocspsign.SignerFromConfig(c); err != nil { log.Warningf("couldn't initialize ocsp signer: %v", err) } registerHandlers() addr := net.JoinHostPort(conf.Address, strconv.Itoa(conf.Port)) log.Info("Now listening on ", addr) return http.ListenAndServe(addr, nil) }
// revCheck should check the certificate for any revocations. It // returns a pair of booleans: the first indicates whether the certificate // is revoked, the second indicates whether the revocations were // successfully checked.. This leads to the following combinations: // // false, false: an error was encountered while checking revocations. // // false, true: the certificate was checked successfully and // it is not revoked. // // true, true: the certificate was checked successfully and // it is revoked. // // true, false: failure to check revocation status causes // verification to fail func revCheck(cert *x509.Certificate) (revoked, ok bool) { for _, url := range cert.CRLDistributionPoints { if ldapURL(url) { log.Infof("skipping LDAP CRL: %s", url) continue } if revoked, ok := certIsRevokedCRL(cert, url); !ok { log.Warning("error checking revocation via CRL") if HardFail { return true, false } return false, false } else if revoked { log.Info("certificate is revoked via CRL") return true, true } if revoked, ok := certIsRevokedOCSP(cert, HardFail); !ok { log.Warning("error checking revocation via OCSP") if HardFail { return true, false } return false, false } else if revoked { log.Info("certificate is revoked via OCSP") return true, true } } return false, true }
func (tr *Transport) getConfig() (*tls.Config, error) { if tr.ClientTrustStore != nil { log.Info("using client auth") return tr.TLSClientAuthServerConfig() } log.Info("not using client auth") return tr.TLSServerConfig() }
// Handle responds to requests for the CA to sign the certificate request // present in the "certificate_request" parameter for the host named // in the "hostname" parameter. The certificate should be PEM-encoded. If // provided, subject information from the "subject" parameter will be used // in place of the subject information from the CSR. func (h *SignHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("signature request received") body, err := ioutil.ReadAll(r.Body) if err != nil { return err } r.Body.Close() var req signer.SignRequest err = json.Unmarshal(body, &req) if err != nil { return err } if len(req.Hosts) == 0 { return errors.NewBadRequestString("missing paratmeter 'hosts'") } if req.Request == "" { return errors.NewBadRequestString("missing parameter 'certificate_request'") } var cert []byte var profile *config.SigningProfile policy := h.signer.Policy() if policy != nil && policy.Profiles != nil && req.Profile != "" { profile = policy.Profiles[req.Profile] } if profile == nil && policy != nil { profile = policy.Default } if profile.Provider != nil { log.Error("profile requires authentication") return errors.NewBadRequestString("authentication required") } cert, err = h.signer.Sign(req) if err != nil { log.Warningf("failed to sign request: %v", err) return err } result := map[string]string{"certificate": string(cert)} log.Info("wrote response") return api.SendResponse(w, result) }
// 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 := ioutil.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") } } } }
// Handle responds to requests for the CA to generate a new private // key and certificate on behalf of the client. The format for these // requests is documented in the API documentation. func (cg *CertGeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("request for CSR") req := new(genSignRequest) body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } key, csr, err := cg.generator.ProcessRequest(req.Request) if err != nil { log.Warningf("failed to process CSR: %v", err) // The validator returns a *cfssl/errors.HttpError return err } certPEM, err := cg.signer.Sign(req.Hostname, csr, req.Profile) if err != nil { log.Warningf("failed to sign certificate: %v", err) return errors.NewBadRequest(err) } result := map[string]string{ "private_key": string(key), "certificate": string(certPEM), } return sendResponse(w, result) }
// initialCAHandler is an HTTP handler that accepts a JSON blob in the // same format as the CSR endpoint; this blob should contain the // identity information for the CA's root key. This endpoint is not // suitable for creating intermediate certificates. func initialCAHandler(w http.ResponseWriter, r *http.Request) error { log.Info("setting up initial CA handler") body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } r.Body.Close() req := new(csr.CertificateRequest) req.KeyRequest = csr.NewBasicKeyRequest() err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } cert, _, key, err := initca.New(req) if err != nil { log.Warningf("failed to initialise new CA: %v", err) return err } response := api.NewSuccessResponse(&NewCA{string(key), string(cert)}) enc := json.NewEncoder(w) err = enc.Encode(response) return err }
// Handle responds to requests for the CA to generate a new private // key and certificate request on behalf of the client. The format for // these requests is documented in the API documentation. func (g *GeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("request for CSR") req := new(csr.CertificateRequest) body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } key, csr, err := g.generator.ProcessRequest(req) if err != nil { log.Warningf("failed to process CSR: %v", err) // The validator returns a *cfssl/errors.HttpError return err } // Both key and csr are returned PEM-encoded. response := newSuccessResponse(&CertRequest{string(key), string(csr)}) w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) err = enc.Encode(response) return err }
// NewCertGeneratorHandler builds a new handler for generating // certificates directly from certificate requests; the validator covers // the certificate request and the CA's key and certificate are used to // sign the generated request. If remote is not an empty string, the // handler will send signature requests to the CFSSL instance contained // in remote. func NewCertGeneratorHandler(validator Validator, caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) { var err error log.Info("setting up new generator / signer") cg := new(CertGeneratorHandler) if policy == nil { policy = &config.Signing{ Default: config.DefaultConfig(), Profiles: nil, } } root := universal.Root{ Config: map[string]string{ "ca-file": caFile, "ca-key-file": caKeyFile, }, } if cg.signer, err = universal.NewSigner(root, policy); err != nil { log.Errorf("setting up signer failed: %v", err) return nil, err } cg.generator = &csr.Generator{Validator: validator} return api.HTTPHandler{Handler: cg, Methods: []string{"POST"}}, nil }
// scanInfoHandler is an HTTP handler that returns a JSON blob result describing // the possible families and scans to be run. func scanInfoHandler(w http.ResponseWriter, r *http.Request) error { log.Info("setting up scaninfo handler") response := api.NewSuccessResponse(scan.Default) enc := json.NewEncoder(w) err := enc.Encode(response) return err }
// Generate creates a new CSR from a CertificateRequest structure and // an existing key. The KeyRequest field is ignored. func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) { sigAlgo := helpers.SignerAlgo(priv, crypto.SHA256) if sigAlgo == x509.UnknownSignatureAlgorithm { return nil, cferr.New(cferr.PrivateKeyError, cferr.Unavailable) } var tpl = x509.CertificateRequest{ Subject: req.Name(), SignatureAlgorithm: sigAlgo, } for i := range req.Hosts { if ip := net.ParseIP(req.Hosts[i]); ip != nil { tpl.IPAddresses = append(tpl.IPAddresses, ip) } else { tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i]) } } csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv) if err != nil { log.Errorf("failed to generate a CSR: %v", err) err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err) return } block := pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csr, } log.Info("encoded CSR") csr = pem.EncodeToMemory(&block) return }
// check a cert against a specific CRL. Returns the same bool pair // as revCheck. func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { crl, ok := CRLSet[url] if ok && crl == nil { ok = false delete(CRLSet, url) } var shouldFetchCRL = true if ok { if !crl.HasExpired(time.Now()) { shouldFetchCRL = false } } if shouldFetchCRL { var err error crl, err = fetchCRL(url) if err != nil { log.Warningf("failed to fetch CRL: %v", err) return false, false } CRLSet[url] = crl } for _, revoked := range crl.TBSCertList.RevokedCertificates { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { log.Info("Serial number match: intermediate is revoked.") return true, true } } return false, true }
func TestOverrideSubject(t *testing.T) { csrPEM, err := ioutil.ReadFile(fullSubjectCSR) if err != nil { t.Fatalf("%v", err) } req := &signer.Subject{ Names: []csr.Name{ {O: "example.net"}, }, } s := newCustomSigner(t, testECDSACaFile, testECDSACaKeyFile) request := signer.SignRequest{ Hosts: []string{"127.0.0.1", "localhost", "*****@*****.**"}, Request: string(csrPEM), Subject: req, } certPEM, err := s.Sign(request) if err != nil { t.Fatalf("%v", err) } cert, err := helpers.ParseCertificatePEM(certPEM) if err != nil { t.Fatalf("%v", err) } block, _ := pem.Decode(csrPEM) template, err := x509.ParseCertificateRequest(block.Bytes) if err != nil { t.Fatal(err.Error()) } if cert.Subject.Organization[0] != "example.net" { t.Fatalf("Failed to override subject: want example.net but have %s", cert.Subject.Organization[0]) } if cert.Subject.Country[0] != template.Subject.Country[0] { t.Fatal("Failed to override Country") } if cert.Subject.Locality[0] != template.Subject.Locality[0] { t.Fatal("Failed to override Locality") } if cert.Subject.Organization[0] == template.Subject.Organization[0] { t.Fatal("Shouldn't have overrode Organization") } if cert.Subject.OrganizationalUnit[0] != template.Subject.OrganizationalUnit[0] { t.Fatal("Failed to override OrganizationalUnit") } log.Info("Overrode subject info") }
func main() { var addr, conf string flag.StringVar(&addr, "a", "127.0.0.1:9876", "`address` of server") flag.StringVar(&conf, "f", "server.json", "config `file` to use") flag.Parse() var id = new(core.Identity) data, err := ioutil.ReadFile(conf) if err != nil { exlib.Err(1, err, "reading config file") } err = json.Unmarshal(data, id) if err != nil { exlib.Err(1, err, "parsing config file") } tr, err := transport.New(exlib.Before, id) if err != nil { exlib.Err(1, err, "creating transport") } l, err := transport.Listen(addr, tr) if err != nil { exlib.Err(1, err, "setting up listener") } var errChan = make(chan error, 0) go func(ec <-chan error) { for { err, ok := <-ec if !ok { log.Warning("error channel closed, future errors will not be reported") break } log.Errorf("auto update error: %v", err) } }(errChan) log.Info("setting up auto-update") go l.AutoUpdate(nil, errChan) log.Info("listening on ", addr) exlib.Warn(serve(l), "serving listener") }
// Handle implements an http.Handler interface for the bundle handler. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error { blob, matched, err := api.ProcessRequestFirstMatchOf(r, [][]string{ {"certificate"}, {"domain"}, }) if err != nil { log.Warningf("invalid request: %v", err) return err } flavor := blob["flavor"] bf := bundler.Ubiquitous if flavor != "" { bf = bundler.BundleFlavor(flavor) } log.Infof("request for flavor %v", bf) var result *bundler.Bundle switch matched[0] { case "domain": bundle, err := h.bundler.BundleFromRemote(blob["domain"], blob["ip"], bf) if err != nil { log.Warningf("couldn't bundle from remote: %v", err) return err } result = bundle case "certificate": bundle, err := h.bundler.BundleFromPEMorDER([]byte(blob["certificate"]), []byte(blob["private_key"]), bf, "") if err != nil { log.Warning("bad PEM certifcate or private key") return err } serverName := blob["domain"] ip := blob["ip"] if serverName != "" { err := bundle.Cert.VerifyHostname(serverName) if err != nil { return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } } if ip != "" { err := bundle.Cert.VerifyHostname(ip) if err != nil { return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } } result = bundle } log.Info("wrote response") return api.SendResponse(w, result) }
// NewHandler builds a new Handler from the // validation function provided. func NewHandler(validator Validator) (http.Handler, error) { log.Info("setting up key / CSR generator") return &api.HTTPHandler{ Handler: &Handler{ generator: &csr.Generator{Validator: validator}, }, Methods: []string{"POST"}, }, nil }
// Handle responds to requests for the CA to sign the certificate // present in the "cert" parameter for the host named in the "hostname" // parameter. The certificate should be PEM-encoded. func (h *SignHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("signature request received") blob, err := processRequestRequired(r, []string{"hostname", "certificate_request"}) if err != nil { return err } certificate := []byte(blob["certificate_request"]) cert, err := h.signer.Sign(blob["hostname"], certificate, blob["profile"]) if err != nil { log.Warningf("failed to sign request: %v", err) return errors.NewBadRequest(err) } result := map[string]string{"certificate": string(cert)} log.Info("wrote response") return sendResponse(w, result) }
// Handle responds to requests for the CA to sign the certificate request // present in the "certificate_request" parameter for the host named // in the "hostname" parameter. The certificate should be PEM-encoded. If // provided, subject information from the "subject" parameter will be used // in place of the subject information from the CSR. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("signature request received") body, err := ioutil.ReadAll(r.Body) if err != nil { return err } r.Body.Close() var req jsonSignRequest err = json.Unmarshal(body, &req) if err != nil { return errors.NewBadRequestString("Unable to parse sign request") } signReq := jsonReqToTrue(req) if req.Request == "" { return errors.NewBadRequestString("missing parameter 'certificate_request'") } var cert []byte profile, err := signer.Profile(h.signer, req.Profile) if err != nil { return err } if profile.Provider != nil { log.Error("profile requires authentication") return errors.NewBadRequestString("authentication required") } cert, err = h.signer.Sign(signReq) if err != nil { log.Warningf("failed to sign request: %v", err) return err } result := map[string]string{"certificate": string(cert)} log.Info("wrote response") return api.SendResponse(w, result) }
func NewBundleHandler(caBundleFile, intBundleFile string) (http.Handler, error) { var err error b := new(BundlerHandler) if b.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile); err != nil { return nil, err } log.Info("bundler API ready") return HttpHandler{b, "POST"}, nil }
// NewGeneratorHandler builds a new GeneratorHandler from the // validation function provided. func NewCertGeneratorHandler(validator Validator, caFile, caKeyFile string) (http.Handler, error) { var err error log.Info("setting up new generator / signer") cg := new(CertGeneratorHandler) if cg.signer, err = signer.NewSigner(caFile, caKeyFile, nil); err != nil { return nil, err } cg.generator = &csr.Generator{validator} return HttpHandler{cg, "POST"}, nil }
func NewRemoteCertGenerator(validator Validator, remote string) (http.Handler, error) { log.Info("setting up a new remote certificate generator") cg := new(RemoteCertGeneratorHandler) if cg.remote = client.NewServer(remote); cg.remote == nil { log.Errorf("invalid address for remote server") return nil, errors.New(errors.DialError, errors.Unknown, nil) } cg.generator = &csr.Generator{validator} return HttpHandler{cg, "POST"}, nil }
func initializeServer() *server.Server { var hosts string fmt.Print("Keyserver Hostnames/IPs (comma-seperated): ") fmt.Scanln(&hosts) hostnames := strings.Split(hosts, ",") csr, key, err := csr.ParseRequest(&csr.CertificateRequest{ CN: "Keyless Server Authentication Certificate", Hosts: hostnames, KeyRequest: &csr.BasicKeyRequest{ A: "ecdsa", S: 384, }, }) if err != nil { log.Fatal(err) } if err := ioutil.WriteFile(keyFile, key, 0400); err != nil { log.Fatal(err) } log.Infof("Key generated and saved to %s\n", keyFile) log.Info("Server entering initialization state") s, err := server.NewServerFromFile(initCertFile, initKeyFile, caFile, net.JoinHostPort("", port), net.JoinHostPort("", metricsPort)) if err != nil { log.Fatal(err) } s.ActivationToken = []byte(initToken) go func() { log.Fatal(s.ListenAndServe()) }() cert, err := initAPICall(hostnames, string(csr)) if err != nil { log.Fatal(err) } if err := ioutil.WriteFile(certFile, cert, 0644); err != nil { log.Fatal(err) } log.Infof("Cert saved to %s\n", certFile) // Remove server from activation state and initialize issued certificate. s.ActivationToken = s.ActivationToken[:0] tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { log.Fatal(err) } s.Config.Certificates = []tls.Certificate{tlsCert} return s }
// NewHandler creates a new bundler that uses the root bundle and // intermediate bundle in the trust chain. func NewHandler(caBundleFile, intBundleFile string) (http.Handler, error) { var err error b := new(Handler) if b.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile); err != nil { return nil, err } log.Info("bundler API ready") return api.HTTPHandler{Handler: b, Methods: []string{"POST"}}, nil }
// ocspServerMain is the command line entry point to the OCSP responder. // It sets up a new HTTP server that responds to OCSP requests. func ocspServerMain(args []string, c cli.Config) error { // serve doesn't support arguments. if len(args) > 0 { return errors.New("argument is provided but not defined; please refer to the usage by flag -h") } if c.Responses == "" { return errors.New("no response file provided, please set the -responses flag") } src, err := ocsp.NewSourceFromFile(c.Responses) if err != nil { return errors.New("unable to read response file") } log.Info("Registering OCSP responder handler") http.Handle(c.Path, ocsp.Responder{Source: src}) addr := fmt.Sprintf("%s:%d", c.Address, c.Port) log.Info("Now listening on ", addr) return http.ListenAndServe(addr, nil) }
// registerHandlers instantiates various handlers and associate them to corresponding endpoints. func registerHandlers() { for path, getHandler := range endpoints { path = v1APIPath(path) log.Infof("Setting up '%s' endpoint", path) if handler, err := getHandler(); err != nil { log.Warningf("endpoint '%s' is disabled: %v", path, err) } else { http.Handle(path, handler) } } log.Info("Handler set up complete.") }
func serve(l net.Listener) error { defer l.Close() for { conn, err := l.Accept() if err != nil { exlib.Warn(err, "client connection failed") continue } log.Info("connection from ", conn.RemoteAddr()) go connHandler(conn) } }
// check a cert against a specific CRL. Returns the same bool pair // as revCheck. func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { crl, ok := CRLSet[url] if ok && crl == nil { ok = false crlLock.Lock() delete(CRLSet, url) crlLock.Unlock() } var shouldFetchCRL = true if ok { if !crl.HasExpired(time.Now()) { shouldFetchCRL = false } } issuer := getIssuer(cert) if shouldFetchCRL { var err error crl, err = fetchCRL(url) if err != nil { log.Warningf("failed to fetch CRL: %v", err) return false, false } // check CRL signature if issuer != nil { err = issuer.CheckCRLSignature(crl) if err != nil { log.Warningf("failed to verify CRL: %v", err) return false, false } } crlLock.Lock() CRLSet[url] = crl crlLock.Unlock() } for _, revoked := range crl.TBSCertList.RevokedCertificates { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { log.Info("Serial number match: intermediate is revoked.") return true, true } } return false, true }
// ProcessRequest validates and processes the incoming request. It is // a wrapper around a validator and the ParseRequest function. func (g *Generator) ProcessRequest(req *CertificateRequest) (csr, key []byte, err error) { log.Info("generate received request") err = g.Validator(req) if err != nil { log.Warningf("invalid request: %v", err) return } csr, key, err = ParseRequest(req) if err != nil { return nil, nil, err } return }
func (b *Bundler) verifyChain(chain []*fetchedIntermediate) bool { // This process will verify if the root of the (partial) chain is in our root pool, // and will fail otherwise. log.Debugf("verifying chain") for vchain := chain[:]; len(vchain) > 0; vchain = vchain[1:] { cert := vchain[0] // If this is a certificate in one of the pools, skip it. if b.KnownIssuers[string(cert.Cert.Signature)] { log.Debugf("certificate is known") continue } _, err := cert.Cert.Verify(b.VerifyOptions()) if err != nil { log.Debugf("certificate failed verification: %v", err) return false } else if len(chain) == len(vchain) && isChainRootNode(cert.Cert) { // The first certificate in the chain is a root; it shouldn't be stored. log.Debug("looking at root certificate, will not store") continue } // leaf cert has an empty name, don't store leaf cert. if cert.Name == "" { continue } log.Debug("add certificate to intermediate pool:", cert.Name) b.IntermediatePool.AddCert(cert.Cert) b.KnownIssuers[string(cert.Cert.Signature)] = true if IntermediateStash != "" { fileName := filepath.Join(IntermediateStash, cert.Name) var block = pem.Block{Type: "CERTIFICATE", Bytes: cert.Cert.Raw} log.Debugf("write intermediate to stash directory: %s", fileName) // If the write fails, verification should not fail. err = ioutil.WriteFile(fileName, pem.EncodeToMemory(&block), 0644) if err != nil { log.Errorf("failed to write new intermediate: %v", err) } else { log.Info("stashed new intermediate ", cert.Name) } } } return true }