// 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 { // if the line/space is empty just skip if b64 == "" { continue } der, tmpErr := base64.StdEncoding.DecodeString(b64) if tmpErr != nil { log.Errorf("Base64 decode error %s on: %s", tmpErr, b64) continue } response, tmpErr := ocsp.ParseResponse(der, nil) if tmpErr != nil { log.Errorf("OCSP decode error %s on: %s", tmpErr, b64) continue } src[response.SerialNumber.String()] = der } log.Infof("Read %d OCSP responses", len(src)) return src, nil }
// signWithCSR creates a new root certificate from signing a X509.CertificateRequest // by a crypto.Signer. func signWithCSR(tpl *x509.CertificateRequest, priv crypto.Signer) (cert, csrPEM []byte, err error) { csrPEM, 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: csrPEM, } csrPEM = 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(csrPEM)} cert, err = s.Sign(signReq) return }
// 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) }
// 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 }
// 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 cfssl, // we rewrite the status code accordingly. If it is a // cfssl 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 }
// NewBundlerFromPEM creates a new Bundler from PEM-encoded root certificates and // intermediate certificates. // If caBundlePEM is nil, the resulting Bundler can only do "Force" bundle. func NewBundlerFromPEM(caBundlePEM, intBundlePEM []byte) (*Bundler, error) { 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") intermediates, err := helpers.ParseCertificatesPEM(intBundlePEM) if err != nil { log.Errorf("failed to parse intermediate bundle: %v", err) return nil, errors.New(errors.IntermediatesError, errors.ParseFailed) } b := &Bundler{ KnownIssuers: map[string]bool{}, IntermediatePool: x509.NewCertPool(), } log.Debug("building certificate pools") // RootPool will be nil if caBundlePEM is nil, also // that translates to caBundleFile is "". // Systems root store will be used. if caBundlePEM != nil { b.RootPool = x509.NewCertPool() } 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 }
// New creates a new root certificate from the certificate request. func New(req *csr.CertificateRequest) (cert, csrPEM, 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} csrPEM, 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(csrPEM)} cert, err = s.Sign(signReq) return }
// 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) { var caBundle, intBundle []byte var err error if caBundleFile != "" { log.Debug("Loading CA bundle: ", caBundleFile) caBundle, 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) } } if intBundleFile != "" { log.Debug("Loading Intermediate bundle: ", intBundleFile) intBundle, 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 IntermediateStash != "" { 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(caBundle, intBundle) }
// NewAuthHandler generates a new AuthHandler 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 NewAuthHandler(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 signhandler.NewAuthHandlerFromSigner(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 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 }
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 dumpMetrics(w http.ResponseWriter, req *http.Request) { log.Info("whitelisted requested for metrics endpoint") var statsOut = struct { Metrics metrics.Registry `json:"metrics"` Signers []string `json:"signers"` }{stats.Registry, make([]string, 0, len(signers))} for signer := range signers { statsOut.Signers = append(statsOut.Signers, signer) } out, err := json.Marshal(statsOut) if err != nil { log.Errorf("failed to dump metrics: %v", err) } w.Write(out) }
func fail(w http.ResponseWriter, req *http.Request, status, code int, msg, ad string) { incErrors() if ad != "" { ad = " (" + ad + ")" } log.Errorf("[HTTP %d] %d - %s%s", status, code, msg, ad) dumpReq, err := httputil.DumpRequest(req, true) if err != nil { fmt.Printf("%v#v\n", req) } else { fmt.Printf("%s\n", dumpReq) } res := api.NewErrorResponse(msg, code) w.WriteHeader(status) jenc := json.NewEncoder(w) jenc.Encode(res) }
// 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) }
// 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) }
// 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) { if IntermediateStash != "" { 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:] } } }
// 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 = NewBasicKeyRequest() } 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 dispatchRequest(w http.ResponseWriter, req *http.Request) { incRequests() if req.Method != "POST" { fail(w, req, http.StatusMethodNotAllowed, 1, "only POST is permitted", "") return } body, err := ioutil.ReadAll(req.Body) if err != nil { fail(w, req, http.StatusInternalServerError, 1, err.Error(), "while reading request body") return } defer req.Body.Close() var authReq auth.AuthenticatedRequest err = json.Unmarshal(body, &authReq) if err != nil { fail(w, req, http.StatusBadRequest, 1, err.Error(), "while unmarshaling request body") return } var sigRequest signer.SignRequest err = json.Unmarshal(authReq.Request, &sigRequest) if err != nil { fail(w, req, http.StatusBadRequest, 1, err.Error(), "while unmarshalling authenticated request") return } if sigRequest.Label == "" { sigRequest.Label = defaultLabel } acl := whitelists[sigRequest.Label] if acl != nil { ip, err := whitelist.HTTPRequestLookup(req) if err != nil { fail(w, req, http.StatusInternalServerError, 1, err.Error(), "while getting request IP") return } if !acl.Permitted(ip) { fail(w, req, http.StatusForbidden, 1, "not authorised", "because IP is not whitelisted") return } } s, ok := signers[sigRequest.Label] if !ok { fail(w, req, http.StatusBadRequest, 1, "bad request", "request is for non-existent label "+sigRequest.Label) return } stats.Requests[sigRequest.Label].Counter.Inc(1) stats.Requests[sigRequest.Label].Rate.Mark(1) // Sanity checks to ensure that we have a valid policy. This // should have been checked in NewAuthSignHandler. policy := s.Policy() if policy == nil { fail(w, req, http.StatusInternalServerError, 1, "invalid policy", "signer was initialised without a signing policy") return } profile := policy.Default if policy.Profiles != nil && sigRequest.Profile != "" { profile = policy.Profiles[sigRequest.Profile] if profile == nil { fail(w, req, http.StatusBadRequest, 1, "invalid profile", "failed to look up profile with name: "+sigRequest.Profile) return } } if profile == nil { fail(w, req, http.StatusInternalServerError, 1, "invalid profile", "signer was initialised without any valid profiles") return } if profile.Provider == nil { fail(w, req, http.StatusUnauthorized, 1, "authorisation required", "received unauthenticated request") return } if !profile.Provider.Verify(&authReq) { fail(w, req, http.StatusBadRequest, 1, "invalid token", "received authenticated request with invalid token") return } if sigRequest.Request == "" { fail(w, req, http.StatusBadRequest, 1, "invalid request", "empty request") return } cert, err := s.Sign(sigRequest) if err != nil { fail(w, req, http.StatusBadRequest, 1, "bad request", "signature failed: "+err.Error()) return } x509Cert, err := helpers.ParseCertificatePEM(cert) if err != nil { fail(w, req, http.StatusInternalServerError, 1, "bad certificate", err.Error()) } log.Infof("signature: requester=%s, label=%s, profile=%s, serialno=%s", req.RemoteAddr, sigRequest.Label, sigRequest.Profile, x509Cert.SerialNumber) res := api.NewSuccessResponse(&SignatureResponse{Certificate: string(cert)}) jenc := json.NewEncoder(w) err = jenc.Encode(res) if err != nil { log.Errorf("error writing response: %v", err) } }
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 }
// 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) }