docs/trustmanager/x509store.go

145 lines
4.5 KiB
Go

package trustmanager
import (
"crypto/x509"
"errors"
"fmt"
)
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")
}
// ErrBadCertificateStore is returned when there is an internal inconsistency
// in our x509 store
type ErrBadCertificateStore struct {
}
// ErrBadCertificateStore is returned when there is an internal inconsistency
// in our x509 store
func (err ErrBadCertificateStore) Error() string {
return fmt.Sprintf("inconsistent certificate 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
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)
}
// CertID represent the ID used to identify certificates
type CertID string
// Validator is a convenience type to create validating function that filters
// certificates that get added to the store
type Validator interface {
Validate(cert *x509.Certificate) bool
}
// ValidatorFunc is a convenience type to create functions that implement
// the Validator interface
type ValidatorFunc func(cert *x509.Certificate) bool
// Validate implements the Validator interface to allow for any func() bool method
// to be passed as a Validator
func (vf ValidatorFunc) Validate(cert *x509.Certificate) bool {
return vf(cert)
}
// Verify operates on an X509Store and validates the existence of a chain of trust
// between a leafCertificate and a CA present inside of the X509 Store.
// It requires at least two certificates in certList, a leaf Certificate and an
// intermediate CA certificate.
func Verify(s X509Store, dnsName string, certList []*x509.Certificate) error {
// If we have no Certificates loaded return error (we don't want to revert to using
// system CAs).
if len(s.GetCertificates()) == 0 {
return errors.New("no root CAs available")
}
// At a minimum we should be provided a leaf cert and an intermediate.
if len(certList) < 2 {
return errors.New("certificate and at least one intermediate needed")
}
// Get the VerifyOptions from the keystore for a base dnsName
opts, err := s.GetVerifyOptions(dnsName)
if err != nil {
return err
}
// Create a Certificate Pool for our intermediate certificates
intPool := x509.NewCertPool()
var leafCert *x509.Certificate
// Iterate through all the certificates
for _, c := range certList {
// If the cert is a CA, we add it to the intermediates pool. If not, we call
// it the leaf cert
if c.IsCA {
intPool.AddCert(c)
continue
}
// Certificate is not a CA, it must be our leaf certificate.
// If we already found one, bail with error
if leafCert != nil {
return errors.New("more than one leaf certificate found")
}
leafCert = c
}
// We exited the loop with no leaf certificates
if leafCert == nil {
return errors.New("no leaf certificates found")
}
// We have one leaf certificate and at least one intermediate. Lets add this
// Cert Pool as the Intermediates list on our VerifyOptions
opts.Intermediates = intPool
// Finally, let's call Verify on our leafCert with our fully configured options
chains, err := leafCert.Verify(opts)
if len(chains) == 0 || err != nil {
return fmt.Errorf("certificate verification failed: %v", err)
}
return nil
}