// NewKeyStoreManager returns an initialized KeyStoreManager, or an error // if it fails to create the KeyFileStores or load certificates func NewKeyStoreManager(baseDir string) (*KeyStoreManager, error) { trustPath := filepath.Join(baseDir, trustDir) // Load all CAs that aren't expired and don't use SHA1 trustedCAStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } // Load all individual (non-CA) certificates that aren't expired and don't use SHA1 trustedCertificateStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return !cert.IsCA && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } return &KeyStoreManager{ trustedCAStore: trustedCAStore, trustedCertificateStore: trustedCertificateStore, }, nil }
func (c *certCommander) certList(cmd *cobra.Command, args []string) error { if len(args) > 0 { cmd.Usage() return fmt.Errorf("") } config, err := c.configGetter() if err != nil { return err } trustDir := config.GetString("trust_dir") certPath := filepath.Join(trustDir, notary.TrustedCertsDir) // Load all individual (non-CA) certificates that aren't expired and don't use SHA1 certStore, err := trustmanager.NewX509FilteredFileStore( certPath, trustmanager.FilterCertsExpiredSha1, ) if err != nil { return fmt.Errorf("Failed to create a new truststore with directory: %s", trustDir) } trustedCerts := certStore.GetCertificates() cmd.Println("") prettyPrintCerts(trustedCerts, cmd.Out()) cmd.Println("") return nil }
// Generates a X509Store in a temporary directory and returns the // store and certificates for two keys which have been added to the keystore. // Also returns the temporary directory so it can be cleaned up. func filestoreWithTwoCerts(t *testing.T, gun, keyAlg string) ( string, trustmanager.X509Store, *cryptoservice.CryptoService, []*x509.Certificate) { tempBaseDir, err := ioutil.TempDir("", "notary-test-") assert.NoError(t, err, "failed to create a temporary directory: %s", err) fileKeyStore, err := trustmanager.NewKeyFileStore(tempBaseDir, passphraseRetriever) assert.NoError(t, err) cryptoService := cryptoservice.NewCryptoService(gun, fileKeyStore) // Create a store trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir) certStore, err := trustmanager.NewX509FilteredFileStore( trustPath, trustmanager.FilterCertsExpiredSha1, ) assert.NoError(t, err) certificates := make([]*x509.Certificate, 2) for i := 0; i < 2; i++ { pubKey, err := cryptoService.Create("root", keyAlg) assert.NoError(t, err) key, _, err := fileKeyStore.GetKey(pubKey.ID()) assert.NoError(t, err) cert, err := cryptoservice.GenerateTestingCertificate(key.CryptoSigner(), gun) assert.NoError(t, err) certificates[i] = cert } return tempBaseDir, certStore, cryptoService, certificates }
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error // if it fails to create the KeyFileStores or load certificates func NewKeyStoreManager(baseDir string, passphraseRetriever passphrase.Retriever) (*KeyStoreManager, error) { nonRootKeysPath := filepath.Join(baseDir, privDir, nonRootKeysSubdir) nonRootKeyStore, err := trustmanager.NewKeyFileStore(nonRootKeysPath, passphraseRetriever) if err != nil { return nil, err } // Load the keystore that will hold all of our encrypted Root Private Keys rootKeysPath := filepath.Join(baseDir, privDir, rootKeysSubdir) rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysPath, passphraseRetriever) if err != nil { return nil, err } trustPath := filepath.Join(baseDir, trustDir) // Load all CAs that aren't expired and don't use SHA1 trustedCAStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } // Load all individual (non-CA) certificates that aren't expired and don't use SHA1 trustedCertificateStore, err := trustmanager.NewX509FilteredFileStore(trustPath, func(cert *x509.Certificate) bool { return !cert.IsCA && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return nil, err } return &KeyStoreManager{ rootKeyStore: rootKeyStore, nonRootKeyStore: nonRootKeyStore, trustedCAStore: trustedCAStore, trustedCertificateStore: trustedCertificateStore, }, nil }
func init() { logrus.SetLevel(logrus.ErrorLevel) logrus.SetOutput(os.Stderr) // Retrieve current user to get home directory usr, err := user.Current() if err != nil { fatalf("cannot get current user: %v", err) } // Get home directory for current user homeDir := usr.HomeDir if homeDir == "" { fatalf("cannot get current user home directory") } // Setup the configuration details viper.SetConfigName(configFileName) viper.AddConfigPath(path.Join(homeDir, path.Dir(configPath))) viper.SetConfigType("json") // Find and read the config file err = viper.ReadInConfig() if err != nil { // Ignore if the configuration file doesn't exist, we can use the defaults if !os.IsNotExist(err) { fatalf("fatal error config file: %v", err) } } // Set up the defaults for our config viper.SetDefault("trustDir", path.Join(homeDir, path.Dir(trustDir))) viper.SetDefault("privDir", path.Join(homeDir, path.Dir(privDir))) viper.SetDefault("tufDir", path.Join(homeDir, path.Dir(tufDir))) // Get the final value for the CA directory finalTrustDir := viper.GetString("trustDir") finalPrivDir := viper.GetString("privDir") // Load all CAs that aren't expired and don't use SHA1 // We could easily add "return cert.IsCA && cert.BasicConstraintsValid" in order // to have only valid CA certificates being loaded caStore, err = trustmanager.NewX509FilteredFileStore(finalTrustDir, func(cert *x509.Certificate) bool { return time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { fatalf("could not create X509FileStore: %v", err) } privKeyStore, err = trustmanager.NewPrivateFileStore(finalPrivDir, "key") if err != nil { fatalf("could not create FileStore: %v", err) } }
func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error { // Load all CAs that aren't expired and don't use SHA1 caStore, err := trustmanager.NewX509FilteredFileStore(trustDir, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return err } // Load all individual (non-CA) certificates that aren't expired and don't use SHA1 certificateStore, err := trustmanager.NewX509FilteredFileStore(trustDir, func(cert *x509.Certificate) bool { return !cert.IsCA && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { return err } // Load the keystore that will hold all of our encrypted Root Private Keys rootKeyStore, err := trustmanager.NewKeyFileStore(rootKeysDir) if err != nil { return err } r.caStore = caStore r.certificateStore = certificateStore r.rootKeyStore = rootKeyStore return nil }
// repositoryFromKeystores is a helper function for NewNotaryRepository that // takes some basic NotaryRepository parameters as well as keystores (in order // of usage preference), and returns a NotaryRepository. func repositoryFromKeystores(baseDir, gun, baseURL string, rt http.RoundTripper, keyStores []trustmanager.KeyStore) (*NotaryRepository, error) { certPath := filepath.Join(baseDir, notary.TrustedCertsDir) certStore, err := trustmanager.NewX509FilteredFileStore( certPath, trustmanager.FilterCertsExpiredSha1, ) if err != nil { return nil, err } cryptoService := cryptoservice.NewCryptoService(gun, keyStores...) nRepo := &NotaryRepository{ gun: gun, baseDir: baseDir, baseURL: baseURL, tufRepoPath: filepath.Join(baseDir, tufDir, filepath.FromSlash(gun)), CryptoService: cryptoService, roundTrip: rt, CertStore: certStore, } fileStore, err := store.NewFilesystemStore( nRepo.tufRepoPath, "metadata", "json", "", ) if err != nil { return nil, err } nRepo.fileStore = fileStore return nRepo, nil }
func TestValidateRoot(t *testing.T) { var testSignedRoot data.Signed var signedRootBytes bytes.Buffer // 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) // Create a X509Store trustPath := filepath.Join(tempBaseDir, notary.TrustedCertsDir) certStore, err := trustmanager.NewX509FilteredFileStore( trustPath, trustmanager.FilterCertsExpiredSha1, ) assert.NoError(t, err) // Execute our template templ, _ := template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validPEMEncodedRSARoot}) // Unmarshal our signedroot json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) // This call to ValidateRoot will succeed since we are using a valid PEM // encoded certificate, and have no other certificates for this CN err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary") assert.NoError(t, err) // This call to ValidateRoot will fail since we are passing in a dnsName that // doesn't match the CN of the certificate. err = ValidateRoot(certStore, &testSignedRoot, "diogomonica.com/notary") if assert.Error(t, err, "An error was expected") { assert.Equal(t, err, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) } // // This call to ValidateRoot will fail since we are passing an unparsable RootSigned // // Execute our template deleting the old buffer first signedRootBytes.Reset() templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: "------ ABSOLUTELY NOT A PEM -------"}) // Unmarshal our signedroot json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary") assert.Error(t, err, "illegal base64 data at input byte") // // This call to ValidateRoot will fail since we are passing an invalid PEM cert // // Execute our template deleting the old buffer first signedRootBytes.Reset() templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: "LS0tLS1CRUdJTiBDRVJU"}) // Unmarshal our signedroot json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary") if assert.Error(t, err, "An error was expected") { assert.Equal(t, err, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) } // // This call to ValidateRoot will fail since we are passing only CA certificate // This will fail due to the lack of a leaf certificate // // Execute our template deleting the old buffer first signedRootBytes.Reset() templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validCAPEMEncodeRSARoot}) // Unmarshal our signedroot json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) err = ValidateRoot(certStore, &testSignedRoot, "docker.com/notary") if assert.Error(t, err, "An error was expected") { assert.Equal(t, err, &ErrValidationFail{Reason: "unable to retrieve valid leaf certificates"}) } // // This call to ValidateRoot will suceed in getting to the TUF validation, since // we are using a valid PEM encoded certificate chain of intermediate + leaf cert // that are signed by a trusted root authority and the leaf cert has a correct CN. // It will, however, fail to validate, because it has an invalid TUF signature // // Execute our template deleting the old buffer first signedRootBytes.Reset() templ, _ = template.New("SignedRSARootTemplate").Parse(signedRSARootTemplate) templ.Execute(&signedRootBytes, SignedRSARootTemplate{RootPem: validIntermediateAndCertRSA}) // Unmarshal our signedroot json.Unmarshal(signedRootBytes.Bytes(), &testSignedRoot) err = ValidateRoot(certStore, &testSignedRoot, "secure.example.com") if assert.Error(t, err, "An error was expected") { assert.Equal(t, err, &ErrValidationFail{Reason: "failed to validate integrity of roots"}) } }
// 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 }
func init() { logrus.SetLevel(logrus.DebugLevel) logrus.SetOutput(os.Stderr) // Retrieve current user to get home directory usr, err := user.Current() if err != nil { fatalf("cannot get current user: %v", err) } // Get home directory for current user homeDir := usr.HomeDir if homeDir == "" { fatalf("cannot get current user home directory") } // Setup the configuration details viper.SetConfigName(configFileName) viper.AddConfigPath(path.Join(homeDir, path.Dir(configPath))) viper.SetConfigType("json") // Find and read the config file err = viper.ReadInConfig() if err != nil { // Ignore if the configuration file doesn't exist, we can use the defaults if !os.IsNotExist(err) { fatalf("fatal error config file: %v", err) } } // Set up the defaults for our config viper.SetDefault("baseTrustDir", path.Join(homeDir, path.Dir(configPath))) // Get the final value for the CA directory finalTrustDir := path.Join(viper.GetString("baseTrustDir"), trustDir) finalPrivDir := path.Join(viper.GetString("baseTrustDir"), privDir) // Load all CAs that aren't expired and don't use SHA1 caStore, err = trustmanager.NewX509FilteredFileStore(finalTrustDir, func(cert *x509.Certificate) bool { return cert.IsCA && cert.BasicConstraintsValid && cert.SubjectKeyId != nil && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { fatalf("could not create CA X509FileStore: %v", err) } // Load all individual (nonCA) certificates that aren't expired and don't use SHA1 certificateStore, err = trustmanager.NewX509FilteredFileStore(finalTrustDir, func(cert *x509.Certificate) bool { return !cert.IsCA && time.Now().Before(cert.NotAfter) && cert.SignatureAlgorithm != x509.SHA1WithRSA && cert.SignatureAlgorithm != x509.DSAWithSHA1 && cert.SignatureAlgorithm != x509.ECDSAWithSHA1 }) if err != nil { fatalf("could not create Certificate X509FileStore: %v", err) } privKeyStore, err = trustmanager.NewKeyFileStore(finalPrivDir) if err != nil { fatalf("could not create KeyFileStore: %v", err) } }