mirror of https://github.com/docker/docs.git
Merge pull request #65 from docker/adding-the-not-yummy-kind-of-tofu
Adding the not yummy kind of tofu
This commit is contained in:
commit
a90a8b27b3
File diff suppressed because one or more lines are too long
|
@ -64,7 +64,7 @@ func keysRemove(cmd *cobra.Command, args []string) {
|
|||
gunOrID := args[0]
|
||||
|
||||
// Try to retrieve the ID from the CA store.
|
||||
cert, err := caStore.GetCertificateByKeyID(gunOrID)
|
||||
cert, err := caStore.GetCertificateByCertID(gunOrID)
|
||||
if err == nil {
|
||||
fmt.Printf("Removing: ")
|
||||
printCert(cert)
|
||||
|
@ -78,7 +78,7 @@ func keysRemove(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
|
||||
// Try to retrieve the ID from the Certificate store.
|
||||
cert, err = certificateStore.GetCertificateByKeyID(gunOrID)
|
||||
cert, err = certificateStore.GetCertificateByCertID(gunOrID)
|
||||
if err == nil {
|
||||
fmt.Printf("Removing: ")
|
||||
printCert(cert)
|
||||
|
@ -216,12 +216,12 @@ func keysGenerate(cmd *cobra.Command, args []string) {
|
|||
|
||||
func printCert(cert *x509.Certificate) {
|
||||
timeDifference := cert.NotAfter.Sub(time.Now())
|
||||
keyID, err := trustmanager.FingerprintCert(cert)
|
||||
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, keyID, math.Floor(timeDifference.Hours()/24))
|
||||
fmt.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, certID, math.Floor(timeDifference.Hours()/24))
|
||||
}
|
||||
|
||||
func printKey(keyPath string) {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBpDCCAUqgAwIBAgIRAIquZ7lRJj1Um030Kd7GFXgwCgYIKoZIzj0EAwIwODEa
|
||||
MBgGA1UEChMRZG9ja2VyLmNvbS9ub3RhcnkxGjAYBgNVBAMTEWRvY2tlci5jb20v
|
||||
bm90YXJ5MB4XDTE1MDcxNzAwMzE1NFoXDTE3MDcxNjAwMzE1NFowODEaMBgGA1UE
|
||||
ChMRZG9ja2VyLmNvbS9ub3RhcnkxGjAYBgNVBAMTEWRvY2tlci5jb20vbm90YXJ5
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjnnozttLzYgIN5fL8ZwYbsMig0pj
|
||||
HSNupVTPjDIrLUYUnoQfG6IQ0E2BMixEGnI/A9WreeXP2oz06LZ4SROMQqM1MDMw
|
||||
DgYDVR0PAQH/BAQDAgCgMBMGA1UdJQQMMAoGCCsGAQUFBwMDMAwGA1UdEwEB/wQC
|
||||
MAAwCgYIKoZIzj0EAwIDSAAwRQIgT9cxottjza9BBQcMsoB/Uf2JYXWgSkp9QMXT
|
||||
8mG4mMICIQDMYWFdgn5u8nDeThJ+bG8Lu5nIGb/NWEOFtU0xQv913Q==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBqDCCAU6gAwIBAgIRAM1vKVhmZuWcrogc3ASBaZUwCgYIKoZIzj0EAwIwOjEb
|
||||
MBkGA1UEChMSc2VjdXJlLmV4YW1wbGUuY29tMRswGQYDVQQDExJzZWN1cmUuZXhh
|
||||
bXBsZS5jb20wHhcNMTUwNzE3MDU1NTIzWhcNMTcwNzE2MDU1NTIzWjA6MRswGQYD
|
||||
VQQKExJzZWN1cmUuZXhhbXBsZS5jb20xGzAZBgNVBAMTEnNlY3VyZS5leGFtcGxl
|
||||
LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI556M7bS82ICDeXy/GcGG7D
|
||||
IoNKYx0jbqVUz4wyKy1GFJ6EHxuiENBNgTIsRBpyPwPVq3nlz9qM9Oi2eEkTjEKj
|
||||
NTAzMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEFBQcDAzAMBgNVHRMB
|
||||
Af8EAjAAMAoGCCqGSM49BAMCA0gAMEUCIER2XCkQ8dUWBZEUeT5kABg7neiHPtSL
|
||||
VVE6bJxu2sxlAiEAkRG6u1ieXKGl38gUkCn75Yvo9nOSLdh0gtxUUcOXvUc=
|
||||
-----END CERTIFICATE-----
|
|
@ -3,8 +3,6 @@ package keystoremanager
|
|||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
@ -36,6 +34,15 @@ const (
|
|||
rsaRootKeySize = 4096 // Used for new root keys
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrValidationFail is returned when there is no trusted certificate in any of the
|
||||
// root keys available in the roots.json
|
||||
ErrValidationFail = errors.New("could not validate the path to a trusted root")
|
||||
// ErrRootRotationFail is returned when we fail to do a full root key rotation
|
||||
// by either failing to add the new root certificate, or delete the old ones
|
||||
ErrRootRotationFail = errors.New("could not rotate trust to a new trusted root")
|
||||
)
|
||||
|
||||
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error
|
||||
// if it fails to create the KeyFileStores or load certificates
|
||||
func NewKeyStoreManager(baseDir string) (*KeyStoreManager, error) {
|
||||
|
@ -166,79 +173,166 @@ 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.
|
||||
Currently this method operates on a Trust On First Use (TOFU) model: if we
|
||||
have never seen a certificate for a particular CN, we trust it. If later we see
|
||||
a different certificate for that certificate, we return an ErrValidationFailed error.
|
||||
|
||||
Example TUF Content for root role:
|
||||
"roles" : {
|
||||
"root" : {
|
||||
"threshold" : 1,
|
||||
"keyids" : [
|
||||
"e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38"
|
||||
]
|
||||
},
|
||||
...
|
||||
}
|
||||
Note that since we only allow trust data to be downloaded over an HTTPS channel
|
||||
we are using the current web-of-trust 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.
|
||||
|
||||
Example TUF Content for root key:
|
||||
"e6da5c303d572712a086e669ecd4df7b785adfc844e0c9a7b1f21a7dfc477a38" : {
|
||||
"keytype" : "RSA",
|
||||
"keyval" : {
|
||||
"private" : "",
|
||||
"public" : "Base64-encoded, PEM encoded x509 Certificate"
|
||||
}
|
||||
}
|
||||
ValidateRoot also supports root key rotation, trusting a new certificate that has
|
||||
been included in the roots.json, and removing trust in the old one.
|
||||
*/
|
||||
func (km *KeyStoreManager) ValidateRoot(root *data.Signed, dnsName string) error {
|
||||
rootSigned := &data.Root{}
|
||||
err := json.Unmarshal(root.Signed, rootSigned)
|
||||
logrus.Debugf("entered ValidateRoot with dns: %s", dnsName)
|
||||
rootSigned, err := data.RootFromSigned(root)
|
||||
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)
|
||||
// validKeys will store all the keys that were considered valid either by
|
||||
// direct certificate match, or CA chain path
|
||||
validKeys := make(map[string]data.PublicKey)
|
||||
|
||||
// allCerts will keep a list of all leafCerts that were found, and is used
|
||||
// to aid on root certificate rotation
|
||||
allCerts := make(map[string]*x509.Certificate)
|
||||
|
||||
// Before we loop through all root keys available, make sure any exist
|
||||
rootRoles, ok := rootSigned.Signed.Roles["root"]
|
||||
if !ok {
|
||||
return errors.New("no root roles found in tuf metadata")
|
||||
}
|
||||
|
||||
logrus.Debugf("found the following root keys in roots.json: %v", rootRoles.KeyIDs)
|
||||
// Iterate over every keyID for the root role inside of roots.json
|
||||
for _, keyID := range rootRoles.KeyIDs {
|
||||
// Decode all the x509 certificates that were bundled with this
|
||||
// Specific root key
|
||||
decodedCerts, err := trustmanager.LoadCertBundleFromPEM([]byte(rootSigned.Signed.Keys[keyID].Public()))
|
||||
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]
|
||||
|
||||
// Get all non-CA certificates in the decoded certificates
|
||||
leafCerts := trustmanager.GetLeafCerts(decodedCerts)
|
||||
|
||||
// If we got no leaf certificates or we got more than one, fail
|
||||
if len(leafCerts) != 1 {
|
||||
logrus.Debugf("wasn't able to find a leaf certificate in the chain of keyID: %s", keyID)
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the ID of the leaf certificate
|
||||
leafCert := leafCerts[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]
|
||||
// Validate that this leaf certificate has a CN that matches the exact gun
|
||||
if leafCert.Subject.CommonName != dnsName {
|
||||
logrus.Debugf("error leaf certificate CN: %s doesn't match the given dns name: %s", leafCert.Subject.CommonName, dnsName)
|
||||
continue
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Add all the valid leafs to the certificates map so we can refer to them later
|
||||
allCerts[leafID] = leafCert
|
||||
|
||||
// Retrieve all the trusted certificates that match this dns Name
|
||||
certsForCN, err := km.trustedCertificateStore.GetCertificatesByCN(dnsName)
|
||||
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 not trust this key
|
||||
if _, ok := err.(*trustmanager.ErrNoCertificatesFound); !ok {
|
||||
logrus.Debugf("error retrieving certificates for: %s, %v", dnsName, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no certificates with this CN, lets TOFUS!
|
||||
// Note that this logic should only exist in docker 1.8
|
||||
if len(certsForCN) == 0 {
|
||||
km.trustedCertificateStore.AddCert(leafCert)
|
||||
certsForCN = append(certsForCN, leafCert)
|
||||
logrus.Debugf("using TOFUS on %s with keyID: %s", dnsName, leafID)
|
||||
}
|
||||
|
||||
// Iterate over all known certificates for this CN and see if any are trusted
|
||||
for _, cert := range certsForCN {
|
||||
// Check to see if there is an exact match of this certificate.
|
||||
certID, err := trustmanager.FingerprintCert(cert)
|
||||
if err == nil && certID == leafID {
|
||||
validKeys[keyID] = rootSigned.Signed.Keys[keyID]
|
||||
logrus.Debugf("found an exact match for %s with keyID: %s", dnsName, keyID)
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if this leafCertificate has a chain to one of the Root
|
||||
// CAs of our CA Store.
|
||||
err = trustmanager.Verify(km.trustedCAStore, dnsName, decodedCerts)
|
||||
if err == nil {
|
||||
certs[keyID] = rootSigned.Keys[keyID]
|
||||
validKeys[keyID] = rootSigned.Signed.Keys[keyID]
|
||||
logrus.Debugf("found a CA path for %s with keyID: %s", dnsName, keyID)
|
||||
}
|
||||
}
|
||||
|
||||
if len(certs) < 1 {
|
||||
return errors.New("could not validate the path to a trusted root")
|
||||
if len(validKeys) < 1 {
|
||||
logrus.Debugf("wasn't able to trust any of the root keys")
|
||||
return ErrValidationFail
|
||||
}
|
||||
|
||||
_, err = signed.VerifyRoot(root, 0, certs, 1)
|
||||
// TODO(david): change hardcoded minversion on TUF.
|
||||
newRootKey, err := signed.VerifyRoot(root, 0, validKeys, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
// VerifyRoot returns a non-nil value if there is a root key rotation happening.
|
||||
// If this happens, we should replace the old root of trust with the new one
|
||||
if newRootKey != nil {
|
||||
logrus.Debugf("got a new root key to rotate to: %s", newRootKey.ID())
|
||||
|
||||
// Retrieve the certificate associated with the new root key and trust it
|
||||
newRootKeyCert, ok := allCerts[newRootKey.ID()]
|
||||
// Paranoid check for the certificate still being in the map
|
||||
if !ok {
|
||||
logrus.Debugf("error while retrieving new root certificate with keyID: %s, %v", newRootKey.ID(), err)
|
||||
return ErrRootRotationFail
|
||||
}
|
||||
|
||||
// Add the new root certificate to our certificate store
|
||||
err := km.trustedCertificateStore.AddCert(newRootKeyCert)
|
||||
if err != nil {
|
||||
// Ignore the error if the certificate already exists
|
||||
if _, ok := err.(*trustmanager.ErrCertExists); !ok {
|
||||
logrus.Debugf("error while adding new root certificate with keyID: %s, %v", newRootKey.ID(), err)
|
||||
return ErrRootRotationFail
|
||||
}
|
||||
logrus.Debugf("root certificate already exists in keystore: %s", newRootKey.ID())
|
||||
}
|
||||
|
||||
// Remove the new root certificate from the certificate mapping so we
|
||||
// can remove trust from all of the remaining ones
|
||||
delete(allCerts, newRootKey.ID())
|
||||
|
||||
// Iterate over all old valid certificates and remove them, essentially
|
||||
// finishing the rotation of the currently trusted root certificate
|
||||
for _, cert := range allCerts {
|
||||
err := km.trustedCertificateStore.RemoveCert(cert)
|
||||
if err != nil {
|
||||
logrus.Debugf("error while removing old root certificate: %v", err)
|
||||
return ErrRootRotationFail
|
||||
}
|
||||
logrus.Debugf("removed trust from old root certificate")
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Debugf("Root validation succeeded")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -162,3 +162,8 @@ func listKeys(s LimitedFileStore) []string {
|
|||
}
|
||||
return keyIDList
|
||||
}
|
||||
|
||||
// RemoveKey removes the key from the keyfilestore
|
||||
func (s *KeyFileStore) RemoveKey(name string) error {
|
||||
return s.Remove(name)
|
||||
}
|
||||
|
|
|
@ -251,3 +251,53 @@ func TestGetDecryptedWithInvalidPassphrase(t *testing.T) {
|
|||
t.Fatalf("expected error while decrypting the content due to invalid passphrase")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveKey(t *testing.T) {
|
||||
testName := "docker.com/notary/root"
|
||||
testExt := "key"
|
||||
|
||||
// Temporary directory where test files will be created
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create a temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
// Since we're generating this manually we need to add the extension '.'
|
||||
expectedFilePath := filepath.Join(tempBaseDir, testName+"."+testExt)
|
||||
|
||||
// Create our store
|
||||
store, err := NewKeyFileStore(tempBaseDir)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new key filestore: %v", err)
|
||||
}
|
||||
|
||||
privKey, err := GenerateRSAKey(rand.Reader, 512)
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Call the AddKey function
|
||||
err = store.AddKey(testName, privKey)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add file to store: %v", err)
|
||||
}
|
||||
|
||||
// Check to see if file exists
|
||||
_, err = ioutil.ReadFile(expectedFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("expected file not found: %v", err)
|
||||
}
|
||||
|
||||
// Call remove key
|
||||
err = store.RemoveKey(testName)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to remove key: %v", err)
|
||||
}
|
||||
|
||||
// Check to see if file still exists
|
||||
_, err = ioutil.ReadFile(expectedFilePath)
|
||||
if err == nil {
|
||||
t.Fatalf("file should not exist %s", expectedFilePath)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,14 +50,14 @@ func newX509FileStore(directory string, validate func(*x509.Certificate) bool) (
|
|||
}
|
||||
|
||||
// AddCert creates a filename for a given cert and adds a certificate with that name
|
||||
func (s X509FileStore) AddCert(cert *x509.Certificate) error {
|
||||
func (s *X509FileStore) AddCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("adding nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
// Check if this certificate meets our validation criteria
|
||||
if !s.validate.Validate(cert) {
|
||||
return errors.New("certificate validation failed")
|
||||
return &ErrCertValidation{}
|
||||
}
|
||||
// Attempt to write the certificate to the file
|
||||
if err := s.addNamedCert(cert); err != nil {
|
||||
|
@ -69,16 +69,16 @@ func (s X509FileStore) AddCert(cert *x509.Certificate) error {
|
|||
|
||||
// addNamedCert allows adding a certificate while controling the filename it gets
|
||||
// stored under. If the file does not exist on disk, saves it.
|
||||
func (s X509FileStore) addNamedCert(cert *x509.Certificate) error {
|
||||
fileName, keyID, err := fileName(cert)
|
||||
func (s *X509FileStore) addNamedCert(cert *x509.Certificate) error {
|
||||
fileName, certID, err := fileName(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debug("Adding cert with keyID: ", keyID)
|
||||
// Validate if we already loaded this certificate before
|
||||
if _, ok := s.fingerprintMap[keyID]; ok {
|
||||
return errors.New("certificate already in the store")
|
||||
logrus.Debug("Adding cert with certID: ", certID)
|
||||
// Validate if we already added this certificate before
|
||||
if _, ok := s.fingerprintMap[certID]; ok {
|
||||
return &ErrCertExists{}
|
||||
}
|
||||
|
||||
// Convert certificate to PEM
|
||||
|
@ -98,28 +98,28 @@ func (s X509FileStore) addNamedCert(cert *x509.Certificate) error {
|
|||
}
|
||||
|
||||
// We wrote the certificate succcessfully, add it to our in-memory storage
|
||||
s.fingerprintMap[keyID] = cert
|
||||
s.fileMap[keyID] = fileName
|
||||
s.fingerprintMap[certID] = cert
|
||||
s.fileMap[certID] = fileName
|
||||
|
||||
name := string(cert.RawSubject)
|
||||
s.nameMap[name] = append(s.nameMap[name], keyID)
|
||||
name := string(cert.Subject.CommonName)
|
||||
s.nameMap[name] = append(s.nameMap[name], certID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCert removes a certificate from a X509FileStore.
|
||||
func (s X509FileStore) RemoveCert(cert *x509.Certificate) error {
|
||||
func (s *X509FileStore) RemoveCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("removing nil Certificate from X509Store")
|
||||
}
|
||||
|
||||
keyID, err := fingerprintCert(cert)
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(s.fingerprintMap, keyID)
|
||||
filename := s.fileMap[keyID]
|
||||
delete(s.fileMap, keyID)
|
||||
delete(s.fingerprintMap, certID)
|
||||
filename := s.fileMap[certID]
|
||||
delete(s.fileMap, certID)
|
||||
|
||||
name := string(cert.RawSubject)
|
||||
|
||||
|
@ -127,7 +127,7 @@ func (s X509FileStore) RemoveCert(cert *x509.Certificate) error {
|
|||
fpList := s.nameMap[name]
|
||||
newfpList := fpList[:0]
|
||||
for _, x := range fpList {
|
||||
if x != keyID {
|
||||
if x != certID {
|
||||
newfpList = append(newfpList, x)
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +141,20 @@ func (s X509FileStore) RemoveCert(cert *x509.Certificate) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll removes all the certificates from the store
|
||||
func (s *X509FileStore) RemoveAll() error {
|
||||
for _, filename := range s.fileMap {
|
||||
if err := s.fileStore.Remove(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.fileMap = make(map[CertID]string)
|
||||
s.fingerprintMap = make(map[CertID]*x509.Certificate)
|
||||
s.nameMap = make(map[string][]CertID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertFromPEM adds the first certificate that it finds in the byte[], returning
|
||||
// an error if no Certificates are found
|
||||
func (s X509FileStore) AddCertFromPEM(pemBytes []byte) error {
|
||||
|
@ -152,7 +166,7 @@ func (s X509FileStore) AddCertFromPEM(pemBytes []byte) error {
|
|||
}
|
||||
|
||||
// AddCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
func (s X509FileStore) AddCertFromFile(filename string) error {
|
||||
func (s *X509FileStore) AddCertFromFile(filename string) error {
|
||||
cert, err := LoadCertFromFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -162,7 +176,7 @@ func (s X509FileStore) AddCertFromFile(filename string) error {
|
|||
}
|
||||
|
||||
// GetCertificates returns an array with all of the current X509 Certificates.
|
||||
func (s X509FileStore) GetCertificates() []*x509.Certificate {
|
||||
func (s *X509FileStore) GetCertificates() []*x509.Certificate {
|
||||
certs := make([]*x509.Certificate, len(s.fingerprintMap))
|
||||
i := 0
|
||||
for _, v := range s.fingerprintMap {
|
||||
|
@ -174,7 +188,7 @@ func (s X509FileStore) GetCertificates() []*x509.Certificate {
|
|||
|
||||
// GetCertificatePool returns an x509 CertPool loaded with all the certificates
|
||||
// in the store.
|
||||
func (s X509FileStore) GetCertificatePool() *x509.CertPool {
|
||||
func (s *X509FileStore) GetCertificatePool() *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, v := range s.fingerprintMap {
|
||||
|
@ -183,25 +197,52 @@ func (s X509FileStore) GetCertificatePool() *x509.CertPool {
|
|||
return pool
|
||||
}
|
||||
|
||||
// GetCertificateByKeyID returns the certificate that matches a certain keyID or error
|
||||
func (s X509FileStore) GetCertificateByKeyID(keyID string) (*x509.Certificate, error) {
|
||||
// GetCertificateByCertID returns the certificate that matches a certain certID
|
||||
func (s *X509FileStore) GetCertificateByCertID(certID string) (*x509.Certificate, error) {
|
||||
return s.getCertificateByCertID(CertID(certID))
|
||||
}
|
||||
|
||||
// getCertificateByCertID returns the certificate that matches a certain certID
|
||||
func (s *X509FileStore) getCertificateByCertID(certID CertID) (*x509.Certificate, error) {
|
||||
// If it does not look like a hex encoded sha256 hash, error
|
||||
if len(keyID) != 64 {
|
||||
if len(certID) != 64 {
|
||||
return nil, errors.New("invalid Subject Key Identifier")
|
||||
}
|
||||
|
||||
// Check to see if this subject key identifier exists
|
||||
if cert, ok := s.fingerprintMap[CertID(keyID)]; ok {
|
||||
if cert, ok := s.fingerprintMap[CertID(certID)]; ok {
|
||||
return cert, nil
|
||||
|
||||
}
|
||||
return nil, errors.New("certificate not found in Key Store")
|
||||
return nil, &ErrNoCertificatesFound{query: string(certID)}
|
||||
}
|
||||
|
||||
// GetCertificatesByCN returns all the certificates that match a specific
|
||||
// CommonName
|
||||
func (s *X509FileStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) {
|
||||
var certs []*x509.Certificate
|
||||
if ids, ok := s.nameMap[cn]; ok {
|
||||
for _, v := range ids {
|
||||
cert, err := s.getCertificateByCertID(v)
|
||||
if err != nil {
|
||||
// This error should never happen. This would mean that we have
|
||||
// an inconsistent X509FileStore
|
||||
return nil, err
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
}
|
||||
if len(certs) == 0 {
|
||||
return nil, &ErrNoCertificatesFound{query: cn}
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// GetVerifyOptions returns VerifyOptions with the certificates within the KeyStore
|
||||
// as part of the roots list. This never allows the use of system roots, returning
|
||||
// an error if there are no root CAs.
|
||||
func (s X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) {
|
||||
func (s *X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) {
|
||||
// If we have no Certificates loaded return error (we don't want to rever to using
|
||||
// system CAs).
|
||||
if len(s.fingerprintMap) == 0 {
|
||||
|
@ -217,10 +258,10 @@ func (s X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, err
|
|||
}
|
||||
|
||||
func fileName(cert *x509.Certificate) (string, CertID, error) {
|
||||
keyID, err := fingerprintCert(cert)
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return path.Join(cert.Subject.CommonName, string(keyID)), keyID, nil
|
||||
return path.Join(cert.Subject.CommonName, string(certID)), certID, nil
|
||||
}
|
||||
|
|
|
@ -135,6 +135,52 @@ func TestRemoveCertX509FileStore(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRemoveAllX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Add three certificates to store
|
||||
store, _ := NewX509FileStore(tempDir)
|
||||
certFiles := [3]string{"../fixtures/root-ca.crt",
|
||||
"../fixtures/intermediate-ca.crt",
|
||||
"../fixtures/secure.example.com.crt"}
|
||||
for _, file := range certFiles {
|
||||
b, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Number of certificates should be 3 since we added the cert
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 3 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// Remove the cert from the store
|
||||
err = store.RemoveAll()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove all certificates: %v", err)
|
||||
}
|
||||
// Number of certificates should be 0 since we added and removed the cert
|
||||
numCerts = len(store.GetCertificates())
|
||||
if numCerts != 0 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
func TestInexistentGetCertificateByKeyIDX509FileStore(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
if err != nil {
|
||||
|
@ -146,7 +192,7 @@ func TestInexistentGetCertificateByKeyIDX509FileStore(t *testing.T) {
|
|||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
_, err = store.GetCertificateByKeyID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a")
|
||||
_, err = store.GetCertificateByCertID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a")
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned for inexistent certificate")
|
||||
}
|
||||
|
@ -181,7 +227,7 @@ func TestGetCertificateByKeyIDX509FileStore(t *testing.T) {
|
|||
}
|
||||
|
||||
// Tries to retrieve cert by Subject Key IDs
|
||||
_, err = store.GetCertificateByKeyID(keyID)
|
||||
_, err = store.GetCertificateByCertID(keyID)
|
||||
if err != nil {
|
||||
t.Fatalf("expected certificate in store: %s", keyID)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package trustmanager
|
|||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// X509MemStore implements X509Store as an in-memory object with no persistence
|
||||
|
@ -23,7 +25,7 @@ func NewX509MemStore() *X509MemStore {
|
|||
}
|
||||
}
|
||||
|
||||
// NewX509FilteredMemStore returns a new X509FileStore that validates certificates
|
||||
// NewX509FilteredMemStore returns a new X509Memstore that validates certificates
|
||||
// that are added.
|
||||
func NewX509FilteredMemStore(validate func(*x509.Certificate) bool) *X509MemStore {
|
||||
s := &X509MemStore{
|
||||
|
@ -37,45 +39,48 @@ func NewX509FilteredMemStore(validate func(*x509.Certificate) bool) *X509MemStor
|
|||
}
|
||||
|
||||
// AddCert adds a certificate to the store
|
||||
func (s X509MemStore) AddCert(cert *x509.Certificate) error {
|
||||
func (s *X509MemStore) AddCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("adding nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
if !s.validate.Validate(cert) {
|
||||
return errors.New("certificate failed validation")
|
||||
return &ErrCertValidation{}
|
||||
}
|
||||
|
||||
keyID, err := fingerprintCert(cert)
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.fingerprintMap[keyID] = cert
|
||||
logrus.Debug("Adding cert with certID: ", certID)
|
||||
|
||||
// In this store we overwrite the certificate if it already exists
|
||||
s.fingerprintMap[certID] = cert
|
||||
name := string(cert.RawSubject)
|
||||
s.nameMap[name] = append(s.nameMap[name], keyID)
|
||||
s.nameMap[name] = append(s.nameMap[name], certID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCert removes a certificate from a X509MemStore.
|
||||
func (s X509MemStore) RemoveCert(cert *x509.Certificate) error {
|
||||
func (s *X509MemStore) RemoveCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("removing nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
keyID, err := fingerprintCert(cert)
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(s.fingerprintMap, keyID)
|
||||
delete(s.fingerprintMap, certID)
|
||||
name := string(cert.RawSubject)
|
||||
|
||||
// Filter the fingerprint out of this name entry
|
||||
fpList := s.nameMap[name]
|
||||
newfpList := fpList[:0]
|
||||
for _, x := range fpList {
|
||||
if x != keyID {
|
||||
if x != certID {
|
||||
newfpList = append(newfpList, x)
|
||||
}
|
||||
}
|
||||
|
@ -84,8 +89,20 @@ func (s X509MemStore) RemoveCert(cert *x509.Certificate) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll removes all the certificates from the store
|
||||
func (s *X509MemStore) RemoveAll() error {
|
||||
|
||||
for _, cert := range s.fingerprintMap {
|
||||
if err := s.RemoveCert(cert); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertFromPEM adds a certificate to the store from a PEM blob
|
||||
func (s X509MemStore) AddCertFromPEM(pemBytes []byte) error {
|
||||
func (s *X509MemStore) AddCertFromPEM(pemBytes []byte) error {
|
||||
cert, err := LoadCertFromPEM(pemBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -94,7 +111,7 @@ func (s X509MemStore) AddCertFromPEM(pemBytes []byte) error {
|
|||
}
|
||||
|
||||
// AddCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
func (s X509MemStore) AddCertFromFile(originFilname string) error {
|
||||
func (s *X509MemStore) AddCertFromFile(originFilname string) error {
|
||||
cert, err := LoadCertFromFile(originFilname)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -104,7 +121,7 @@ func (s X509MemStore) AddCertFromFile(originFilname string) error {
|
|||
}
|
||||
|
||||
// GetCertificates returns an array with all of the current X509 Certificates.
|
||||
func (s X509MemStore) GetCertificates() []*x509.Certificate {
|
||||
func (s *X509MemStore) GetCertificates() []*x509.Certificate {
|
||||
certs := make([]*x509.Certificate, len(s.fingerprintMap))
|
||||
i := 0
|
||||
for _, v := range s.fingerprintMap {
|
||||
|
@ -116,7 +133,7 @@ func (s X509MemStore) GetCertificates() []*x509.Certificate {
|
|||
|
||||
// GetCertificatePool returns an x509 CertPool loaded with all the certificates
|
||||
// in the store.
|
||||
func (s X509MemStore) GetCertificatePool() *x509.CertPool {
|
||||
func (s *X509MemStore) GetCertificatePool() *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, v := range s.fingerprintMap {
|
||||
|
@ -125,25 +142,52 @@ func (s X509MemStore) GetCertificatePool() *x509.CertPool {
|
|||
return pool
|
||||
}
|
||||
|
||||
// GetCertificateByKeyID returns the certificate that matches a certain keyID or error
|
||||
func (s X509MemStore) GetCertificateByKeyID(keyID string) (*x509.Certificate, error) {
|
||||
// GetCertificateByCertID returns the certificate that matches a certain certID
|
||||
func (s *X509MemStore) GetCertificateByCertID(certID string) (*x509.Certificate, error) {
|
||||
return s.getCertificateByCertID(CertID(certID))
|
||||
}
|
||||
|
||||
// getCertificateByCertID returns the certificate that matches a certain certID or error
|
||||
func (s *X509MemStore) getCertificateByCertID(certID CertID) (*x509.Certificate, error) {
|
||||
// If it does not look like a hex encoded sha256 hash, error
|
||||
if len(keyID) != 64 {
|
||||
if len(certID) != 64 {
|
||||
return nil, errors.New("invalid Subject Key Identifier")
|
||||
}
|
||||
|
||||
// Check to see if this subject key identifier exists
|
||||
if cert, ok := s.fingerprintMap[CertID(keyID)]; ok {
|
||||
if cert, ok := s.fingerprintMap[CertID(certID)]; ok {
|
||||
return cert, nil
|
||||
|
||||
}
|
||||
return nil, errors.New("certificate not found in Key Store")
|
||||
return nil, &ErrNoCertificatesFound{query: string(certID)}
|
||||
}
|
||||
|
||||
// GetCertificatesByCN returns all the certificates that match a specific
|
||||
// CommonName
|
||||
func (s *X509MemStore) GetCertificatesByCN(cn string) ([]*x509.Certificate, error) {
|
||||
var certs []*x509.Certificate
|
||||
if ids, ok := s.nameMap[cn]; ok {
|
||||
for _, v := range ids {
|
||||
cert, err := s.getCertificateByCertID(v)
|
||||
if err != nil {
|
||||
// This error should never happen. This would mean that we have
|
||||
// an inconsistent X509MemStore
|
||||
return nil, err
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
}
|
||||
if len(certs) == 0 {
|
||||
return nil, &ErrNoCertificatesFound{query: cn}
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// GetVerifyOptions returns VerifyOptions with the certificates within the KeyStore
|
||||
// as part of the roots list. This never allows the use of system roots, returning
|
||||
// an error if there are no root CAs.
|
||||
func (s X509MemStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) {
|
||||
func (s *X509MemStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, error) {
|
||||
// If we have no Certificates loaded return error (we don't want to rever to using
|
||||
// system CAs).
|
||||
if len(s.fingerprintMap) == 0 {
|
||||
|
|
|
@ -106,14 +106,55 @@ func TestRemoveCert(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInexistentGetCertificateByKeyID(t *testing.T) {
|
||||
func TestRemoveAllX509MemStore(t *testing.T) {
|
||||
// Add three certificates to store
|
||||
store := NewX509MemStore()
|
||||
certFiles := [3]string{"../fixtures/root-ca.crt",
|
||||
"../fixtures/intermediate-ca.crt",
|
||||
"../fixtures/secure.example.com.crt"}
|
||||
for _, file := range certFiles {
|
||||
b, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load fixture: %v", err)
|
||||
}
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(b)
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't parse certificate: %v", err)
|
||||
}
|
||||
err = store.AddCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Number of certificates should be 3 since we added the cert
|
||||
numCerts := len(store.GetCertificates())
|
||||
if numCerts != 3 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
|
||||
// Remove the cert from the store
|
||||
err := store.RemoveAll()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove all certificates: %v", err)
|
||||
}
|
||||
// Number of certificates should be 0 since we added and removed the cert
|
||||
numCerts = len(store.GetCertificates())
|
||||
if numCerts != 0 {
|
||||
t.Fatalf("unexpected number of certificates in store: %d", numCerts)
|
||||
}
|
||||
}
|
||||
func TestInexistentGetCertificateByCertID(t *testing.T) {
|
||||
store := NewX509MemStore()
|
||||
err := store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load certificate from file: %v", err)
|
||||
}
|
||||
|
||||
_, err = store.GetCertificateByKeyID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a")
|
||||
_, err = store.GetCertificateByCertID("4d06afd30b8bed131d2a84c97d00b37f422021598bfae34285ce98e77b708b5a")
|
||||
if err == nil {
|
||||
t.Fatalf("no error returned for inexistent certificate")
|
||||
}
|
||||
|
@ -138,15 +179,15 @@ func TestGetCertificateByKeyID(t *testing.T) {
|
|||
t.Fatalf("failed to load certificate from PEM: %v", err)
|
||||
}
|
||||
|
||||
keyID, err := FingerprintCert(cert)
|
||||
certID, err := FingerprintCert(cert)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to fingerprint the certificate: %v", err)
|
||||
}
|
||||
|
||||
// Tries to retrieve cert by Subject Key IDs
|
||||
_, err = store.GetCertificateByKeyID(keyID)
|
||||
_, err = store.GetCertificateByCertID(certID)
|
||||
if err != nil {
|
||||
t.Fatalf("expected certificate in store: %s", keyID)
|
||||
t.Fatalf("expected certificate in store: %s", certID)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,47 @@ import (
|
|||
|
||||
const certExtension string = "crt"
|
||||
|
||||
// ErrNoCertificatesFound is returned when no certificates are found for a
|
||||
// GetCertificatesBy*
|
||||
type ErrNoCertificatesFound struct {
|
||||
query string
|
||||
}
|
||||
|
||||
// ErrNoCertificatesFound is returned when no certificates are found for a
|
||||
// GetCertificatesBy*
|
||||
func (err ErrNoCertificatesFound) Error() string {
|
||||
return fmt.Sprintf("error, no certificates found in the keystore match: %s", err.query)
|
||||
}
|
||||
|
||||
// ErrCertValidation is returned when a certificate doesn't pass the store specific
|
||||
// validations
|
||||
type ErrCertValidation struct {
|
||||
}
|
||||
|
||||
// ErrCertValidation is returned when a certificate doesn't pass the store specific
|
||||
// validations
|
||||
func (err ErrCertValidation) Error() string {
|
||||
return fmt.Sprintf("store-specific certificate validations failed")
|
||||
}
|
||||
|
||||
// ErrCertExists is returned when a Certificate already exists in the key store
|
||||
type ErrCertExists struct {
|
||||
}
|
||||
|
||||
// ErrCertExists is returned when a Certificate already exists in the key store
|
||||
func (err ErrCertExists) Error() string {
|
||||
return fmt.Sprintf("certificate already in the store")
|
||||
}
|
||||
|
||||
// X509Store is the interface for all X509Stores
|
||||
type X509Store interface {
|
||||
AddCert(cert *x509.Certificate) error
|
||||
AddCertFromPEM(pemCerts []byte) error
|
||||
AddCertFromFile(filename string) error
|
||||
RemoveCert(cert *x509.Certificate) error
|
||||
GetCertificateByKeyID(keyID string) (*x509.Certificate, error)
|
||||
RemoveAll() error
|
||||
GetCertificateByCertID(certID string) (*x509.Certificate, error)
|
||||
GetCertificatesByCN(cn string) ([]*x509.Certificate, error)
|
||||
GetCertificates() []*x509.Certificate
|
||||
GetCertificatePool() *x509.CertPool
|
||||
GetVerifyOptions(dnsName string) (x509.VerifyOptions, error)
|
||||
|
|
|
@ -127,25 +127,66 @@ func loadCertsFromDir(s *X509FileStore) {
|
|||
}
|
||||
}
|
||||
|
||||
// LoadCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
// LoadCertFromFile loads the first certificate from the file provided. The
|
||||
// data is expected to be PEM Encoded and contain one of more certificates
|
||||
// with PEM type "CERTIFICATE"
|
||||
func LoadCertFromFile(filename string) (*x509.Certificate, error) {
|
||||
// TODO(diogo): handle multiple certificates in one file.
|
||||
certs, err := LoadCertBundleFromFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return certs[0], nil
|
||||
}
|
||||
|
||||
// LoadCertBundleFromFile loads certificates from the []byte provided. The
|
||||
// data is expected to be PEM Encoded and contain one of more certificates
|
||||
// with PEM type "CERTIFICATE"
|
||||
func LoadCertBundleFromFile(filename string) ([]*x509.Certificate, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return LoadCertBundleFromPEM(b)
|
||||
}
|
||||
|
||||
// LoadCertBundleFromPEM loads certificates from the []byte provided. The
|
||||
// data is expected to be PEM Encoded and contain one of more certificates
|
||||
// with PEM type "CERTIFICATE"
|
||||
func LoadCertBundleFromPEM(pemBytes []byte) ([]*x509.Certificate, error) {
|
||||
certificates := []*x509.Certificate{}
|
||||
var block *pem.Block
|
||||
block, b = pem.Decode(b)
|
||||
for ; block != nil; block, b = pem.Decode(b) {
|
||||
block, pemBytes = pem.Decode(pemBytes)
|
||||
for ; block != nil; block, pemBytes = pem.Decode(pemBytes) {
|
||||
if block.Type == "CERTIFICATE" {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err == nil {
|
||||
return cert, nil
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates = append(certificates, cert)
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid pem block type: %s", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("could not load certificate from file")
|
||||
if len(certificates) == 0 {
|
||||
return nil, fmt.Errorf("no valid certificates found")
|
||||
}
|
||||
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
// GetLeafCerts parses a list of x509 Certificates and returns all of them
|
||||
// that aren't CA
|
||||
func GetLeafCerts(certs []*x509.Certificate) []*x509.Certificate {
|
||||
var leafCerts []*x509.Certificate
|
||||
for _, cert := range certs {
|
||||
if cert.IsCA {
|
||||
continue
|
||||
}
|
||||
leafCerts = append(leafCerts, cert)
|
||||
}
|
||||
return leafCerts
|
||||
}
|
||||
|
||||
// ParsePEMPrivateKey returns a data.PrivateKey from a PEM encoded private key. It
|
||||
|
|
Loading…
Reference in New Issue