Beispiel #1
0
// certsToRemove returns all the certifificates from oldCerts that aren't present
// in newCerts
func certsToRemove(oldCerts, newCerts []*x509.Certificate) map[string]*x509.Certificate {
	certsToRemove := make(map[string]*x509.Certificate)

	// If no newCerts were provided
	if len(newCerts) == 0 {
		return certsToRemove
	}

	// Populate a map with all the IDs from newCert
	var newCertMap = make(map[string]struct{})
	for _, cert := range newCerts {
		certID, err := trustmanager.FingerprintCert(cert)
		if err != nil {
			logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", certID, err)
			continue
		}
		newCertMap[certID] = struct{}{}
	}

	// Iterate over all the old certificates and check to see if we should remove them
	for _, cert := range oldCerts {
		certID, err := trustmanager.FingerprintCert(cert)
		if err != nil {
			logrus.Debugf("error while fingerprinting root certificate with certID: %s, %v", certID, err)
			continue
		}
		if _, ok := newCertMap[certID]; !ok {
			certsToRemove[certID] = cert
		}
	}

	return certsToRemove
}
// generateKeyAndCert deals with the creation and storage of a key and returns a cert
func generateKeyAndCert(gun string) (crypto.PrivateKey, *x509.Certificate, error) {
	// Generates a new RSA key
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, nil, fmt.Errorf("could not generate private key: %v", err)
	}

	// Creates a new Certificate template. We need the certificate to calculate the
	// TUF-compliant keyID
	//TODO (diogo): We're hardcoding the Organization to be the GUN. Probably want to
	// change it
	template := newCertificate(gun, gun)
	derBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate the certificate for key: %v", err)
	}

	// Encode the new certificate into PEM
	cert, err := x509.ParseCertificate(derBytes)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate the certificate for key: %v", err)
	}

	fingerprint := trustmanager.FingerprintCert(cert)
	// The key is going to be stored in the private directory, using the GUN and
	// the filename will be the TUF-compliant ID. The Store takes care of extensions.
	privKeyFilename := filepath.Join(gun, fingerprint)
	pemKey, err := trustmanager.KeyToPEM(key)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to generate the certificate for key: %v", err)
	}

	return key, cert, privKeyStore.Add(privKeyFilename, pemKey)
}
Beispiel #3
0
// Given a list of Ceritifcates in order of listing preference, pretty-prints
// the cert common name, fingerprint, and expiry
func prettyPrintCerts(certs []*x509.Certificate, writer io.Writer) {
	if len(certs) == 0 {
		writer.Write([]byte("\nNo trusted root certificates present.\n\n"))
		return
	}

	sort.Stable(certSorter(certs))

	table := getTable([]string{
		"GUN", "Fingerprint of Trusted Root Certificate", "Expires In"}, writer)

	for _, c := range certs {
		days := math.Floor(c.NotAfter.Sub(time.Now()).Hours() / 24)
		expiryString := "< 1 day"
		if days == 1 {
			expiryString = "1 day"
		} else if days > 1 {
			expiryString = fmt.Sprintf("%d days", int(days))
		}

		certID, err := trustmanager.FingerprintCert(c)
		if err != nil {
			fatalf("Could not fingerprint certificate: %v", err)
		}

		table.Append([]string{c.Subject.CommonName, certID, expiryString})
	}
	table.Render()
}
Beispiel #4
0
func printCert(cert *x509.Certificate) {
	timeDifference := cert.NotAfter.Sub(time.Now())
	certID, err := trustmanager.FingerprintCert(cert)
	if err != nil {
		fatalf("could not fingerprint certificate: %v", err)
	}

	fmt.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, certID, math.Floor(timeDifference.Hours()/24))
}
Beispiel #5
0
// parseAllCerts returns two maps, one with all of the leafCertificates and one
// with all the intermediate certificates found in signedRoot
func parseAllCerts(signedRoot *data.SignedRoot) (map[string]*x509.Certificate, map[string][]*x509.Certificate) {
	leafCerts := make(map[string]*x509.Certificate)
	intCerts := make(map[string][]*x509.Certificate)

	// Before we loop through all root keys available, make sure any exist
	rootRoles, ok := signedRoot.Signed.Roles["root"]
	if !ok {
		logrus.Debugf("tried to parse certificates from invalid root signed data")
		return nil, nil
	}

	logrus.Debugf("found the following root keys: %v", rootRoles.KeyIDs)
	// Iterate over every keyID for the root role inside of roots.json
	for _, keyID := range rootRoles.KeyIDs {
		// check that the key exists in the signed root keys map
		key, ok := signedRoot.Signed.Keys[keyID]
		if !ok {
			logrus.Debugf("error while getting data for keyID: %s", keyID)
			continue
		}

		// Decode all the x509 certificates that were bundled with this
		// Specific root key
		decodedCerts, err := trustmanager.LoadCertBundleFromPEM(key.Public())
		if err != nil {
			logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err)
			continue
		}

		// Get all non-CA certificates in the decoded certificates
		leafCertList := trustmanager.GetLeafCerts(decodedCerts)

		// If we got no leaf certificates or we got more than one, fail
		if len(leafCertList) != 1 {
			logrus.Debugf("invalid chain due to leaf certificate missing or too many leaf certificates for keyID: %s", keyID)
			continue
		}

		// Get the ID of the leaf certificate
		leafCert := leafCertList[0]
		leafID, err := trustmanager.FingerprintCert(leafCert)
		if err != nil {
			logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", keyID, err)
			continue
		}

		// Store the leaf cert in the map
		leafCerts[leafID] = leafCert

		// Get all the remainder certificates marked as a CA to be used as intermediates
		intermediateCerts := trustmanager.GetIntermediateCerts(decodedCerts)
		intCerts[leafID] = intermediateCerts
	}

	return leafCerts, intCerts
}
Beispiel #6
0
func prettyFormatCertIDs(certs []*x509.Certificate) string {
	ids := make([]string, 0, len(certs))
	for _, cert := range certs {
		id, err := trustmanager.FingerprintCert(cert)
		if err != nil {
			id = fmt.Sprintf("[Error %s]", err)
		}
		ids = append(ids, id)
	}
	return strings.Join(ids, ", ")
}
/*
ValidateRoot iterates over every root key included in the TUF data and
attempts to validate the certificate by first checking for an exact match on
the certificate store, and subsequently trying to find a valid chain on the
trustedCAStore.

When this is being used with a notary repository, the dnsName parameter should
be the GUN associated with the repository.

Example TUF Content for root role:
"roles" : {
  "root" : {
    "threshold" : 1,
      "keyids" : [
        "e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38"
      ]
  },
 ...
}

Example TUF Content for root key:
"e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38" : {
	"keytype" : "RSA",
	"keyval" : {
	  "private" : "",
	  "public" : "Base64-encoded, PEM encoded x509 Certificate"
	}
}
*/
func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error {
	rootSigned := &data.Root{}
	err := json.Unmarshal(root.Signed, rootSigned)
	if err != nil {
		return err
	}

	certs := make(map[string]*data.PublicKey)
	for _, keyID := range rootSigned.Roles["root"].KeyIDs {
		// TODO(dlaw): currently assuming only one cert contained in
		// public key entry. Need to fix when we want to pass in chains.
		k, _ := pem.Decode([]byte(rootSigned.Keys[keyID].Public()))
		decodedCerts, err := x509.ParseCertificates(k.Bytes)
		if err != nil {
			logrus.Debugf("error while parsing root certificate with keyID: %s, %v", keyID, err)
			continue
		}
		// TODO(diogo): Assuming that first certificate is the leaf-cert. Need to
		// iterate over all decodedCerts and find a non-CA one (should be the last).
		leafCert := decodedCerts[0]

		leafID, err := trustmanager.FingerprintCert(leafCert)
		if err != nil {
			logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", keyID, err)
			continue
		}

		// Check to see if there is an exact match of this certificate.
		// Checking the CommonName is not required since ID is calculated over
		// Cert.Raw. It's included to prevent breaking logic with changes of how the
		// ID gets computed.
		_, err = km.trustedCertificateStore.GetCertificateByKeyID(leafID)
		if err == nil && leafCert.Subject.CommonName == dnsName {
			certs[keyID] = rootSigned.Keys[keyID]
		}

		// Check to see if this leafCertificate has a chain to one of the Root CAs
		// of our CA Store.
		certList := []*x509.Certificate{leafCert}
		err = trustmanager.Verify(km.trustedCAStore, dnsName, certList)
		if err == nil {
			certs[keyID] = rootSigned.Keys[keyID]
		}
	}

	if len(certs) < 1 {
		return errors.New("could not validate the path to a trusted root")
	}

	_, err = signed.VerifyRoot(root, 0, certs, 1)

	return err
}
Beispiel #8
0
// certsToRemove returns all the certificates from oldCerts that aren't present
// in newCerts.  Note that newCerts should never be empty, else this function will error.
// We expect newCerts to come from validateRootLeafCerts, which does not return empty sets.
func certsToRemove(oldCerts, newCerts []*x509.Certificate) (map[string]*x509.Certificate, error) {
	certsToRemove := make(map[string]*x509.Certificate)

	// Populate a map with all the IDs from newCert
	var newCertMap = make(map[string]struct{})
	for _, cert := range newCerts {
		certID, err := trustmanager.FingerprintCert(cert)
		if err != nil {
			logrus.Debugf("error while fingerprinting root certificate with keyID: %s, %v", certID, err)
			continue
		}
		newCertMap[certID] = struct{}{}
	}

	// We don't want to "rotate" certificates to an empty set, nor keep old certificates if the
	// new root does not trust them.  newCerts should come from validRootLeafCerts, which refuses
	// to return an empty set, and they should all be fingerprintable, so this should never happen
	// - fail just to be sure.
	if len(newCertMap) == 0 {
		return nil, &ErrRootRotationFail{Reason: "internal error, got no certificates to rotate to"}
	}

	// Iterate over all the old certificates and check to see if we should remove them
	for _, cert := range oldCerts {
		certID, err := trustmanager.FingerprintCert(cert)
		if err != nil {
			logrus.Debugf("error while fingerprinting root certificate with certID: %s, %v", certID, err)
			continue
		}
		if _, ok := newCertMap[certID]; !ok {
			certsToRemove[certID] = cert
		}
	}

	return certsToRemove, nil
}
Beispiel #9
0
func (t trustPinChecker) certsCheck(leafCert *x509.Certificate, intCerts []*x509.Certificate) bool {
	// reconstruct the leaf + intermediate cert chain, which is bundled as {leaf, intermediates...},
	// in order to get the matching id in the root file
	leafCertID, err := trustmanager.FingerprintCert(leafCert)
	if err != nil {
		return false
	}
	rootKeys := trustmanager.CertsToKeys([]*x509.Certificate{leafCert}, map[string][]*x509.Certificate{leafCertID: intCerts})
	for keyID := range rootKeys {
		if utils.StrSliceContains(t.pinnedCertIDs, keyID) {
			return true
		}
	}
	return false
}
Beispiel #10
0
/*
validateRoot iterates over every root key included in the TUF data and attempts
to validate the certificate by first checking for an exact match on the certificate
store, and subsequently trying to find a valid chain on the caStore.

Example TUF Content for root role:
"roles" : {
  "root" : {
    "threshold" : 1,
      "keyids" : [
        "e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38"
      ]
  },
 ...
}

Example TUF Content for root key:
"e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38" : {
	"keytype" : "RSA",
	"keyval" : {
	  "private" : "",
	  "public" : "Base64-encoded, PEM encoded x509 Certificate"
	}
}
*/
func validateRoot(gun string, root *data.Signed) error {
	rootSigned := &data.Root{}
	err := json.Unmarshal(root.Signed, rootSigned)
	if err != nil {
		return err
	}
	certs := make(map[string]*data.PublicKey)
	for _, fingerprint := range rootSigned.Roles["root"].KeyIDs {
		// TODO(dlaw): currently assuming only one cert contained in
		// public key entry. Need to fix when we want to pass in chains.
		k, _ := pem.Decode([]byte(rootSigned.Keys["kid"].Public()))

		decodedCerts, err := x509.ParseCertificates(k.Bytes)
		if err != nil {
			continue
		}

		// TODO(diogo): Assuming that first certificate is the leaf-cert. Need to
		// iterate over all decodedCerts and find a non-CA one (should be the last).
		leafCert := decodedCerts[0]
		leafID := trustmanager.FingerprintCert(leafCert)

		// Check to see if there is an exact match of this certificate.
		// Checking the CommonName is not required since ID is calculated over
		// Cert.Raw. It's included to prevent breaking logic with changes of how the
		// ID gets computed.
		_, err = certificateStore.GetCertificateByFingerprint(leafID)
		if err == nil && leafCert.Subject.CommonName == gun {
			certs[fingerprint] = rootSigned.Keys[fingerprint]
		}

		// Check to see if this leafCertificate has a chain to one of the Root CAs
		// of our CA Store.
		certList := []*x509.Certificate{leafCert}
		err = trustmanager.Verify(caStore, gun, certList)
		if err == nil {
			certs[fingerprint] = rootSigned.Keys[fingerprint]
		}
	}
	_, err = signed.VerifyRoot(root, 0, certs, 1)

	return err
}
Beispiel #11
0
func keysGenerate(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		cmd.Usage()
		fatalf("must specify a GUN")
	}

	//TODO (diogo): Validate GUNs. Don't allow '/' or '\' for now.
	gun := args[0]
	if gun[0:1] == "/" || gun[0:1] == "\\" {
		fatalf("invalid Global Unique Name: %s", gun)
	}

	_, cert, err := generateKeyAndCert(gun)
	if err != nil {
		fatalf("could not generate key: %v", err)
	}

	caStore.AddCert(cert)
	fingerprint := trustmanager.FingerprintCert(cert)
	fmt.Println("Generated new keypair with ID: ", string(fingerprint))
}
Beispiel #12
0
// certRemove deletes a certificate given a cert ID or a gun
func certRemove(cmd *cobra.Command, args []string) {
	// If the user hasn't provided -g with a gun, or a cert ID, show usage
	// If the user provided -g and a cert ID, also show usage
	if (len(args) < 1 && certRemoveGUN == "") || (len(args) > 0 && certRemoveGUN != "") {
		cmd.Usage()
		fatalf("Must specify the cert ID or the GUN of the certificates to remove")
	}
	parseConfig()

	trustDir := mainViper.GetString("trust_dir")
	certManager, err := certs.NewManager(trustDir)
	if err != nil {
		fatalf("Failed to create a new truststore manager with directory: %s", trustDir)
	}

	var certsToRemove []*x509.Certificate

	// If there is no GUN, we expect a cert ID
	if certRemoveGUN == "" {
		certID := args[0]
		// This is an invalid ID
		if len(certID) != idSize {
			fatalf("Invalid certificate ID provided: %s", certID)
		}
		// Attempt to find this certificates
		cert, err := certManager.TrustedCertificateStore().GetCertificateByCertID(certID)
		if err != nil {
			fatalf("Unable to retrieve certificate with cert ID: %s", certID)
		}
		certsToRemove = append(certsToRemove, cert)
	} else {
		// We got the -g flag, it's a GUN
		toRemove, err := certManager.TrustedCertificateStore().GetCertificatesByCN(
			certRemoveGUN)
		if err != nil {
			fatalf("%v", err)
		}
		certsToRemove = append(certsToRemove, toRemove...)
	}

	// List all the keys about to be removed
	cmd.Printf("The following certificates will be removed:\n\n")
	for _, cert := range certsToRemove {
		// This error can't occur because we're getting certs off of an
		// x509 store that indexes by ID.
		certID, _ := trustmanager.FingerprintCert(cert)
		cmd.Printf("%s - %s\n", cert.Subject.CommonName, certID)
	}
	cmd.Println("\nAre you sure you want to remove these certificates? (yes/no)")

	// Ask for confirmation before removing certificates, unless -y is provided
	if !certRemoveYes {
		confirmed := askConfirm()
		if !confirmed {
			fatalf("Aborting action.")
		}
	}

	// Remove all the certs
	for _, cert := range certsToRemove {
		err = certManager.TrustedCertificateStore().RemoveCert(cert)
		if err != nil {
			fatalf("Failed to remove root certificate for %s", cert.Subject.CommonName)
		}
	}
}
Beispiel #13
0
func testInitRepo(t *testing.T, rootType string) {
	gun := "docker.com/notary"
	// Temporary directory where test files will be created
	tempBaseDir, err := ioutil.TempDir("", "notary-test-")
	defer os.RemoveAll(tempBaseDir)

	assert.NoError(t, err, "failed to create a temporary directory: %s", err)

	ts, _ := simpleTestServer(t)
	defer ts.Close()

	repo, rootKeyID := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL)

	// Inspect contents of the temporary directory
	expectedDirs := []string{
		"private",
		filepath.Join("private", "tuf_keys", filepath.FromSlash(gun)),
		filepath.Join("private", "root_keys"),
		"trusted_certificates",
		filepath.Join("trusted_certificates", filepath.FromSlash(gun)),
		"tuf",
		filepath.Join("tuf", filepath.FromSlash(gun), "metadata"),
	}
	for _, dir := range expectedDirs {
		fi, err := os.Stat(filepath.Join(tempBaseDir, dir))
		assert.NoError(t, err, "missing directory in base directory: %s", dir)
		assert.True(t, fi.Mode().IsDir(), "%s is not a directory", dir)
	}

	// Look for keys in private. The filenames should match the key IDs
	// in the private key store.
	keyFileStore, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever)
	assert.NoError(t, err)

	privKeyList := keyFileStore.ListFiles()
	for _, privKeyName := range privKeyList {
		privKeyFileName := filepath.Join(keyFileStore.BaseDir(), privKeyName)
		_, err := os.Stat(privKeyFileName)
		assert.NoError(t, err, "missing private key: %s", privKeyName)
	}

	// Look for keys in root_keys
	// There should be a file named after the key ID of the root key we
	// passed in.
	rootKeyFilename := rootKeyID + "_root.key"
	_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
	assert.NoError(t, err, "missing root key")

	certificates := repo.KeyStoreManager.TrustedCertificateStore().GetCertificates()
	assert.Len(t, certificates, 1, "unexpected number of certificates")

	certID, err := trustmanager.FingerprintCert(certificates[0])
	assert.NoError(t, err, "unable to fingerprint the certificate")

	// There should be a trusted certificate
	_, err = os.Stat(filepath.Join(tempBaseDir, "trusted_certificates", filepath.FromSlash(gun), certID+".crt"))
	assert.NoError(t, err, "missing trusted certificate")

	// Sanity check the TUF metadata files. Verify that they exist, the JSON is
	// well-formed, and the signatures exist. For the root.json file, also check
	// that the root, snapshot, and targets key IDs are present.
	expectedTUFMetadataFiles := []string{
		filepath.Join("tuf", filepath.FromSlash(gun), "metadata", "root.json"),
		filepath.Join("tuf", filepath.FromSlash(gun), "metadata", "snapshot.json"),
		filepath.Join("tuf", filepath.FromSlash(gun), "metadata", "targets.json"),
	}
	for _, filename := range expectedTUFMetadataFiles {
		fullPath := filepath.Join(tempBaseDir, filename)
		_, err := os.Stat(fullPath)
		assert.NoError(t, err, "missing TUF metadata file: %s", filename)

		jsonBytes, err := ioutil.ReadFile(fullPath)
		assert.NoError(t, err, "error reading TUF metadata file %s: %s", filename, err)

		var decoded data.Signed
		err = json.Unmarshal(jsonBytes, &decoded)
		assert.NoError(t, err, "error parsing TUF metadata file %s: %s", filename, err)

		assert.Len(t, decoded.Signatures, 1, "incorrect number of signatures in TUF metadata file %s", filename)

		assert.NotEmpty(t, decoded.Signatures[0].KeyID, "empty key ID field in TUF metadata file %s", filename)
		assert.NotEmpty(t, decoded.Signatures[0].Method, "empty method field in TUF metadata file %s", filename)
		assert.NotEmpty(t, decoded.Signatures[0].Signature, "empty signature in TUF metadata file %s", filename)

		// Special case for root.json: also check that the signed
		// content for keys and roles
		if strings.HasSuffix(filename, "root.json") {
			var decodedRoot data.Root
			err := json.Unmarshal(decoded.Signed, &decodedRoot)
			assert.NoError(t, err, "error parsing root.json signed section: %s", err)

			assert.Equal(t, "Root", decodedRoot.Type, "_type mismatch in root.json")

			// Expect 4 keys in the Keys map: root, targets, snapshot, timestamp
			assert.Len(t, decodedRoot.Keys, 4, "wrong number of keys in root.json")

			roleCount := 0
			for role := range decodedRoot.Roles {
				roleCount++
				if role != "root" && role != "snapshot" && role != "targets" && role != "timestamp" {
					t.Fatalf("unexpected role %s in root.json", role)
				}
			}
			assert.Equal(t, 4, roleCount, "wrong number of roles (%d) in root.json", roleCount)
		}
	}
}
Beispiel #14
0
func printCert(cert *x509.Certificate) {
	timeDifference := cert.NotAfter.Sub(time.Now())
	fingerprint := trustmanager.FingerprintCert(cert)
	fmt.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, fingerprint, math.Floor(timeDifference.Hours()/24))
}
Beispiel #15
0
/*
ValidateRoot receives a new root, validates its correctness and attempts to
do root key rotation if needed.

First we list the current trusted certificates we have for a particular GUN. If
that list is non-empty means that we've already seen this repository before, and
have a list of trusted certificates for it. In this case, we use this list of
certificates to attempt to validate this root file.

If the previous validation succeeds, we check the integrity of the root by
making sure that it is validated by itself. This means that we will attempt to
validate the root data with the certificates that are included in the root keys
themselves.

However, if we do not have any current trusted certificates for this GUN, we
check if there are any pinned certificates specified in the trust_pinning section
of the notary client config.  If this section specifies a Certs section with this
GUN, we attempt to validate that the certificates present in the downloaded root
file match the pinned ID.

If the Certs section is empty for this GUN, we check if the trust_pinning
section specifies a CA section specified in the config for this GUN.  If so, we check
that the specified CA is valid and has signed a certificate included in the downloaded
root file.  The specified CA can be a prefix for this GUN.

If both the Certs and CA configs do not match this GUN, we fall back to the TOFU
section in the config: if true, we trust certificates specified in the root for
this GUN. If later we see a different certificate for that certificate, we return
an ErrValidationFailed error.

Note that since we only allow trust data to be downloaded over an HTTPS channel
we are using the current public PKI to validate the first download of the certificate
adding an extra layer of security over the normal (SSH style) trust model.
We shall call this: TOFUS.

Validation failure at any step will result in an ErrValidationFailed error.
*/
func ValidateRoot(certStore trustmanager.X509Store, root *data.Signed, gun string, trustPinning TrustPinConfig) error {
	logrus.Debugf("entered ValidateRoot with dns: %s", gun)
	signedRoot, err := data.RootFromSigned(root)
	if err != nil {
		return err
	}

	rootRole, err := signedRoot.BuildBaseRole(data.CanonicalRootRole)
	if err != nil {
		return err
	}

	// Retrieve all the leaf and intermediate certificates in root for which the CN matches the GUN
	allLeafCerts, allIntCerts := parseAllCerts(signedRoot)
	certsFromRoot, err := validRootLeafCerts(allLeafCerts, gun)
	if err != nil {
		logrus.Debugf("error retrieving valid leaf certificates for: %s, %v", gun, err)
		return &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}
	}

	// Retrieve all the trusted certificates that match this gun
	trustedCerts, err := certStore.GetCertificatesByCN(gun)
	if err != nil {
		// If the error that we get back is different than ErrNoCertificatesFound
		// we couldn't check if there are any certificates with this CN already
		// trusted. Let's take the conservative approach and return a failed validation
		if _, ok := err.(*trustmanager.ErrNoCertificatesFound); !ok {
			logrus.Debugf("error retrieving trusted certificates for: %s, %v", gun, err)
			return &ErrValidationFail{Reason: "unable to retrieve trusted certificates"}
		}
	}
	// If we have certificates that match this specific GUN, let's make sure to
	// use them first to validate that this new root is valid.
	if len(trustedCerts) != 0 {
		logrus.Debugf("found %d valid root certificates for %s: %s", len(trustedCerts), gun,
			prettyFormatCertIDs(trustedCerts))
		err = signed.VerifySignatures(
			root, data.BaseRole{Keys: trustmanager.CertsToKeys(trustedCerts, allIntCerts), Threshold: 1})
		if err != nil {
			logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
			return &ErrValidationFail{Reason: "failed to validate data with current trusted certificates"}
		}
	} else {
		logrus.Debugf("found no currently valid root certificates for %s, using trust_pinning config to bootstrap trust", gun)
		trustPinCheckFunc, err := NewTrustPinChecker(trustPinning, gun)
		if err != nil {
			return &ErrValidationFail{Reason: err.Error()}
		}

		validPinnedCerts := []*x509.Certificate{}
		for _, cert := range certsFromRoot {
			certID, err := trustmanager.FingerprintCert(cert)
			if err != nil {
				continue
			}
			if ok := trustPinCheckFunc(cert, allIntCerts[certID]); !ok {
				continue
			}
			validPinnedCerts = append(validPinnedCerts, cert)
		}
		if len(validPinnedCerts) == 0 {
			return &ErrValidationFail{Reason: "unable to match any certificates to trust_pinning config"}
		}
		certsFromRoot = validPinnedCerts
	}

	// Validate the integrity of the new root (does it have valid signatures)
	// Note that certsFromRoot is guaranteed to be unchanged only if we had prior cert data for this GUN or enabled TOFUS
	// If we attempted to pin a certain certificate or CA, certsFromRoot could have been pruned accordingly
	err = signed.VerifySignatures(root, data.BaseRole{
		Keys: trustmanager.CertsToKeys(certsFromRoot, allIntCerts), Threshold: rootRole.Threshold})
	if err != nil {
		logrus.Debugf("failed to verify TUF data for: %s, %v", gun, err)
		return &ErrValidationFail{Reason: "failed to validate integrity of roots"}
	}

	// Getting here means:
	// A) we had trusted certificates and both the old and new validated this root.
	// or
	// B) we had no trusted certificates but the new set of certificates has integrity (self-signed).
	logrus.Debugf("entering root certificate rotation for: %s", gun)

	// Do root certificate rotation: we trust only the certs present in the new root
	// First we add all the new certificates (even if they already exist)
	for _, cert := range certsFromRoot {
		err := certStore.AddCert(cert)
		if err != nil {
			// If the error is already exists we don't fail the rotation
			if _, ok := err.(*trustmanager.ErrCertExists); ok {
				logrus.Debugf("ignoring certificate addition to: %s", gun)
				continue
			}
			logrus.Debugf("error adding new trusted certificate for: %s, %v", gun, err)
		}
	}

	// Now we delete old certificates that aren't present in the new root
	oldCertsToRemove, err := certsToRemove(trustedCerts, certsFromRoot)
	if err != nil {
		logrus.Debugf("inconsistency when removing old certificates: %v", err)
		return err
	}
	for certID, cert := range oldCertsToRemove {
		logrus.Debugf("removing certificate with certID: %s", certID)
		err = certStore.RemoveCert(cert)
		if err != nil {
			logrus.Debugf("failed to remove trusted certificate with keyID: %s, %v", certID, err)
			return &ErrRootRotationFail{Reason: "failed to rotate root keys"}
		}
	}

	logrus.Debugf("Root validation succeeded for %s", gun)
	return nil
}
Beispiel #16
0
// certRemove deletes a certificate given a cert ID or a gun
// If given a gun, certRemove will also remove local TUF data
func (c *certCommander) certRemove(cmd *cobra.Command, args []string) error {
	// If the user hasn't provided -g with a gun, or a cert ID, show usage
	// If the user provided -g and a cert ID, also show usage
	if (len(args) < 1 && c.certRemoveGUN == "") || (len(args) > 0 && c.certRemoveGUN != "") {
		cmd.Usage()
		return fmt.Errorf("Must specify the cert ID or the GUN of the certificates to remove")
	}
	config, err := c.configGetter()
	if err != nil {
		return err
	}

	trustDir := config.GetString("trust_dir")
	certPath := filepath.Join(trustDir, notary.TrustedCertsDir)
	certStore, err := trustmanager.NewX509FilteredFileStore(
		certPath,
		trustmanager.FilterCertsExpiredSha1,
	)
	if err != nil {
		return fmt.Errorf("Failed to create a new truststore with directory: %s", trustDir)
	}

	var certsToRemove []*x509.Certificate
	var certFoundByID *x509.Certificate
	var removeTrustData bool

	// If there is no GUN, we expect a cert ID
	if c.certRemoveGUN == "" {
		certID := args[0]
		// Attempt to find this certificate
		certFoundByID, err = certStore.GetCertificateByCertID(certID)
		if err != nil {
			// This is an invalid ID, the user might have forgotten a character
			if len(certID) != notary.Sha256HexSize {
				return fmt.Errorf("Unable to retrieve certificate with invalid certificate ID provided: %s", certID)
			}
			return fmt.Errorf("Unable to retrieve certificate with cert ID: %s", certID)
		}
		// the GUN is the CN from the certificate
		c.certRemoveGUN = certFoundByID.Subject.CommonName
		certsToRemove = []*x509.Certificate{certFoundByID}
	}

	toRemove, err := certStore.GetCertificatesByCN(c.certRemoveGUN)
	// We could not find any certificates matching the user's query, so propagate the error
	if err != nil {
		return fmt.Errorf("%v", err)
	}

	// If we specified a GUN or if the ID we specified is the only certificate with its CN, remove all GUN certs and trust data too
	if certFoundByID == nil || len(toRemove) == 1 {
		removeTrustData = true
		certsToRemove = toRemove
	}

	// List all the certificates about to be removed
	cmd.Printf("The following certificates will be removed:\n\n")
	for _, cert := range certsToRemove {
		// This error can't occur because we're getting certs off of an
		// x509 store that indexes by ID.
		certID, _ := trustmanager.FingerprintCert(cert)
		cmd.Printf("%s - %s\n", cert.Subject.CommonName, certID)
	}
	// If we were given a GUN or the last ID for a GUN, inform the user that we'll also delete all TUF data
	if removeTrustData {
		cmd.Printf("\nAll local trust data will be removed for %s\n", c.certRemoveGUN)
	}
	cmd.Println("\nAre you sure you want to remove these certificates? (yes/no)")

	// Ask for confirmation before removing certificates, unless -y is provided
	if !c.certRemoveYes {
		confirmed := askConfirm()
		if !confirmed {
			return fmt.Errorf("Aborting action.")
		}
	}

	if removeTrustData {
		// Remove all TUF data, so call RemoveTrustData on a NotaryRepository with the GUN
		// no online operations are performed so the transport argument is nil
		nRepo, err := notaryclient.NewNotaryRepository(
			trustDir, c.certRemoveGUN, getRemoteTrustServer(config), nil, c.retriever)
		if err != nil {
			return fmt.Errorf("Could not establish trust data for GUN %s", c.certRemoveGUN)
		}
		// DeleteTrustData will pick up all of the same certificates by GUN (CN) and remove them
		err = nRepo.DeleteTrustData()
		if err != nil {
			return fmt.Errorf("Failed to delete trust data for %s", c.certRemoveGUN)
		}
	} else {
		for _, cert := range certsToRemove {
			err = certStore.RemoveCert(cert)
			if err != nil {
				return fmt.Errorf("Failed to remove cert %s", cert)
			}
		}
	}
	return nil
}
Beispiel #17
0
func TestCertsToRemove(t *testing.T) {
	// Get a few certificates to test with
	cert1, err := trustmanager.LoadCertFromFile("../fixtures/secure.example.com.crt")
	assert.NoError(t, err)
	cert1KeyID, err := trustmanager.FingerprintCert(cert1)
	assert.NoError(t, err)

	// Get intermediate certificate
	cert2, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_secure.example.com.crt")
	assert.NoError(t, err)
	cert2KeyID, err := trustmanager.FingerprintCert(cert2)
	assert.NoError(t, err)

	// Get leaf certificate
	cert3, err := trustmanager.LoadCertFromFile("../fixtures/self-signed_docker.com-notary.crt")
	assert.NoError(t, err)
	cert3KeyID, err := trustmanager.FingerprintCert(cert3)
	assert.NoError(t, err)

	// Call CertsToRemove with only one old and one new
	oldCerts := []*x509.Certificate{cert1}
	newCerts := []*x509.Certificate{cert2}

	certificates := certsToRemove(oldCerts, newCerts)
	assert.Len(t, certificates, 1)
	_, ok := certificates[cert1KeyID]
	assert.True(t, ok)

	// Call CertsToRemove with two old and one new
	oldCerts = []*x509.Certificate{cert1, cert2}
	newCerts = []*x509.Certificate{cert3}

	certificates = certsToRemove(oldCerts, newCerts)
	assert.Len(t, certificates, 2)
	_, ok = certificates[cert1KeyID]
	assert.True(t, ok)
	_, ok = certificates[cert2KeyID]
	assert.True(t, ok)
	_, ok = certificates[cert3KeyID]
	assert.False(t, ok)

	// Call CertsToRemove with two new and one old
	oldCerts = []*x509.Certificate{cert3}
	newCerts = []*x509.Certificate{cert2, cert1}

	certificates = certsToRemove(oldCerts, newCerts)
	assert.Len(t, certificates, 1)
	_, ok = certificates[cert3KeyID]
	assert.True(t, ok)
	_, ok = certificates[cert1KeyID]
	assert.False(t, ok)
	_, ok = certificates[cert2KeyID]
	assert.False(t, ok)

	// Call CertsToRemove with three old certificates and no new
	oldCerts = []*x509.Certificate{cert1, cert2, cert3}
	newCerts = []*x509.Certificate{}

	certificates = certsToRemove(oldCerts, newCerts)
	assert.Len(t, certificates, 0)
	_, ok = certificates[cert1KeyID]
	assert.False(t, ok)
	_, ok = certificates[cert2KeyID]
	assert.False(t, ok)
	_, ok = certificates[cert3KeyID]
	assert.False(t, ok)

	// Call CertsToRemove with three new certificates and no old
	oldCerts = []*x509.Certificate{}
	newCerts = []*x509.Certificate{cert1, cert2, cert3}

	certificates = certsToRemove(oldCerts, newCerts)
	assert.Len(t, certificates, 0)
	_, ok = certificates[cert1KeyID]
	assert.False(t, ok)
	_, ok = certificates[cert2KeyID]
	assert.False(t, ok)
	_, ok = certificates[cert3KeyID]
	assert.False(t, ok)

}