// 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) }
// 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) }
func makeCASigner(certBytes, keyBytes []byte, sigAlgo x509.SignatureAlgorithm, t *testing.T) signer.Signer { cert, err := helpers.ParseCertificatePEM(certBytes) if err != nil { t.Fatal(err) } key, err := helpers.ParsePrivateKeyPEM(keyBytes) if err != nil { t.Fatal(err) } defaultProfile := &config.SigningProfile{ Usage: []string{"cert sign"}, CA: true, Expiry: time.Hour, ExpiryString: "1h", } policy := &config.Signing{ Profiles: map[string]*config.SigningProfile{}, Default: defaultProfile, } s, err := local.NewSigner(key, cert, sigAlgo, policy) if err != nil { t.Fatal(err) } return s }
func TestCAIssuing(t *testing.T) { var caCerts = []string{testCaFile, testECDSACaFile} var caKeys = []string{testCaKeyFile, testECDSACaKeyFile} var interCSRs = []string{ecdsaInterCSR, rsaInterCSR} var interKeys = []string{ecdsaInterKey, rsaInterKey} var CAPolicy = &config.Signing{ Default: &config.SigningProfile{ Usage: []string{"cert sign", "crl sign"}, ExpiryString: "1h", Expiry: 1 * time.Hour, CA: true, }, } var hostname = "cloudflare-inter.com" // Each RSA or ECDSA root CA issues two intermediate CAs (one ECDSA and one RSA). // For each intermediate CA, use it to issue additional RSA and ECDSA intermediate CSRs. for i, caFile := range caCerts { caKeyFile := caKeys[i] s := newCustomSigner(t, caFile, caKeyFile) s.policy = CAPolicy for j, csr := range interCSRs { csrBytes, _ := ioutil.ReadFile(csr) certBytes, err := s.Sign(signer.SignRequest{Hosts: signer.SplitHosts(hostname), Request: string(csrBytes)}) if err != nil { t.Fatal(err) } interCert, err := helpers.ParseCertificatePEM(certBytes) if err != nil { t.Fatal(err) } keyBytes, _ := ioutil.ReadFile(interKeys[j]) interKey, _ := helpers.ParsePrivateKeyPEM(keyBytes) interSigner := &Signer{interCert, interKey, CAPolicy, signer.DefaultSigAlgo(interKey), nil} for _, anotherCSR := range interCSRs { anotherCSRBytes, _ := ioutil.ReadFile(anotherCSR) bytes, err := interSigner.Sign( signer.SignRequest{ Hosts: signer.SplitHosts(hostname), Request: string(anotherCSRBytes), }) if err != nil { t.Fatal(err) } cert, err := helpers.ParseCertificatePEM(bytes) if err != nil { t.Fatal(err) } if cert.SignatureAlgorithm != interSigner.SigAlgo() { t.Fatal("Cert Signature Algorithm does not match the issuer.") } } } } }
// NewFromPEM creates a new root certificate from the key file passed in. func NewFromPEM(req *csr.CertificateRequest, keyFile string) (cert, csrPEM []byte, err error) { privData, err := ioutil.ReadFile(keyFile) if err != nil { return nil, nil, err } priv, err := helpers.ParsePrivateKeyPEM(privData) if err != nil { return nil, nil, err } return NewFromSigner(req, priv) }
// NewCRLFromFile takes in a list of serial numbers, one per line, as well as the issuing certificate // of the CRL, and the private key. This function is then used to parse the list and generate a CRL func NewCRLFromFile(serialList, issuerFile, keyFile []byte, expiryTime string) ([]byte, error) { var revokedCerts []pkix.RevokedCertificate var oneWeek = time.Duration(604800) * time.Second expiryInt, err := strconv.ParseInt(expiryTime, 0, 32) if err != nil { return nil, err } newDurationFromInt := time.Duration(expiryInt) * time.Second newExpiryTime := time.Now().Add(newDurationFromInt) if expiryInt == 0 { newExpiryTime = time.Now().Add(oneWeek) } // Parse the PEM encoded certificate issuerCert, err := helpers.ParseCertificatePEM(issuerFile) if err != nil { return nil, err } // Split input file by new lines individualCerts := strings.Split(string(serialList), "\n") // For every new line, create a new revokedCertificate and add it to slice for _, value := range individualCerts { if len(strings.TrimSpace(value)) == 0 { continue } tempBigInt := new(big.Int) tempBigInt.SetString(value, 10) tempCert := pkix.RevokedCertificate{ SerialNumber: tempBigInt, RevocationTime: time.Now(), } revokedCerts = append(revokedCerts, tempCert) } // Parse the key given key, err := helpers.ParsePrivateKeyPEM(keyFile) if err != nil { log.Debug("Malformed private key %v", err) return nil, err } return CreateGenericCRL(revokedCerts, key, issuerCert, newExpiryTime) }
// TestBadReGenerator ensures that a request that fails the ParseCSR is // not processed. func TestBadReGenerate(t *testing.T) { var req = &CertificateRequest{ Names: []Name{ { C: "US", ST: "California", L: "San Francisco", O: "CloudFlare", OU: "Systems Engineering", }, }, CN: "cloudflare.com", Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1"}, KeyRequest: &BasicKeyRequest{"ecdsa", 256}, } csr, key, err := ParseRequest(req) if err != nil { t.Fatalf("%v", err) } priv, err := helpers.ParsePrivateKeyPEM(key) if err != nil { t.Fatalf("%v", err) } csr, err = Generate(priv, req) if err != nil { t.Fatalf("%v", err) } block := pem.Block{ Type: "CERTIFICATE REQUEST", Headers: map[string]string{ "Location": "UCSD", }, Bytes: csr, } csr = pem.EncodeToMemory(&block) _, err = Regenerate(priv, csr) if err == nil { t.Fatalf("%v", err) } }
// TestReGenerate ensures Regenerate() is abel to use the provided CSR as a template for signing a new // CSR using priv. func TestReGenerate(t *testing.T) { var req = &CertificateRequest{ Names: []Name{ { C: "US", ST: "California", L: "San Francisco", O: "CloudFlare", OU: "Systems Engineering", }, }, CN: "cloudflare.com", Hosts: []string{"cloudflare.com", "www.cloudflare.com", "192.168.0.1"}, KeyRequest: &BasicKeyRequest{"ecdsa", 256}, } csr, key, err := ParseRequest(req) if err != nil { t.Fatalf("%v", err) } priv, err := helpers.ParsePrivateKeyPEM(key) if err != nil { t.Fatalf("%v", err) } csr, err = Generate(priv, req) if err != nil { t.Fatalf("%v", err) } if _, _, err = helpers.ParseCSR(csr); err != nil { t.Fatalf("%v", err) } _, err = Regenerate(priv, csr) if err != nil { t.Fatalf("%v", err) } }
// 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 }
// BundleFromPEMorDER builds a certificate bundle from the set of byte // slices containing the PEM or DER-encoded certificate(s), private key. func (b *Bundler) BundleFromPEMorDER(certsRaw, keyPEM []byte, flavor BundleFlavor, password string) (*Bundle, error) { log.Debug("bundling from PEM files") var key crypto.Signer var err error if len(keyPEM) != 0 { key, err = helpers.ParsePrivateKeyPEM(keyPEM) if err != nil { log.Debugf("failed to parse private key: %v", err) return nil, err } } certs, err := helpers.ParseCertificatesPEM(certsRaw) if err != nil { // If PEM doesn't work try DER var keyDER crypto.Signer var errDER error certs, keyDER, errDER = helpers.ParseCertificatesDER(certsRaw, password) // Only use DER key if no key read from file if key == nil && keyDER != nil { key = keyDER } if errDER != nil { log.Debugf("failed to parse certificates: %v", err) // If neither parser works pass along PEM error return nil, err } } if len(certs) == 0 { log.Debugf("no certificates found") return nil, errors.New(errors.CertificateError, errors.DecodeFailed) } log.Debugf("bundle ready") return b.Bundle(certs, key, flavor) }
// RenewFromPEM re-creates a root certificate from the CA cert and key // files. The resulting root certificate will have the input CA certificate // as the template and have the same expiry length. E.g. the exsiting CA // is valid for a year from Jan 01 2015 to Jan 01 2016, the renewed certificate // will be valid from now and expire in one year as well. func RenewFromPEM(caFile, keyFile string) ([]byte, error) { caBytes, err := ioutil.ReadFile(caFile) if err != nil { return nil, err } ca, err := helpers.ParseCertificatePEM(caBytes) if err != nil { return nil, err } keyBytes, err := ioutil.ReadFile(keyFile) if err != nil { return nil, err } key, err := helpers.ParsePrivateKeyPEM(keyBytes) if err != nil { return nil, err } return RenewFromSigner(ca, key) }
// Handle responds to requests for crl generation. It creates this crl // based off of the given certificate, serial numbers, and private key func crlHandler(w http.ResponseWriter, r *http.Request) error { var revokedCerts []pkix.RevokedCertificate var oneWeek = time.Duration(604800) * time.Second var newExpiryTime = time.Now() body, err := ioutil.ReadAll(r.Body) if err != nil { return err } r.Body.Close() req := &jsonCRLRequest{} err = json.Unmarshal(body, req) if err != nil { log.Error(err) } if req.ExpiryTime != "" { expiryTime := strings.TrimSpace(req.ExpiryTime) expiryInt, err := strconv.ParseInt(expiryTime, 0, 32) if err != nil { return err } newExpiryTime = time.Now().Add((time.Duration(expiryInt) * time.Second)) } if req.ExpiryTime == "" { newExpiryTime = time.Now().Add(oneWeek) } if err != nil { return err } cert, err := helpers.ParseCertificatePEM([]byte(req.Certificate)) if err != nil { log.Error("Error from ParseCertificatePEM", err) return errors.NewBadRequestString("Malformed certificate") } for _, value := range req.SerialNumber { tempBigInt := new(big.Int) tempBigInt.SetString(value, 10) tempCert := pkix.RevokedCertificate{ SerialNumber: tempBigInt, RevocationTime: time.Now(), } revokedCerts = append(revokedCerts, tempCert) } key, err := helpers.ParsePrivateKeyPEM([]byte(req.PrivateKey)) if err != nil { log.Debug("Malformed private key %v", err) return errors.NewBadRequestString("Malformed Private Key") } result, err := cert.CreateCRL(rand.Reader, key, revokedCerts, time.Now(), newExpiryTime) return api.SendResponse(w, result) }
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 selfSignMain(args []string, c cli.Config) (err error) { if c.Hostname == "" && !c.IsCA { c.Hostname, args, err = cli.PopFirstArgument(args) if err != nil { return } } csrFile, args, err := cli.PopFirstArgument(args) if err != nil { return } csrFileBytes, err := cli.ReadStdin(csrFile) if err != nil { return } var req = csr.New() err = json.Unmarshal(csrFileBytes, req) if err != nil { return } var key, csrPEM []byte g := &csr.Generator{Validator: genkey.Validator} csrPEM, key, err = g.ProcessRequest(req) if err != nil { key = nil return } priv, err := helpers.ParsePrivateKeyPEM(key) if err != nil { key = nil return } var profile *config.SigningProfile // If there is a config, use its signing policy. Otherwise, leave policy == nil // and NewSigner will use DefaultConfig(). if c.CFG != nil { if c.Profile != "" && c.CFG.Signing.Profiles != nil { profile = c.CFG.Signing.Profiles[c.Profile] } } if profile == nil { profile = config.DefaultConfig() profile.Expiry = 2190 * time.Hour } cert, err := selfsign.Sign(priv, csrPEM, profile) if err != nil { key = nil priv = nil return } fmt.Fprintf(os.Stderr, `*** WARNING *** Self-signed certificates are dangerous. Use this self-signed certificate at your own risk. It is strongly recommended that these certificates NOT be used in production. *** WARNING *** `) cli.PrintCert(key, csrPEM, cert) return }
func TestInitCA(t *testing.T) { var req *csr.CertificateRequest hostname := "cloudflare.com" for _, param := range validKeyParams { req = &csr.CertificateRequest{ Names: []csr.Name{ { C: "US", ST: "California", L: "San Francisco", O: "CloudFlare", OU: "Systems Engineering", }, }, CN: hostname, Hosts: []string{hostname, "www." + hostname}, KeyRequest: ¶m, } certBytes, _, keyBytes, err := New(req) if err != nil { t.Fatal("InitCA failed:", err) } key, err := helpers.ParsePrivateKeyPEM(keyBytes) if err != nil { t.Fatal("InitCA private key parsing failed:", err) } cert, err := helpers.ParseCertificatePEM(certBytes) if err != nil { t.Fatal("InitCA cert parsing failed:", err) } // Verify key parameters. switch req.KeyRequest.Algo() { case "rsa": if cert.PublicKey.(*rsa.PublicKey).N.BitLen() != param.Size() { t.Fatal("Cert key length mismatch.") } if key.(*rsa.PrivateKey).N.BitLen() != param.Size() { t.Fatal("Private key length mismatch.") } case "ecdsa": if cert.PublicKey.(*ecdsa.PublicKey).Curve.Params().BitSize != param.Size() { t.Fatal("Cert key length mismatch.") } if key.(*ecdsa.PrivateKey).Curve.Params().BitSize != param.Size() { t.Fatal("Private key length mismatch.") } } // Start a signer var CAPolicy = &config.Signing{ Default: &config.SigningProfile{ Usage: []string{"cert sign", "crl sign"}, ExpiryString: "300s", Expiry: 300 * time.Second, CA: true, }, } s, err := local.NewSigner(key, cert, signer.DefaultSigAlgo(key), nil) if err != nil { t.Fatal("Signer Creation error:", err) } s.SetPolicy(CAPolicy) // Sign RSA and ECDSA customer CSRs. for _, csrFile := range csrFiles { csrBytes, err := ioutil.ReadFile(csrFile) if err != nil { t.Fatal("CSR loading error:", err) } req := signer.SignRequest{ Request: string(csrBytes), Hosts: signer.SplitHosts(hostname), Profile: "", Label: "", } bytes, err := s.Sign(req) if err != nil { t.Fatal(err) } customerCert, _ := helpers.ParseCertificatePEM(bytes) if customerCert.SignatureAlgorithm != s.SigAlgo() { t.Fatal("Signature Algorithm mismatch") } err = customerCert.CheckSignatureFrom(cert) if err != nil { t.Fatal("Signing CSR failed.", err) } } } }