Example #1
0
// 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
}
Example #2
0
// 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.BundleFromPEM(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.")
	}

}
Example #3
0
// 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

}
Example #4
0
// BundleFromPEM builds a certificate bundle from the set of byte
// slices containing the PEM-encoded certificate(s), private key.
func (b *Bundler) BundleFromPEM(certsPEM, keyPEM []byte, flavor BundleFlavor) (*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(certsPEM)
	if err != nil {
		log.Debugf("failed to parse certificates: %v", err)
		return nil, err
	} else 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)
}
Example #5
0
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?
}