Refactor client certificates

This commit is contained in:
Adnan Maolood
2020-10-28 13:40:25 -04:00
parent 768664e0c5
commit fbd97a62de
6 changed files with 140 additions and 155 deletions

75
cert.go
View File

@@ -20,9 +20,9 @@ type CertificateStore struct {
store map[string]tls.Certificate
}
// Add adds a certificate for the given hostname to the store.
// Add adds a certificate for the given scope to the store.
// It tries to parse the certificate if it is not already parsed.
func (c *CertificateStore) Add(hostname string, cert tls.Certificate) {
func (c *CertificateStore) Add(scope string, cert tls.Certificate) {
if c.store == nil {
c.store = map[string]tls.Certificate{}
}
@@ -33,7 +33,7 @@ func (c *CertificateStore) Add(hostname string, cert tls.Certificate) {
cert.Leaf = parsed
}
}
c.store[hostname] = cert
c.store[scope] = cert
}
// Lookup returns the certificate for the given hostname.
@@ -49,6 +49,22 @@ func (c *CertificateStore) Lookup(hostname string) (*tls.Certificate, error) {
return &cert, nil
}
// lookup returns the certificate for the given hostname + path.
func (c *CertificateStore) lookup(scope string) (*tls.Certificate, error) {
for {
cert, err := c.Lookup(scope)
switch err {
case ErrCertificateExpired, nil:
return cert, err
}
scope = path.Dir(scope)
if scope == "." {
break
}
}
return nil, ErrCertificateUnknown
}
// Load loads certificates from the given path.
// The path should lead to a directory containing certificates and private keys
// in the form hostname.crt and hostname.key.
@@ -71,36 +87,16 @@ func (c *CertificateStore) Load(path string) error {
return nil
}
type ClientCertificateStore struct {
CertificateStore
// CertificateOptions configures how a certificate is created.
type CertificateOptions struct {
IPAddresses []net.IP
DNSNames []string
Duration time.Duration
}
func (c *ClientCertificateStore) Lookup(hostname, urlPath string) (*tls.Certificate, error) {
urlPath = path.Clean(urlPath)
if urlPath == "." {
urlPath = "/"
}
if urlPath[0] != '/' {
urlPath = "/" + urlPath
}
for {
cert, err := c.CertificateStore.Lookup(hostname + urlPath)
switch err {
case ErrCertificateExpired, nil:
return cert, err
}
slash := urlPath == "/"
urlPath = path.Dir(urlPath)
if slash && urlPath == "/" {
break
}
}
return nil, ErrCertificateUnknown
}
// NewCertificate creates and returns a new parsed certificate.
func NewCertificate(host string, duration time.Duration) (tls.Certificate, error) {
crt, priv, err := newX509KeyPair(host, duration)
// CreateCertificate creates a new TLS certificate.
func CreateCertificate(options CertificateOptions) (tls.Certificate, error) {
crt, priv, err := newX509KeyPair(options)
if err != nil {
return tls.Certificate{}, err
}
@@ -112,7 +108,7 @@ func NewCertificate(host string, duration time.Duration) (tls.Certificate, error
}
// newX509KeyPair creates and returns a new certificate and private key.
func newX509KeyPair(host string, duration time.Duration) (*x509.Certificate, crypto.PrivateKey, error) {
func newX509KeyPair(options CertificateOptions) (*x509.Certificate, crypto.PrivateKey, error) {
// Generate an ED25519 private key
_, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
@@ -131,7 +127,7 @@ func newX509KeyPair(host string, duration time.Duration) (*x509.Certificate, cry
}
notBefore := time.Now()
notAfter := notBefore.Add(duration)
notAfter := notBefore.Add(options.Duration)
template := x509.Certificate{
SerialNumber: serialNumber,
@@ -140,17 +136,8 @@ func newX509KeyPair(host string, duration time.Duration) (*x509.Certificate, cry
KeyUsage: keyUsage,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
if host != "" {
hosts := strings.Split(host, ",")
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
} else {
template.DNSNames = append(template.DNSNames, h)
}
}
IPAddresses: options.IPAddresses,
DNSNames: options.DNSNames,
}
crt, err := x509.CreateCertificate(rand.Reader, &template, &template, public, priv)