// Tests on verifying the rebundle flag and error code in Bundle.Status when rebundling. func TestRebundleFromPEM(t *testing.T) { newBundler := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, interL1, "") newBundle, err := newBundler.BundleFromPEMorDER(expiredBundlePEM, nil, Optimal, "") if err != nil { t.Fatalf("Re-bundle failed. %s", err.Error()) } newChain := newBundle.Chain if len(newChain) != 2 { t.Fatalf("Expected bundle chain length is 2. Got %d.", len(newChain)) } expiredChain, _ := helpers.ParseCertificatesPEM(expiredBundlePEM) for i, cert := range newChain { old := expiredChain[i] if i == 0 { if !bytes.Equal(old.Signature, cert.Signature) { t.Fatal("Leaf cert should be the same.") } } else { if bytes.Equal(old.Signature, cert.Signature) { t.Fatal("Intermediate cert should be different.") } } } // The status must be {Code: ExpiringBit is not set, IsRebundled:true, ExpiringSKIs:{}} if len(newBundle.Status.ExpiringSKIs) != 0 || !newBundle.Status.IsRebundled || newBundle.Status.Code&errors.BundleExpiringBit != 0 { t.Fatal("Rebundle Status is incorrect.") } }
// 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 }
// TrustPEM takes a source file containing one or more certificates // and adds them to the trust store. func TrustPEM(metadata map[string]string) ([]*x509.Certificate, error) { sourceFile, ok := metadata["source"] if !ok { return nil, errors.New("transport: PEM source requires a source file") } in, err := ioutil.ReadFile(sourceFile) if err != nil { return nil, err } return helpers.ParseCertificatesPEM(in) }
// NewCFSSL produces a new CFSSL root. func NewCFSSL(metadata map[string]string) ([]*x509.Certificate, error) { host, ok := metadata["host"] if !ok { return nil, errors.New("transport: CFSSL root provider requires a host") } label := metadata["label"] profile := metadata["profile"] srv := client.NewServer(host) data, err := json.Marshal(info.Req{Label: label, Profile: profile}) if err != nil { return nil, err } resp, err := srv.Info(data) if err != nil { return nil, err } return helpers.ParseCertificatesPEM([]byte(resp.Certificate)) }
// newCustomizedBundleCreator is a helper function that returns a new Bundler // takes specified CA bundle, intermediate bundle, and any additional intermdiate certs to generate a bundler. func newCustomizedBundlerFromFile(t *testing.T, caBundle, intBundle, adhocInters string) (b *Bundler) { b, err := NewBundler(caBundle, intBundle) if err != nil { t.Fatal(err) } if adhocInters != "" { moreIntersPEM, err := ioutil.ReadFile(adhocInters) if err != nil { t.Fatalf("Read additional intermediates failed. %v", err) } intermediates, err := helpers.ParseCertificatesPEM(moreIntersPEM) if err != nil { t.Fatalf("Parsing additional intermediates failed. %s", err.Error()) } for _, c := range intermediates { b.IntermediatePool.AddCert(c) } } 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) }
func TestCreateCertificateChain(t *testing.T) { // N is the number of certificates that will be chained together. N := 10 // --- TEST: Create a chain of one certificate. --- // encodedChainFromCode, _, err := CreateCertificateChain([]csr.CertificateRequest{CARequest}) checkError(err, t) // Now compare to a pre-made certificate chain using a JSON file containing // the same request data. CLIOutputFile := preMadeOutput CLIOutput, err := ioutil.ReadFile(CLIOutputFile) checkError(err, t) encodedChainFromCLI, err := cleanCLIOutput(CLIOutput, "cert") checkError(err, t) chainFromCode, err := helpers.ParseCertificatesPEM(encodedChainFromCode) checkError(err, t) chainFromCLI, err := helpers.ParseCertificatesPEM(encodedChainFromCLI) checkError(err, t) if !chainsEqual(chainFromCode, chainFromCLI) { unequalFieldSlices := checkFieldsOfChains(chainFromCode, chainFromCLI) for i, unequalFields := range unequalFieldSlices { if len(unequalFields) > 0 { t.Log("The certificate chains held unequal fields for chain " + strconv.Itoa(i)) t.Log("The following fields were unequal:") for _, field := range unequalFields { t.Log("\t" + field) } } } t.Fatal("Certificate chains unequal.") } // --- TEST: Create a chain of N certificates. --- // // First we make a slice of N requests. We make each slightly different. cnGrabBag := []string{"example", "invalid", "test"} topLevelDomains := []string{".com", ".net", ".org"} subDomains := []string{"www.", "secure.", "ca.", ""} countryGrabBag := []string{"USA", "China", "England", "Vanuatu"} stateGrabBag := []string{"California", "Texas", "Alaska", "London"} localityGrabBag := []string{"San Francisco", "Houston", "London", "Oslo"} orgGrabBag := []string{"Internet Widgets, LLC", "CloudFlare, Inc."} orgUnitGrabBag := []string{"Certificate Authority", "Systems Engineering"} requests := make([]csr.CertificateRequest, N) requests[0] = CARequest for i := 1; i < N; i++ { requests[i] = baseRequest cn := randomElement(cnGrabBag) tld := randomElement(topLevelDomains) subDomain1 := randomElement(subDomains) subDomain2 := randomElement(subDomains) country := randomElement(countryGrabBag) state := randomElement(stateGrabBag) locality := randomElement(localityGrabBag) org := randomElement(orgGrabBag) orgUnit := randomElement(orgUnitGrabBag) requests[i].CN = cn + "." + tld requests[i].Names = []csr.Name{ {C: country, ST: state, L: locality, O: org, OU: orgUnit, }, } hosts := []string{subDomain1 + requests[i].CN} if subDomain2 != subDomain1 { hosts = append(hosts, subDomain2+requests[i].CN) } requests[i].Hosts = hosts } // Now we make a certificate chain out of these requests. encodedCertChain, _, err := CreateCertificateChain(requests) checkError(err, t) // To test this chain, we compare the data encoded in each certificate to // each request we used to generate the chain. chain, err := helpers.ParseCertificatesPEM(encodedCertChain) checkError(err, t) if len(chain) != len(requests) { t.Log("Length of chain: " + strconv.Itoa(len(chain))) t.Log("Length of requests: " + strconv.Itoa(len(requests))) t.Fatal("Length of chain not equal to length of requests.") } mismatchOccurred := false for i := 0; i < len(chain); i++ { certEqualsRequest, unequalFields := certEqualsRequest(chain[i], requests[i]) if !certEqualsRequest { mismatchOccurred = true t.Log( "Certificate " + strconv.Itoa(i) + " and request " + strconv.Itoa(i) + " unequal.", ) t.Log("Unequal fields for index " + strconv.Itoa(i) + ":") for _, field := range unequalFields { t.Log("\t" + field) } } } // TODO: check that each certificate is actually signed by the previous one if mismatchOccurred { t.Fatal("Unequal certificate(s) and request(s) found.") } // --- TEST: Create a chain of certificates with invalid path lengths. --- // // Other invalid chains? }