// 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 }
// 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{ []string{"certificate"}, []string{"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) }
// 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") } } } }
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") }
func parsePrivateKeySpec(spec string, cfg map[string]string) (crypto.Signer, error) { specURL, err := url.Parse(spec) if err != nil { return nil, err } var priv crypto.Signer switch specURL.Scheme { case "file": // A file spec will be parsed such that the root // directory of a relative path will be stored as the // hostname, and the remainder of the file's path is // stored in the Path field. log.Debug("loading private key file", specURL.Path) path := filepath.Join(specURL.Host, specURL.Path) in, err := ioutil.ReadFile(path) if err != nil { return nil, err } log.Debug("attempting to load PEM-encoded private key") priv, err = helpers.ParsePrivateKeyPEM(in) if err != nil { log.Debug("file is not a PEM-encoded private key") log.Debug("attempting to load DER-encoded private key") priv, err = derhelpers.ParsePrivateKeyDER(in) if err != nil { return nil, err } } log.Debug("loaded private key") return priv, nil case "rofile": log.Warning("Red October support is currently experimental") path := filepath.Join(specURL.Host, specURL.Path) in, err := ioutil.ReadFile(path) if err != nil { return nil, err } roServer := cfg["ro_server"] if roServer == "" { return nil, errors.New("config: no RedOctober server available") } // roCAPath can be empty; if it is, the client uses // the system default CA roots. roCAPath := cfg["ro_ca"] roUser := cfg["ro_user"] if roUser == "" { return nil, errors.New("config: no RedOctober user available") } roPass := cfg["ro_pass"] if roPass == "" { return nil, errors.New("config: no RedOctober passphrase available") } log.Debug("decrypting key via RedOctober Server") roClient, err := client.NewRemoteServer(roServer, roCAPath) if err != nil { return nil, err } req := core.DecryptRequest{ Name: roUser, Password: roPass, Data: in, } in, err = roClient.DecryptIntoData(req) if err != nil { return nil, err } return priv, nil default: return nil, ErrUnsupportedScheme } }
func gencertMain(args []string, c cli.Config) error { if c.RenewCA { log.Infof("re-generate a CA certificate from CA cert and key") cert, err := initca.RenewFromPEM(c.CAFile, c.CAKeyFile) if err != nil { log.Errorf("%v\n", err) return err } cli.PrintCert(nil, nil, cert) return nil } csrJSONFile, args, err := cli.PopFirstArgument(args) if err != nil { return err } csrJSONFileBytes, err := cli.ReadStdin(csrJSONFile) if err != nil { return err } req := csr.CertificateRequest{ KeyRequest: csr.NewBasicKeyRequest(), } err = json.Unmarshal(csrJSONFileBytes, &req) if err != nil { return err } switch { case c.IsCA: var key, csrPEM, cert []byte if c.CAKeyFile != "" { log.Infof("re-generate a CA certificate from CSR and CA key") cert, csrPEM, err = initca.NewFromPEM(&req, c.CAKeyFile) if err != nil { log.Errorf("%v\n", err) return err } } else { log.Infof("generating a new CA key and certificate from CSR") cert, csrPEM, key, err = initca.New(&req) if err != nil { return err } } cli.PrintCert(key, csrPEM, cert) default: if req.CA != nil { err = errors.New("ca section only permitted in initca") return err } // Remote can be forced on the command line or in the config if c.Remote == "" && c.CFG == nil { if c.CAFile == "" { log.Error("need a CA certificate (provide one with -ca)") return nil } if c.CAKeyFile == "" { log.Error("need a CA key (provide one with -ca-key)") return nil } } var key, csrBytes []byte g := &csr.Generator{Validator: genkey.Validator} csrBytes, key, err = g.ProcessRequest(&req) if err != nil { key = nil return err } s, err := sign.SignerFromConfig(c) if err != nil { return err } var cert []byte req := signer.SignRequest{ Request: string(csrBytes), Hosts: signer.SplitHosts(c.Hostname), Profile: c.Profile, Label: c.Label, } cert, err = s.Sign(req) if err != nil { return err } // This follows the Baseline Requirements for the Issuance and // Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser // Forum (https://cabforum.org). Specifically, section 10.2.3 ("Information // Requirements"), states: // // "Applicant information MUST include, but not be limited to, at least one // Fully-Qualified Domain Name or IP address to be included in the Certificate’s // SubjectAltName extension." if len(req.Hosts) == 0 { log.Warning(generator.CSRNoHostMessage) } cli.PrintCert(key, csrBytes, cert) } return nil }
func metricsDisallowed(w http.ResponseWriter, req *http.Request) { log.Warning("attempt to access metrics endpoint from external address ", req.RemoteAddr) http.NotFound(w, req) }
// 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) req.Request = csr.New() 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) } if req.Request == nil { log.Warning("empty request received") return errors.NewBadRequestString("missing request section") } if req.Request.CA != nil { log.Warningf("request received with CA section") return errors.NewBadRequestString("ca section only permitted in initca") } csr, key, 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 } signReq := signer.SignRequest{ Request: string(csr), Profile: req.Profile, Label: req.Label, } certBytes, err := cg.signer.Sign(signReq) if err != nil { log.Warningf("failed to sign request: %v", err) return err } reqSum, err := computeSum(csr) if err != nil { return errors.NewBadRequest(err) } certSum, err := computeSum(certBytes) if err != nil { return errors.NewBadRequest(err) } result := map[string]interface{}{ "private_key": string(key), "certificate_request": string(csr), "certificate": string(certBytes), "sums": map[string]Sum{ "certificate_request": reqSum, "certificate": certSum, }, } if len(req.Request.Hosts) == 0 { return api.SendResponseWithMessage(w, result, CSRNoHostMessage, errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode) } return api.SendResponse(w, result) }
// Bundle takes an X509 certificate (already in the // Certificate structure), a private key as crypto.Signer in one of the appropriate // formats (i.e. *rsa.PrivateKey or *ecdsa.PrivateKey, or even a opaque key), using them to // build a certificate bundle. func (b *Bundler) Bundle(certs []*x509.Certificate, key crypto.Signer, flavor BundleFlavor) (*Bundle, error) { log.Infof("bundling certificate for %+v", certs[0].Subject) if len(certs) == 0 { return nil, nil } // Detect reverse ordering of the cert chain. if len(certs) > 1 && !partialVerify(certs) { rcerts := reverse(certs) if partialVerify(rcerts) { certs = rcerts } } var ok bool cert := certs[0] if key != nil { switch { case cert.PublicKeyAlgorithm == x509.RSA: var rsaPublicKey *rsa.PublicKey if rsaPublicKey, ok = key.Public().(*rsa.PublicKey); !ok { return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch) } if cert.PublicKey.(*rsa.PublicKey).N.Cmp(rsaPublicKey.N) != 0 { return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch) } case cert.PublicKeyAlgorithm == x509.ECDSA: var ecdsaPublicKey *ecdsa.PublicKey if ecdsaPublicKey, ok = key.Public().(*ecdsa.PublicKey); !ok { return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch) } if cert.PublicKey.(*ecdsa.PublicKey).X.Cmp(ecdsaPublicKey.X) != 0 { return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch) } default: return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC) } } else { switch { case cert.PublicKeyAlgorithm == x509.RSA: case cert.PublicKeyAlgorithm == x509.ECDSA: default: return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC) } } bundle := new(Bundle) bundle.Cert = cert bundle.Key = key bundle.Issuer = &cert.Issuer bundle.Subject = &cert.Subject bundle.buildHostnames() if flavor == Force { // force bundle checks the certificates // forms a verification chain. if !partialVerify(certs) { return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, goerr.New("Unable to verify the certificate chain")) } bundle.Chain = certs } else { // disallow self-signed cert if cert.CheckSignatureFrom(cert) == nil { return nil, errors.New(errors.CertificateError, errors.SelfSigned) } // verify and store input intermediates to the intermediate pool. // Ignore the returned error here, will treat it in the second call. b.fetchIntermediates(certs) chains, err := cert.Verify(b.VerifyOptions()) if err != nil { log.Debugf("verification failed: %v", err) // If the error was an unknown authority, try to fetch // the intermediate specified in the AIA and add it to // the intermediates bundle. switch err := err.(type) { case x509.UnknownAuthorityError: // Do nothing -- have the default case return out. default: return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } log.Debugf("searching for intermediates via AIA issuer") err = b.fetchIntermediates(certs) if err != nil { log.Debugf("search failed: %v", err) return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } log.Debugf("verifying new chain") chains, err = cert.Verify(b.VerifyOptions()) if err != nil { log.Debugf("failed to verify chain: %v", err) return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } log.Debugf("verify ok") } var matchingChains [][]*x509.Certificate switch flavor { case Optimal: matchingChains = optimalChains(chains) case Ubiquitous: if len(ubiquity.Platforms) == 0 { log.Warning("No metadata, Ubiquitous falls back to Optimal.") } matchingChains = ubiquitousChains(chains) default: matchingChains = ubiquitousChains(chains) } bundle.Chain = matchingChains[0] } statusCode := int(errors.Success) var messages []string // Check if bundle is expiring. expiringCerts := checkExpiringCerts(bundle.Chain) bundle.Expires = helpers.ExpiryTime(bundle.Chain) if len(expiringCerts) > 0 { statusCode |= errors.BundleExpiringBit messages = append(messages, expirationWarning(expiringCerts)) } // Check if bundle contains SHA2 certs. if ubiquity.ChainHashUbiquity(bundle.Chain) <= ubiquity.SHA2Ubiquity { statusCode |= errors.BundleNotUbiquitousBit messages = append(messages, sha2Warning) } // Check if bundle contains ECDSA signatures. if ubiquity.ChainKeyAlgoUbiquity(bundle.Chain) <= ubiquity.ECDSA256Ubiquity { statusCode |= errors.BundleNotUbiquitousBit messages = append(messages, ecdsaWarning) } // when forcing a bundle, bundle ubiquity doesn't matter // also we don't retrieve the anchoring root of the bundle var untrusted []string if flavor != Force { // Add root store presence info root := bundle.Chain[len(bundle.Chain)-1] bundle.Root = root log.Infof("the anchoring root is %v", root.Subject) // Check if there is any platform that doesn't trust the chain. // Also, an warning will be generated if ubiquity.Platforms is nil, untrusted = ubiquity.UntrustedPlatforms(root) untrustedMsg := untrustedPlatformsWarning(untrusted) if len(untrustedMsg) > 0 { log.Debug("Populate untrusted platform warning.") statusCode |= errors.BundleNotUbiquitousBit messages = append(messages, untrustedMsg) } } // Check if there is any platform that rejects the chain because of SHA1 deprecation. sha1Msgs := ubiquity.SHA1DeprecationMessages(bundle.Chain) if len(sha1Msgs) > 0 { log.Debug("Populate SHA1 deprecation warning.") statusCode |= errors.BundleNotUbiquitousBit messages = append(messages, sha1Msgs...) } bundle.Status = &BundleStatus{ExpiringSKIs: getSKIs(bundle.Chain, expiringCerts), Code: statusCode, Messages: messages, Untrusted: untrusted} // attempt to not to include the root certificate for optimization if flavor != Force { // Include at least one intermediate if the leaf has enabled OCSP and is not CA. if bundle.Cert.OCSPServer != nil && !bundle.Cert.IsCA && len(bundle.Chain) <= 2 { // No op. Return one intermediate if there is one. } else { // do not include the root. bundle.Chain = bundle.Chain[:len(bundle.Chain)-1] } } bundle.Status.IsRebundled = diff(bundle.Chain, certs) log.Debugf("bundle complete") return bundle, nil }
// Handle receives the incoming request, validates it, and processes it. func (h *AuthHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("signature request received") body, err := ioutil.ReadAll(r.Body) if err != nil { log.Errorf("failed to read response body: %v", err) return err } r.Body.Close() var aReq auth.AuthenticatedRequest err = json.Unmarshal(body, &aReq) if err != nil { log.Errorf("failed to unmarshal authenticated request: %v", err) return errors.NewBadRequest(err) } var req jsonSignRequest err = json.Unmarshal(aReq.Request, &req) if err != nil { log.Errorf("failed to unmarshal request from authenticated request: %v", err) return errors.NewBadRequestString("Unable to parse authenticated sign request") } // Sanity checks to ensure that we have a valid policy. This // should have been checked in NewAuthHandler. policy := h.signer.Policy() if policy == nil { log.Critical("signer was initialised without a signing policy") return errors.NewBadRequestString("invalid policy") } profile, err := signer.Profile(h.signer, req.Profile) if err != nil { return err } if profile.Provider == nil { log.Error("profile has no authentication provider") return errors.NewBadRequestString("no authentication provider") } if !profile.Provider.Verify(&aReq) { log.Warning("received authenticated request with invalid token") return errors.NewBadRequestString("invalid token") } signReq := jsonReqToTrue(req) if signReq.Request == "" { return errors.NewBadRequestString("missing parameter 'certificate_request'") } cert, err := h.signer.Sign(signReq) if err != nil { log.Errorf("signature failed: %v", err) return err } result := map[string]string{"certificate": string(cert)} log.Info("wrote response") return api.SendResponse(w, result) }