2017-04-14 20:46:19 +00:00
|
|
|
package rootcerts
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
2020-01-28 10:54:49 +00:00
|
|
|
"errors"
|
2017-04-14 20:46:19 +00:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
)
|
|
|
|
|
2020-01-28 10:54:49 +00:00
|
|
|
// Config determines where LoadCACerts will load certificates from. When CAFile,
|
|
|
|
// CACertificate and CAPath are blank, this library's functions will either load
|
2017-04-14 20:46:19 +00:00
|
|
|
// system roots explicitly and return them, or set the CertPool to nil to allow
|
|
|
|
// Go's standard library to load system certs.
|
|
|
|
type Config struct {
|
|
|
|
// CAFile is a path to a PEM-encoded certificate file or bundle. Takes
|
2020-01-28 10:54:49 +00:00
|
|
|
// precedence over CACertificate and CAPath.
|
2017-04-14 20:46:19 +00:00
|
|
|
CAFile string
|
|
|
|
|
2020-01-28 10:54:49 +00:00
|
|
|
// CACertificate is a PEM-encoded certificate or bundle. Takes precedence
|
|
|
|
// over CAPath.
|
|
|
|
CACertificate []byte
|
|
|
|
|
2017-04-14 20:46:19 +00:00
|
|
|
// CAPath is a path to a directory populated with PEM-encoded certificates.
|
|
|
|
CAPath string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConfigureTLS sets up the RootCAs on the provided tls.Config based on the
|
|
|
|
// Config specified.
|
|
|
|
func ConfigureTLS(t *tls.Config, c *Config) error {
|
|
|
|
if t == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
pool, err := LoadCACerts(c)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
t.RootCAs = pool
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadCACerts loads a CertPool based on the Config specified.
|
|
|
|
func LoadCACerts(c *Config) (*x509.CertPool, error) {
|
|
|
|
if c == nil {
|
|
|
|
c = &Config{}
|
|
|
|
}
|
|
|
|
if c.CAFile != "" {
|
|
|
|
return LoadCAFile(c.CAFile)
|
|
|
|
}
|
2020-01-28 10:54:49 +00:00
|
|
|
if len(c.CACertificate) != 0 {
|
|
|
|
return AppendCertificate(c.CACertificate)
|
|
|
|
}
|
2017-04-14 20:46:19 +00:00
|
|
|
if c.CAPath != "" {
|
|
|
|
return LoadCAPath(c.CAPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
return LoadSystemCAs()
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadCAFile loads a single PEM-encoded file from the path specified.
|
|
|
|
func LoadCAFile(caFile string) (*x509.CertPool, error) {
|
|
|
|
pool := x509.NewCertPool()
|
|
|
|
|
|
|
|
pem, err := ioutil.ReadFile(caFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Error loading CA File: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ok := pool.AppendCertsFromPEM(pem)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", caFile)
|
|
|
|
}
|
|
|
|
|
|
|
|
return pool, nil
|
|
|
|
}
|
|
|
|
|
2020-01-28 10:54:49 +00:00
|
|
|
// AppendCertificate appends an in-memory PEM-encoded certificate or bundle and returns a pool.
|
|
|
|
func AppendCertificate(ca []byte) (*x509.CertPool, error) {
|
|
|
|
pool := x509.NewCertPool()
|
|
|
|
|
|
|
|
ok := pool.AppendCertsFromPEM(ca)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Error appending CA: Couldn't parse PEM")
|
|
|
|
}
|
|
|
|
|
|
|
|
return pool, nil
|
|
|
|
}
|
|
|
|
|
2017-04-14 20:46:19 +00:00
|
|
|
// LoadCAPath walks the provided path and loads all certificates encounted into
|
|
|
|
// a pool.
|
|
|
|
func LoadCAPath(caPath string) (*x509.CertPool, error) {
|
|
|
|
pool := x509.NewCertPool()
|
|
|
|
walkFn := func(path string, info os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if info.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
pem, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error loading file from CAPath: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ok := pool.AppendCertsFromPEM(pem)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Error loading CA Path: Couldn't parse PEM in: %s", path)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := filepath.Walk(caPath, walkFn)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return pool, nil
|
|
|
|
}
|