// NewBundlerFromPEM creates a new Bundler from PEM-encoded root certificates and // intermediate certificates. func NewBundlerFromPEM(caBundlePEM, intBundlePEM []byte) (*Bundler, error) { b := &Bundler{ RootPool: x509.NewCertPool(), IntermediatePool: x509.NewCertPool(), KnownIssuers: map[string]bool{}, } log.Debug("parsing root certificates from PEM") roots, err := helpers.ParseCertificatesPEM(caBundlePEM) if err != nil { log.Errorf("failed to parse root bundle: %v", err) return nil, errors.New(errors.RootError, errors.ParseFailed) } log.Debug("parse intermediate certificates from PEM") var intermediates []*x509.Certificate if intermediates, err = helpers.ParseCertificatesPEM(intBundlePEM); err != nil { log.Errorf("failed to parse intermediate bundle: %v", err) return nil, errors.New(errors.IntermediatesError, errors.ParseFailed) } log.Debug("building certificate pools") for _, c := range roots { b.RootPool.AddCert(c) b.KnownIssuers[string(c.Signature)] = true } for _, c := range intermediates { b.IntermediatePool.AddCert(c) b.KnownIssuers[string(c.Signature)] = true } log.Debug("bundler set up") return b, nil }
// NewSourceFromFile reads the named file into an InMemorySource. // The file read by this function must contain whitespace-separated OCSP // responses. Each OCSP response must be in base64-encoded DER form (i.e., // PEM without headers or whitespace). Invalid responses are ignored. // This function pulls the entire file into an InMemorySource. func NewSourceFromFile(responseFile string) (Source, error) { fileContents, err := ioutil.ReadFile(responseFile) if err != nil { return nil, err } responsesB64 := regexp.MustCompile("\\s").Split(string(fileContents), -1) src := InMemorySource{} for _, b64 := range responsesB64 { der, tmpErr := base64.StdEncoding.DecodeString(b64) if tmpErr != nil { log.Errorf("Base64 decode error on: %s", b64) continue } response, tmpErr := ocsp.ParseResponse(der, nil) if tmpErr != nil { log.Errorf("OCSP decode error on: %s", b64) continue } src[response.SerialNumber.String()] = der } log.Infof("Read %d OCSP responses", len(src)) return src, nil }
// NewBundler creates a new Bundler from the files passed in; these // files should contain a list of valid root certificates and a list // of valid intermediate certificates, respectively. func NewBundler(caBundleFile, intBundleFile string) (*Bundler, error) { log.Debug("Loading CA bundle: ", caBundleFile) caBundlePEM, err := ioutil.ReadFile(caBundleFile) if err != nil { log.Errorf("root bundle failed to load: %v", err) return nil, errors.Wrap(errors.RootError, errors.ReadFailed, err) } log.Debug("Loading Intermediate bundle: ", intBundleFile) intBundlePEM, err := ioutil.ReadFile(intBundleFile) if err != nil { log.Errorf("intermediate bundle failed to load: %v", err) return nil, errors.Wrap(errors.IntermediatesError, errors.ReadFailed, err) } if _, err := os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) { log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash) err = os.MkdirAll(IntermediateStash, 0755) if err != nil { log.Errorf("failed to create intermediate stash directory %s: %v", IntermediateStash, err) return nil, err } log.Infof("intermediate stash directory %s created", IntermediateStash) } return NewBundlerFromPEM(caBundlePEM, intBundlePEM) }
// A Responder can process both GET and POST requests. The mapping // from an OCSP request to an OCSP response is done by the Source; // the Responder simply decodes the request, and passes back whatever // response is provided by the source. func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Request) { // Read response from request var requestBody []byte var err error switch request.Method { case "GET": re := regexp.MustCompile("^.*/") base64Request := re.ReplaceAllString(request.RequestURI, "") base64Request, err = url.QueryUnescape(base64Request) if err != nil { return } requestBody, err = base64.StdEncoding.DecodeString(base64Request) if err != nil { return } case "POST": requestBody, err = ioutil.ReadAll(request.Body) if err != nil { response.WriteHeader(http.StatusBadRequest) return } default: response.WriteHeader(http.StatusMethodNotAllowed) return } // TODO log request b64Body := base64.StdEncoding.EncodeToString(requestBody) log.Infof("Received OCSP request: %s", b64Body) // All responses after this point will be OCSP. // We could check for the content type of the request, but that // seems unnecessariliy restrictive. response.Header().Add("Content-Type", "application/ocsp-response") // Parse response as an OCSP request // XXX: This fails if the request contains the nonce extension. // We don't intend to support nonces anyway, but maybe we // should return unauthorizedRequest instead of malformed. ocspRequest, err := ocsp.ParseRequest(requestBody) if err != nil { log.Errorf("Error decoding request body: %s", b64Body) response.Write(malformedRequestErrorResponse) return } // Look up OCSP response from source ocspResponse, found := rs.Source.Response(ocspRequest) if !found { log.Errorf("No response found for request: %s", b64Body) response.Write(unauthorizedErrorResponse) return } // Write OCSP response to response response.WriteHeader(http.StatusOK) response.Write(ocspResponse) }
// 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, Method: "POST"}, nil }
// handleError is the centralised error handling and reporting. func handleError(w http.ResponseWriter, err error) (code int) { if err == nil { return http.StatusOK } msg := err.Error() httpCode := http.StatusInternalServerError // If it is recognized as HttpError emitted from cf-ssl, // we rewrite the status code accordingly. If it is a // cf-ssl error, set the http status to StatusBadRequest switch err := err.(type) { case *errors.HTTPError: httpCode = err.StatusCode code = err.StatusCode case *errors.Error: httpCode = http.StatusBadRequest code = err.ErrorCode msg = err.Message } response := NewErrorResponse(msg, code) jsonMessage, err := json.Marshal(response) if err != nil { log.Errorf("Failed to marshal JSON: %v", err) } else { msg = string(jsonMessage) } http.Error(w, msg, httpCode) return code }
// 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 }
// New creates a new root certificate from the certificate request. func New(req *csr.CertificateRequest) (cert, key []byte, err error) { if req.CA != nil { if req.CA.Expiry != "" { CAPolicy.Default.ExpiryString = req.CA.Expiry CAPolicy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry) } if req.CA.PathLength != 0 { signer.MaxPathLen = req.CA.PathLength } } g := &csr.Generator{Validator: validator} csr, key, err := g.ProcessRequest(req) if err != nil { log.Errorf("failed to process request: %v", err) key = nil return } priv, err := helpers.ParsePrivateKeyPEM(key) if err != nil { log.Errorf("failed to parse private key: %v", err) return } s, err := local.NewSigner(priv, nil, signer.DefaultSigAlgo(priv), nil) if err != nil { log.Errorf("failed to create signer: %v", err) return } s.SetPolicy(CAPolicy) signReq := signer.SignRequest{Hosts: req.Hosts, Request: string(csr)} cert, err = s.Sign(signReq) return }
// NewHandler generates a new Handler using the certificate // authority private key and certficate to sign certificates. If remote // is not an empty string, the handler will send signature requests to // the CFSSL instance contained in remote by default. func NewHandler(caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) { root := universal.Root{ Config: map[string]string{ "cert-file": caFile, "key-file": caKeyFile, }, } s, err := universal.NewSigner(root, policy) if err != nil { log.Errorf("setting up signer failed: %v", err) return nil, err } return NewHandlerFromSigner(s) }
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 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 }
// ParseRequest takes a certificate request and generates a key and // CSR from it. It does no validation -- caveat emptor. It will, // however, fail if the key request is not valid (i.e., an unsupported // curve or RSA key size). The lack of validation was specifically // chosen to allow the end user to define a policy and validate the // request appropriately before calling this function. func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) { log.Info("received CSR") if req.KeyRequest == nil { req.KeyRequest = &KeyRequest{ Algo: DefaultKeyRequest.Algo, Size: DefaultKeyRequest.Size, } } log.Infof("generating key: %s-%d", req.KeyRequest.Algo, req.KeyRequest.Size) priv, err := req.KeyRequest.Generate() if err != nil { err = cferr.Wrap(cferr.PrivateKeyError, cferr.GenerationFailed, err) return } switch priv := priv.(type) { case *rsa.PrivateKey: key = x509.MarshalPKCS1PrivateKey(priv) block := pem.Block{ Type: "RSA PRIVATE KEY", Bytes: key, } key = pem.EncodeToMemory(&block) case *ecdsa.PrivateKey: key, err = x509.MarshalECPrivateKey(priv) if err != nil { err = cferr.Wrap(cferr.PrivateKeyError, cferr.Unknown, err) return } block := pem.Block{ Type: "EC PRIVATE KEY", Bytes: key, } key = pem.EncodeToMemory(&block) default: panic("Generate should have failed to produce a valid key.") } var tpl = x509.CertificateRequest{ Subject: req.Name(), SignatureAlgorithm: req.KeyRequest.SigAlgo(), DNSNames: req.Hosts, } csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv) if err != nil { log.Errorf("failed to generate a CSR: %v", err) // The use of CertificateError was a matter of some // debate; it is the one edge case in which a new // error category specifically for CSRs might be // useful, but it was deemed that one edge case did // not a new category justify. err = cferr.Wrap(cferr.CertificateError, cferr.BadRequest, err) return } block := pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csr, } log.Info("encoded CSR") csr = pem.EncodeToMemory(&block) return }
// NewFromPEM creates a new root certificate from the key file passed in. func NewFromPEM(req *csr.CertificateRequest, keyFile string) (cert []byte, err error) { if req.CA != nil { if req.CA.Expiry != "" { CAPolicy.Default.ExpiryString = req.CA.Expiry CAPolicy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry) } if req.CA.PathLength != 0 { signer.MaxPathLen = req.CA.PathLength } } privData, err := ioutil.ReadFile(keyFile) if err != nil { return nil, err } priv, err := helpers.ParsePrivateKeyPEM(privData) if err != nil { return nil, err } var sigAlgo x509.SignatureAlgorithm switch priv := priv.(type) { case *rsa.PrivateKey: bitLength := priv.PublicKey.N.BitLen() switch { case bitLength >= 4096: sigAlgo = x509.SHA512WithRSA case bitLength >= 3072: sigAlgo = x509.SHA384WithRSA case bitLength >= 2048: sigAlgo = x509.SHA256WithRSA default: sigAlgo = x509.SHA1WithRSA } case *ecdsa.PrivateKey: switch priv.Curve { case elliptic.P521(): sigAlgo = x509.ECDSAWithSHA512 case elliptic.P384(): sigAlgo = x509.ECDSAWithSHA384 case elliptic.P256(): sigAlgo = x509.ECDSAWithSHA256 default: sigAlgo = x509.ECDSAWithSHA1 } default: sigAlgo = x509.UnknownSignatureAlgorithm } var tpl = x509.CertificateRequest{ Subject: req.Name(), SignatureAlgorithm: sigAlgo, DNSNames: req.Hosts, } certReq, err := x509.CreateCertificateRequest(rand.Reader, &tpl, priv) if err != nil { log.Errorf("failed to generate a CSR: %v", err) // The use of CertificateError was a matter of some // debate; it is the one edge case in which a new // error category specifically for CSRs might be // useful, but it was deemed that one edge case did // not a new category justify. err = cferr.Wrap(cferr.CertificateError, cferr.BadRequest, err) return } p := &pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: certReq, } certReq = pem.EncodeToMemory(p) s, err := local.NewSigner(priv, nil, signer.DefaultSigAlgo(priv), nil) if err != nil { log.Errorf("failed to create signer: %v", err) return } s.SetPolicy(CAPolicy) signReq := signer.SignRequest{Request: string(certReq)} cert, err = s.Sign(signReq) return }
// ParseRequest takes a certificate request and generates a key and // CSR from it. It does no validation -- caveat emptor. It will, // however, fail if the key request is not valid (i.e., an unsupported // curve or RSA key size). The lack of validation was specifically // chosen to allow the end user to define a policy and validate the // request appropriately before calling this function. func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) { log.Info("received CSR") if req.KeyRequest == nil { req.KeyRequest = &KeyRequest{ Algo: DefaultKeyRequest.Algo, Size: DefaultKeyRequest.Size, } } log.Infof("generating key: %s-%d", req.KeyRequest.Algo, req.KeyRequest.Size) priv, err := req.KeyRequest.Generate() if err != nil { err = cferr.Wrap(cferr.PrivateKeyError, cferr.GenerationFailed, err) return } switch priv := priv.(type) { case *rsa.PrivateKey: key = x509.MarshalPKCS1PrivateKey(priv) block := pem.Block{ Type: "RSA PRIVATE KEY", Bytes: key, } key = pem.EncodeToMemory(&block) case *ecdsa.PrivateKey: key, err = x509.MarshalECPrivateKey(priv) if err != nil { err = cferr.Wrap(cferr.PrivateKeyError, cferr.Unknown, err) return } block := pem.Block{ Type: "EC PRIVATE KEY", Bytes: key, } key = pem.EncodeToMemory(&block) default: panic("Generate should have failed to produce a valid key.") } var tpl = x509.CertificateRequest{ Subject: req.Name(), SignatureAlgorithm: req.KeyRequest.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 }
func gencertMain(args []string, c cli.Config) (err error) { csrJSONFile, args, err := cli.PopFirstArgument(args) if err != nil { return } csrJSONFileBytes, err := cli.ReadStdin(csrJSONFile) if err != nil { return } var req csr.CertificateRequest err = json.Unmarshal(csrJSONFileBytes, &req) if err != nil { return } if c.IsCA { var key, cert []byte cert, err = initca.NewFromPEM(&req, c.CAKeyFile) if err != nil { log.Errorf("%v\n", err) log.Infof("generating a new CA key and certificate from CSR") cert, key, err = initca.New(&req) if err != nil { return } } cli.PrintCert(key, nil, cert) } else { if req.CA != nil { err = errors.New("ca section only permitted in initca") return } // 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 } if c.CAKeyFile == "" { log.Error("need a CA key (provide one with -ca-key)") return } } var key, csrBytes []byte g := &csr.Generator{Validator: genkey.Validator} csrBytes, key, err = g.ProcessRequest(&req) if err != nil { key = nil return } 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 } cli.PrintCert(key, csrBytes, cert) } return 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 := policy.Default if policy.Profiles != nil && req.Profile != "" { profile = policy.Profiles[req.Profile] } if profile == nil { log.Critical("signer was initialised without any valid profiles") return errors.NewBadRequestString("invalid profile") } 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.Hosts == nil { return errors.NewBadRequestString("missing parameter 'hostname' or 'hosts'") } 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) }
// fetchIntermediates goes through each of the URLs in the AIA "Issuing // CA" extensions and fetches those certificates. If those // certificates are not present in either the root pool or // intermediate pool, the certificate is saved to file and added to // the list of intermediates to be used for verification. This will // not add any new certificates to the root pool; if the ultimate // issuer is not trusted, fetching the certicate here will not change // that. func (b *Bundler) fetchIntermediates(certs []*x509.Certificate) (err error) { log.Debugf("searching intermediates") if _, err := os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) { log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash) err = os.MkdirAll(IntermediateStash, 0755) if err != nil { log.Errorf("failed to create intermediate stash directory %s: %v", IntermediateStash, err) return err } log.Infof("intermediate stash directory %s created", IntermediateStash) } // stores URLs and certificate signatures that have been seen seen := map[string]bool{} var foundChains int // Construct a verify chain as a reversed partial bundle, // such that the certs are ordered by promxity to the root CAs. var chain []*fetchedIntermediate for i, cert := range certs { var name string // Only construct filenames for non-leaf intermediate certs // so they will be saved to disk if necessary. // Leaf cert gets a empty name and will be skipped. if i > 0 { name = constructCertFileName(cert) } chain = append([]*fetchedIntermediate{&fetchedIntermediate{cert, name}}, chain...) seen[string(cert.Signature)] = true } // Verify the chain and store valid intermediates in the chain. // If it doesn't verify, fetch the intermediates and extend the chain // in a DFS manner and verify each time we hit a root. for { if len(chain) == 0 { log.Debugf("search complete") if foundChains == 0 { return x509.UnknownAuthorityError{} } return nil } current := chain[0] var advanced bool if b.verifyChain(chain) { foundChains++ } log.Debugf("walk AIA issuers") for _, url := range current.Cert.IssuingCertificateURL { if seen[url] { log.Debugf("url %s has been seen", url) continue } crt, err := fetchRemoteCertificate(url) if err != nil { continue } else if seen[string(crt.Cert.Signature)] { log.Debugf("fetched certificate is known") continue } seen[url] = true seen[string(crt.Cert.Signature)] = true chain = append([]*fetchedIntermediate{crt}, chain...) advanced = true break } if !advanced { log.Debugf("didn't advance, stepping back") chain = chain[1:] } } }
// A Responder can process both GET and POST requests. The mapping // from an OCSP request to an OCSP response is done by the Source; // the Responder simply decodes the request, and passes back whatever // response is provided by the source. // Note: The caller must use http.StripPrefix to strip any path components // (including '/') on GET requests. // Do not use this responder in conjunction with http.NewServeMux, because the // default handler will try to canonicalize path components by changing any // strings of repeated '/' into a single '/', which will break the base64 // encoding. func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Request) { // Read response from request var requestBody []byte var err error switch request.Method { case "GET": base64Request, err := url.QueryUnescape(request.URL.Path) if err != nil { log.Errorf("Error decoding URL: %s", request.URL.Path) response.WriteHeader(http.StatusBadRequest) return } // url.QueryUnescape not only unescapes %2B escaping, but it additionally // turns the resulting '+' into a space, which makes base64 decoding fail. // So we go back afterwards and turn ' ' back into '+'. This means we // accept some malformed input that includes ' ' or %20, but that's fine. base64RequestBytes := []byte(base64Request) for i := range base64RequestBytes { if base64RequestBytes[i] == ' ' { base64RequestBytes[i] = '+' } } requestBody, err = base64.StdEncoding.DecodeString(string(base64RequestBytes)) if err != nil { log.Errorf("Error decoding base64 from URL: %s", base64Request) response.WriteHeader(http.StatusBadRequest) return } case "POST": requestBody, err = ioutil.ReadAll(request.Body) if err != nil { log.Errorf("Problem reading body of POST: %s", err) response.WriteHeader(http.StatusBadRequest) return } default: response.WriteHeader(http.StatusMethodNotAllowed) return } // TODO log request b64Body := base64.StdEncoding.EncodeToString(requestBody) log.Infof("Received OCSP request: %s", b64Body) // All responses after this point will be OCSP. // We could check for the content type of the request, but that // seems unnecessariliy restrictive. response.Header().Add("Content-Type", "application/ocsp-response") // Parse response as an OCSP request // XXX: This fails if the request contains the nonce extension. // We don't intend to support nonces anyway, but maybe we // should return unauthorizedRequest instead of malformed. ocspRequest, err := ocsp.ParseRequest(requestBody) if err != nil { log.Errorf("Error decoding request body: %s", b64Body) response.WriteHeader(http.StatusBadRequest) response.Write(malformedRequestErrorResponse) return } // Look up OCSP response from source ocspResponse, found := rs.Source.Response(ocspRequest) if !found { log.Errorf("No response found for request: %s", b64Body) response.Write(unauthorizedErrorResponse) return } parsedResponse, err := ocsp.ParseResponse(ocspResponse, nil) if err != nil { log.Errorf("Error parsing response: %s", err) response.Write(unauthorizedErrorResponse) return } // Write OCSP response to response response.Header().Add("Last-Modified", parsedResponse.ProducedAt.Format(time.RFC1123)) response.Header().Add("Expires", parsedResponse.NextUpdate.Format(time.RFC1123)) maxAge := int64(parsedResponse.NextUpdate.Sub(rs.clk.Now()) / time.Second) if maxAge > 0 { response.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d", maxAge)) } response.WriteHeader(http.StatusOK) response.Write(ocspResponse) }
// registerHandlers instantiates various handlers and associate them to corresponding endpoints. func registerHandlers(c cli.Config) error { log.Info("Setting up signer endpoint") s, err := sign.SignerFromConfig(c) if err != nil { log.Warningf("sign and authsign endpoints are disabled: %v", err) } else { if signHandler, err := apisign.NewHandlerFromSigner(s); err == nil { log.Info("Assigning handler to /sign") http.Handle("/api/v1/cfssl/sign", signHandler) } else { log.Warningf("endpoint '/api/v1/cfssl/sign' is disabled: %v", err) } if signHandler, err := apisign.NewAuthHandlerFromSigner(s); err == nil { log.Info("Assigning handler to /authsign") http.Handle("/api/v1/cfssl/authsign", signHandler) } else { log.Warningf("endpoint '/api/v1/cfssl/authsign' is disabled: %v", err) } } log.Info("Setting up info endpoint") infoHandler, err := info.NewHandler(s) if err != nil { log.Warningf("endpoint '/api/v1/cfssl/info' is disabled: %v", err) } else { http.Handle("/api/v1/cfssl/info", infoHandler) } log.Info("Setting up new cert endpoint") if err != nil { log.Errorf("endpoint '/api/v1/cfssl/newcert' is disabled") } else { newCertGenerator := generator.NewCertGeneratorHandlerFromSigner(generator.CSRValidate, s) http.Handle("/api/v1/cfssl/newcert", newCertGenerator) } log.Info("Setting up bundler endpoint") bundleHandler, err := bundle.NewHandler(c.CABundleFile, c.IntBundleFile) if err != nil { log.Warningf("endpoint '/api/v1/cfssl/bundle' is disabled: %v", err) } else { http.Handle("/api/v1/cfssl/bundle", bundleHandler) } log.Info("Setting up CSR endpoint") generatorHandler, err := generator.NewHandler(generator.CSRValidate) if err != nil { log.Errorf("Failed to set up CSR endpoint: %v", err) return err } http.Handle("/api/v1/cfssl/newkey", generatorHandler) log.Info("Setting up initial CA endpoint") http.Handle("/api/v1/cfssl/init_ca", initca.NewHandler()) log.Info("Setting up scan endpoint") http.Handle("/api/v1/cfssl/scan", scan.NewHandler()) log.Info("Setting up scaninfo endpoint") http.Handle("/api/v1/cfssl/scaninfo", scan.NewInfoHandler()) log.Info("Handler set up complete.") return nil }