// Helper function to perform a remote sign or info request. func (s *Signer) remoteOp(req interface{}, profile, target string) (resp interface{}, err error) { jsonData, err := json.Marshal(req) if err != nil { return nil, cferr.Wrap(cferr.APIClientError, cferr.JSONError, err) } p, err := signer.Profile(s, profile) if err != nil { return } server := client.NewServer(p.RemoteServer) if server == nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("failed to connect to remote")) } // There's no auth provider for the "info" method if target == "info" { resp, err = server.Info(jsonData) } else if p.RemoteProvider != nil { resp, err = server.AuthSign(jsonData, nil, p.RemoteProvider) } else { resp, err = server.Sign(jsonData) } if err != nil { return nil, err } return }
// LoadConfig attempts to load the configuration from a byte slice. // On error, it returns nil. func LoadConfig(config []byte) (*Config, error) { var cfg = &Config{} err := json.Unmarshal(config, &cfg) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to unmarshal configuration: "+err.Error())) } if cfg.Signing == nil { return nil, errors.New("No \"signing\" field present") } if cfg.Signing.Default == nil { log.Debugf("no default given: using default config") cfg.Signing.Default = DefaultConfig() } else { if err := cfg.Signing.Default.populate(cfg); err != nil { return nil, err } } for k := range cfg.Signing.Profiles { if err := cfg.Signing.Profiles[k].populate(cfg); err != nil { return nil, err } } if !cfg.Valid() { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid configuration")) } log.Debugf("configuration ok") return cfg, nil }
// LoadFile attempts to load the db configuration file stored at the path // and returns the configuration. On error, it returns nil. func LoadFile(path string) (cfg *DBConfig, err error) { log.Debugf("loading db configuration file from %s", path) if path == "" { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path")) } var body []byte body, err = ioutil.ReadFile(path) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file")) } cfg = &DBConfig{} err = json.Unmarshal(body, &cfg) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to unmarshal configuration: "+err.Error())) } if cfg.DataSourceName == "" || cfg.DriverName == "" { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid db configuration")) } return }
// post connects to the remote server and returns a Response struct func (srv *server) post(url string, jsonData []byte) (*api.Response, error) { buf := bytes.NewBuffer(jsonData) resp, err := http.Post(url, "application/json", buf) if err != nil { return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, err) } body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, errors.Wrap(errors.APIClientError, errors.IOError, err) } resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New(string(body))) } var response api.Response err = json.Unmarshal(body, &response) if err != nil { log.Debug("Unable to parse response body:", string(body)) return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err) } if !response.Success || response.Result == nil { if len(response.Errors) > 0 { return nil, errors.Wrap(errors.APIClientError, errors.ServerRequestFailed, stderr.New(response.Errors[0].Message)) } return nil, errors.New(errors.APIClientError, errors.ServerRequestFailed) } return &response, nil }
// BundleFromRemote fetches the certificate served by the server at // serverName (or ip, if the ip argument is not the empty string). It // is expected that the method will be able to make a connection at // port 443. The certificate used by the server in this connection is // used to build the bundle, which will necessarily be keyless. func (b *Bundler) BundleFromRemote(serverName, ip string, flavor BundleFlavor) (*Bundle, error) { config := &tls.Config{ RootCAs: b.RootPool, ServerName: serverName, } // Dial by IP if present var dialName string if ip != "" { dialName = ip + ":443" } else { dialName = serverName + ":443" } log.Debugf("bundling from remote %s", dialName) dialer := &net.Dialer{Timeout: time.Duration(5) * time.Second} conn, err := tls.DialWithDialer(dialer, "tcp", dialName, config) var dialError string // If there's an error in tls.Dial, try again with // InsecureSkipVerify to fetch the remote bundle to (re-)bundle // with. If the bundle is indeed not usable (expired, mismatched // hostnames, etc.), report the error. Otherwise, create a // working bundle and insert the tls error in the bundle.Status. if err != nil { log.Debugf("dial failed: %v", err) // record the error msg dialError = fmt.Sprintf("Failed rigid TLS handshake with %s: %v", dialName, err) // dial again with InsecureSkipVerify log.Debugf("try again with InsecureSkipVerify.") config.InsecureSkipVerify = true conn, err = tls.DialWithDialer(dialer, "tcp", dialName, config) if err != nil { log.Debugf("dial with InsecureSkipVerify failed: %v", err) return nil, errors.Wrap(errors.DialError, errors.Unknown, err) } } connState := conn.ConnectionState() certs := connState.PeerCertificates err = conn.VerifyHostname(serverName) if err != nil { log.Debugf("failed to verify hostname: %v", err) return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } // Bundle with remote certs. Inject the initial dial error, if any, to the status reporting. bundle, err := b.Bundle(certs, nil, flavor) if err != nil { return nil, err } else if dialError != "" { bundle.Status.Messages = append(bundle.Status.Messages, dialError) } return bundle, err }
// 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) }
// LoadFile attempts to load the configuration file stored at the path // and returns the configuration. On error, it returns nil. func LoadFile(path string) (*Config, error) { log.Debugf("loading configuration file from %s", path) if path == "" { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path")) } body, err := ioutil.ReadFile(path) if err != nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file")) } return LoadConfig(body) }
// ParsePKCS7 attempts to parse the DER encoded bytes of a // PKCS7 structure func ParsePKCS7(raw []byte) (msg *PKCS7, err error) { var pkcs7 initPKCS7 _, err = asn1.Unmarshal(raw, &pkcs7) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } msg = new(PKCS7) msg.Raw = pkcs7.Raw msg.ContentInfo = pkcs7.ContentType.String() switch { case msg.ContentInfo == ObjIDData: msg.ContentInfo = "Data" _, err = asn1.Unmarshal(pkcs7.Content.Bytes, &msg.Content.Data) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } case msg.ContentInfo == ObjIDSignedData: msg.ContentInfo = "SignedData" var signedData signedData _, err = asn1.Unmarshal(pkcs7.Content.Bytes, &signedData) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } if len(signedData.Certificates.Bytes) != 0 { msg.Content.SignedData.Certificates, err = x509.ParseCertificates(signedData.Certificates.Bytes) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } } if len(signedData.Crls.Bytes) != 0 { msg.Content.SignedData.Crl, err = x509.ParseDERCRL(signedData.Crls.Bytes) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } } msg.Content.SignedData.Version = signedData.Version msg.Content.SignedData.Raw = pkcs7.Content.Bytes case msg.ContentInfo == ObjIDEncryptedData: msg.ContentInfo = "EncryptedData" var encryptedData EncryptedData _, err = asn1.Unmarshal(pkcs7.Content.Bytes, &encryptedData) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } if encryptedData.Version != 0 { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Only support for PKCS #7 encryptedData version 0")) } msg.Content.EncryptedData = encryptedData default: return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Attempt to parse PKCS# 7 Content not of type data, signed data or encrypted data")) } return msg, nil }
// 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 }
// ParseCertificatePEM parses and returns a PEM-encoded certificate, // can handle PEM encoded PKCS #7 structures. func ParseCertificatePEM(certPEM []byte) (*x509.Certificate, error) { certPEM = bytes.TrimSpace(certPEM) cert, rest, err := ParseOneCertificateFromPEM(certPEM) if err != nil { // Log the actual parsing error but throw a default parse error message. log.Debugf("Certificate parsing error: %v", err) return nil, cferr.New(cferr.CertificateError, cferr.ParseFailed) } else if cert == nil { return nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed) } else if len(rest) > 0 { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("the PEM file should contain only one object")) } else if len(cert) > 1 { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("the PKCS7 object in the PEM file should contain only one certificate")) } return cert[0], nil }
// ParseCertificatesDER parses a DER encoding of a certificate object and possibly private key, // either PKCS #7, PKCS #12, or raw x509. func ParseCertificatesDER(certsDER []byte, password string) (certs []*x509.Certificate, key crypto.Signer, err error) { certsDER = bytes.TrimSpace(certsDER) pkcs7data, err := pkcs7.ParsePKCS7(certsDER) if err != nil { var pkcs12data interface{} certs = make([]*x509.Certificate, 1) pkcs12data, certs[0], err = pkcs12.Decode(certsDER, password) if err != nil { certs, err = x509.ParseCertificates(certsDER) if err != nil { return nil, nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed) } } else { key = pkcs12data.(crypto.Signer) } } else { if pkcs7data.ContentInfo != "SignedData" { return nil, nil, cferr.Wrap(cferr.CertificateError, cferr.DecodeFailed, errors.New("can only extract certificates from signed data content info")) } certs = pkcs7data.Content.SignedData.Certificates } if certs == nil { return nil, key, cferr.New(cferr.CertificateError, cferr.DecodeFailed) } return certs, key, nil }
// NewSignerFromFile reads the issuer cert, the responder cert and the responder key // from PEM files, and takes an interval in seconds func NewSignerFromFile(issuerFile, responderFile, keyFile string, interval time.Duration) (Signer, error) { log.Debug("Loading issuer cert: ", issuerFile) issuerBytes, err := ioutil.ReadFile(issuerFile) if err != nil { return nil, err } log.Debug("Loading responder cert: ", responderFile) responderBytes, err := ioutil.ReadFile(responderFile) if err != nil { return nil, err } log.Debug("Loading responder key: ", keyFile) keyBytes, err := ioutil.ReadFile(keyFile) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err) } issuerCert, err := helpers.ParseCertificatePEM(issuerBytes) if err != nil { return nil, err } responderCert, err := helpers.ParseCertificatePEM(responderBytes) if err != nil { return nil, err } key, err := helpers.ParsePrivateKeyPEM(keyBytes) if err != nil { log.Debug("Malformed private key %v", err) return nil, err } return NewSigner(issuerCert, responderCert, key, interval) }
// 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 }
// NewSignerFromFile generates a new local signer from a caFile // and a caKey file, both PEM encoded. func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signer, error) { log.Debug("Loading CA: ", caFile) ca, err := ioutil.ReadFile(caFile) if err != nil { return nil, err } log.Debug("Loading CA key: ", caKeyFile) cakey, err := ioutil.ReadFile(caKeyFile) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err) } parsedCa, err := helpers.ParseCertificatePEM(ca) if err != nil { return nil, err } priv, err := helpers.ParsePrivateKeyPEM(cakey) if err != nil { log.Debug("Malformed private key %v", err) return nil, err } return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy) }
// InsertCertificate puts a CertificateRecord into db. func InsertCertificate(db *sql.DB, cr *CertificateRecord) error { res, err := db.Exec( insertSQL, cr.Serial, cr.CALabel, cr.Status, cr.Reason, cr.Expiry.UTC(), cr.RevokedAt, cr.PEM, ) if err != nil { return wrapCertStoreError(err) } numRowsAffected, _ := res.RowsAffected() if numRowsAffected == 0 { return cferr.Wrap(cferr.CertStoreError, cferr.InsertionFailed, fmt.Errorf("failed to insert the certificate record")) } if numRowsAffected != 1 { return wrapCertStoreError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected)) } return nil }
// validator contains the default validation logic for certificate // authority certificates. The only requirement here is that the // certificate have a non-empty subject field. func validator(req *csr.CertificateRequest) error { if req.CN != "" { return nil } if len(req.Names) == 0 { return cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("missing subject information")) } for i := range req.Names { if csr.IsNameEmpty(req.Names[i]) { return cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("missing subject information")) } } return nil }
// ParseSelfSignedCertificatePEM parses a PEM-encoded certificate and check if it is self-signed. func ParseSelfSignedCertificatePEM(certPEM []byte) (*x509.Certificate, error) { cert, err := ParseCertificatePEM(certPEM) if err != nil { return nil, err } if err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature); err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.VerifyFailed, err) } return cert, nil }
// request performs the common logic for Sign and Info, performing the actual // request and returning the resultant certificate. func (srv *server) request(jsonData []byte, target string) ([]byte, error) { result, err := srv.getResultMap(jsonData, target) if err != nil { return nil, err } cert := result["certificate"].(string) if cert != "" { return []byte(cert), nil } return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response doesn't contain certificate.")) }
func (srv *server) getResultMap(jsonData []byte, target string) (result map[string]interface{}, err error) { url := srv.getURL(target) response, err := srv.post(url, jsonData) if err != nil { return } result, ok := response.Result.(map[string]interface{}) if !ok { err = errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response is formatted improperly")) return } 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) }
// parseCertificateRequest takes an incoming certificate request and // builds a certificate template from it. func parseCertificateRequest(priv crypto.Signer, csrBytes []byte) (template *x509.Certificate, err error) { csr, err := x509.ParseCertificateRequest(csrBytes) if err != nil { err = cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err) return } err = helpers.CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature) if err != nil { err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err) return } template = &x509.Certificate{ Subject: csr.Subject, PublicKeyAlgorithm: csr.PublicKeyAlgorithm, PublicKey: csr.PublicKey, SignatureAlgorithm: signer.DefaultSigAlgo(priv), } return }
// authReq is the common logic for AuthSign and AuthInfo -- perform the given // request, and return the resultant certificate. // The target is either 'sign' or 'info'. func (srv *server) authReq(req, ID []byte, provider auth.Provider, target string) ([]byte, error) { url := srv.getURL("auth" + target) token, err := provider.Token(req) if err != nil { return nil, errors.Wrap(errors.APIClientError, errors.AuthenticationFailure, err) } aReq := &auth.AuthenticatedRequest{ Timestamp: time.Now().Unix(), RemoteAddress: ID, Token: token, Request: req, } jsonData, err := json.Marshal(aReq) if err != nil { return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err) } response, err := srv.post(url, jsonData) if err != nil { return nil, err } result, ok := response.Result.(map[string]interface{}) if !ok { return nil, errors.New(errors.APIClientError, errors.JSONError) } cert, ok := result["certificate"].(string) if !ok { return nil, errors.New(errors.APIClientError, errors.JSONError) } return []byte(cert), nil }
// ParseCertificateRequest takes an incoming certificate request and // builds a certificate template from it. func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certificate, err error) { csr, err := x509.ParseCertificateRequest(csrBytes) if err != nil { err = cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err) return } err = helpers.CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature) if err != nil { err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err) return } template = &x509.Certificate{ Subject: csr.Subject, PublicKeyAlgorithm: csr.PublicKeyAlgorithm, PublicKey: csr.PublicKey, SignatureAlgorithm: s.SigAlgo(), DNSNames: csr.DNSNames, IPAddresses: csr.IPAddresses, } return }
// BundleFromFile takes a set of files containing the PEM-encoded leaf certificate // (optionally along with some intermediate certs), the PEM-encoded private key // and returns the bundle built from that key and the certificate(s). func (b *Bundler) BundleFromFile(bundleFile, keyFile string, flavor BundleFlavor, password string) (*Bundle, error) { log.Debug("Loading Certificate: ", bundleFile) certsRaw, err := ioutil.ReadFile(bundleFile) if err != nil { return nil, errors.Wrap(errors.CertificateError, errors.ReadFailed, err) } var keyPEM []byte // Load private key PEM only if a file is given if keyFile != "" { log.Debug("Loading private key: ", keyFile) keyPEM, err = ioutil.ReadFile(keyFile) if err != nil { log.Debugf("failed to read private key: ", err) return nil, errors.Wrap(errors.PrivateKeyError, errors.ReadFailed, err) } if len(keyPEM) == 0 { log.Debug("key is empty") return nil, errors.Wrap(errors.PrivateKeyError, errors.DecodeFailed, err) } } return b.BundleFromPEMorDER(certsRaw, keyPEM, flavor, password) }
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) { err = signer.FillTemplate(template, s.policy.Default, profile) if err != nil { return } var initRoot bool if s.ca == nil { if !template.IsCA { err = cferr.New(cferr.PolicyError, cferr.InvalidRequest) return } template.DNSNames = nil s.ca = template initRoot = true template.MaxPathLen = signer.MaxPathLen } else if template.IsCA { template.MaxPathLen = 1 template.DNSNames = nil } derBytes, err := x509.CreateCertificate(rand.Reader, template, s.ca, template.PublicKey, s.priv) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) } if initRoot { s.ca, err = x509.ParseCertificate(derBytes) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } } cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) log.Infof("signed certificate with serial number %d", template.SerialNumber) return }
// Profile gets the specific profile from the signer func Profile(s Signer, profile string) (*config.SigningProfile, error) { var p *config.SigningProfile policy := s.Policy() if policy != nil && policy.Profiles != nil && profile != "" { p = policy.Profiles[profile] } if p == nil && policy != nil { p = policy.Default } if p == nil { return nil, cferr.Wrap(cferr.APIClientError, cferr.ClientHTTPError, errors.New("profile must not be nil")) } return p, nil }
// RevokeCertificate updates a certificate with a given serial number and marks it revoked. func RevokeCertificate(db *sql.DB, serial string, reasonCode int) error { result, err := db.Exec(updateRevokeSQL, reasonCode, serial) if err != nil { return wrapCertStoreError(err) } numRowsAffected, _ := result.RowsAffected() if numRowsAffected == 0 { return cferr.Wrap(cferr.CertStoreError, cferr.RecordNotFound, fmt.Errorf("failed to revoke the certificate: certificate not found")) } if numRowsAffected != 1 { return wrapCertStoreError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected)) } return nil }
// UpdateOCSP updates a ocsp response record with a given serial number. func UpdateOCSP(db *sql.DB, serial, body string, expiry time.Time) (err error) { var result sql.Result result, err = db.Exec(updateOCSPSQL, serial, body, expiry) if err != nil { return wrapCertStoreError(err) } var numRowsAffected int64 numRowsAffected, err = result.RowsAffected() if numRowsAffected == 0 { return cferr.Wrap(cferr.CertStoreError, cferr.RecordNotFound, fmt.Errorf("failed to update the OCSP record")) } if numRowsAffected != 1 { return wrapCertStoreError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected)) } return }
// InsertOCSP puts a new OCSPRecord into the db. func InsertOCSP(db *sql.DB, rr *OCSPRecord) error { res, err := db.Exec( insertOCSPSQL, rr.Serial, rr.Body, rr.Expiry.UTC(), ) if err != nil { return wrapCertStoreError(err) } numRowsAffected, _ := res.RowsAffected() if numRowsAffected == 0 { return cferr.Wrap(cferr.CertStoreError, cferr.InsertionFailed, fmt.Errorf("failed to insert the OCSP record")) } if numRowsAffected != 1 { return wrapCertStoreError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected)) } return nil }
// Sign creates a new self-signed certificate. func Sign(priv crypto.Signer, csrPEM []byte, profile *config.SigningProfile) ([]byte, error) { if profile == nil { return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("no profile for self-signing")) } p, _ := pem.Decode(csrPEM) if p == nil || p.Type != "CERTIFICATE REQUEST" { return nil, cferr.New(cferr.CSRError, cferr.BadRequest) } template, err := parseCertificateRequest(priv, p.Bytes) if err != nil { return nil, err } pub := template.PublicKey encodedpub, err := x509.MarshalPKIXPublicKey(pub) if err != nil { return nil, err } var subPKI subjectPublicKeyInfo _, err = asn1.Unmarshal(encodedpub, &subPKI) if err != nil { return nil, err } pubhash := sha1.New() pubhash.Write(subPKI.SubjectPublicKey.Bytes) var ( eku []x509.ExtKeyUsage ku x509.KeyUsage expiry time.Duration crlURL, ocspURL string ) // The third value returned from Usages is a list of unknown key usages. // This should be used when validating the profile at load, and isn't used // here. ku, eku, _ = profile.Usages() expiry = profile.Expiry if ku == 0 && len(eku) == 0 { err = cferr.New(cferr.PolicyError, cferr.NoKeyUsages) return nil, err } if expiry == 0 { expiry = threeMonths } now := time.Now() serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { err = cferr.Wrap(cferr.CSRError, cferr.Unknown, err) return nil, err } template.SerialNumber = serialNumber template.NotBefore = now.Add(-5 * time.Minute).UTC() template.NotAfter = now.Add(expiry).UTC() template.KeyUsage = ku template.ExtKeyUsage = eku template.BasicConstraintsValid = true template.IsCA = profile.CA template.SubjectKeyId = pubhash.Sum(nil) if ocspURL != "" { template.OCSPServer = []string{ocspURL} } if crlURL != "" { template.CRLDistributionPoints = []string{crlURL} } if len(profile.IssuerURL) != 0 { template.IssuingCertificateURL = profile.IssuerURL } cert, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv) if err != nil { return nil, err } cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}) return cert, nil }