77e69b9cf3
Signed-off-by: Olivier Gambier <olivier@docker.com>
176 lines
4.8 KiB
Go
176 lines
4.8 KiB
Go
package libtrust
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
type certTemplateInfo struct {
|
|
commonName string
|
|
domains []string
|
|
ipAddresses []net.IP
|
|
isCA bool
|
|
clientAuth bool
|
|
serverAuth bool
|
|
}
|
|
|
|
func generateCertTemplate(info *certTemplateInfo) *x509.Certificate {
|
|
// Generate a certificate template which is valid from the past week to
|
|
// 10 years from now. The usage of the certificate depends on the
|
|
// specified fields in the given certTempInfo object.
|
|
var (
|
|
keyUsage x509.KeyUsage
|
|
extKeyUsage []x509.ExtKeyUsage
|
|
)
|
|
|
|
if info.isCA {
|
|
keyUsage = x509.KeyUsageCertSign
|
|
}
|
|
|
|
if info.clientAuth {
|
|
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageClientAuth)
|
|
}
|
|
|
|
if info.serverAuth {
|
|
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth)
|
|
}
|
|
|
|
return &x509.Certificate{
|
|
SerialNumber: big.NewInt(0),
|
|
Subject: pkix.Name{
|
|
CommonName: info.commonName,
|
|
},
|
|
NotBefore: time.Now().Add(-time.Hour * 24 * 7),
|
|
NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10),
|
|
DNSNames: info.domains,
|
|
IPAddresses: info.ipAddresses,
|
|
IsCA: info.isCA,
|
|
KeyUsage: keyUsage,
|
|
ExtKeyUsage: extKeyUsage,
|
|
BasicConstraintsValid: info.isCA,
|
|
}
|
|
}
|
|
|
|
func generateCert(pub PublicKey, priv PrivateKey, subInfo, issInfo *certTemplateInfo) (cert *x509.Certificate, err error) {
|
|
pubCertTemplate := generateCertTemplate(subInfo)
|
|
privCertTemplate := generateCertTemplate(issInfo)
|
|
|
|
certDER, err := x509.CreateCertificate(
|
|
rand.Reader, pubCertTemplate, privCertTemplate,
|
|
pub.CryptoPublicKey(), priv.CryptoPrivateKey(),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create certificate: %s", err)
|
|
}
|
|
|
|
cert, err = x509.ParseCertificate(certDER)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse certificate: %s", err)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// GenerateSelfSignedServerCert creates a self-signed certificate for the
|
|
// given key which is to be used for TLS servers with the given domains and
|
|
// IP addresses.
|
|
func GenerateSelfSignedServerCert(key PrivateKey, domains []string, ipAddresses []net.IP) (*x509.Certificate, error) {
|
|
info := &certTemplateInfo{
|
|
commonName: key.KeyID(),
|
|
domains: domains,
|
|
ipAddresses: ipAddresses,
|
|
serverAuth: true,
|
|
}
|
|
|
|
return generateCert(key.PublicKey(), key, info, info)
|
|
}
|
|
|
|
// GenerateSelfSignedClientCert creates a self-signed certificate for the
|
|
// given key which is to be used for TLS clients.
|
|
func GenerateSelfSignedClientCert(key PrivateKey) (*x509.Certificate, error) {
|
|
info := &certTemplateInfo{
|
|
commonName: key.KeyID(),
|
|
clientAuth: true,
|
|
}
|
|
|
|
return generateCert(key.PublicKey(), key, info, info)
|
|
}
|
|
|
|
// GenerateCACert creates a certificate which can be used as a trusted
|
|
// certificate authority.
|
|
func GenerateCACert(signer PrivateKey, trustedKey PublicKey) (*x509.Certificate, error) {
|
|
subjectInfo := &certTemplateInfo{
|
|
commonName: trustedKey.KeyID(),
|
|
isCA: true,
|
|
}
|
|
issuerInfo := &certTemplateInfo{
|
|
commonName: signer.KeyID(),
|
|
}
|
|
|
|
return generateCert(trustedKey, signer, subjectInfo, issuerInfo)
|
|
}
|
|
|
|
// GenerateCACertPool creates a certificate authority pool to be used for a
|
|
// TLS configuration. Any self-signed certificates issued by the specified
|
|
// trusted keys will be verified during a TLS handshake
|
|
func GenerateCACertPool(signer PrivateKey, trustedKeys []PublicKey) (*x509.CertPool, error) {
|
|
certPool := x509.NewCertPool()
|
|
|
|
for _, trustedKey := range trustedKeys {
|
|
cert, err := GenerateCACert(signer, trustedKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate CA certificate: %s", err)
|
|
}
|
|
|
|
certPool.AddCert(cert)
|
|
}
|
|
|
|
return certPool, nil
|
|
}
|
|
|
|
// LoadCertificateBundle loads certificates from the given file. The file should be pem encoded
|
|
// containing one or more certificates. The expected pem type is "CERTIFICATE".
|
|
func LoadCertificateBundle(filename string) ([]*x509.Certificate, error) {
|
|
b, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certificates := []*x509.Certificate{}
|
|
var block *pem.Block
|
|
block, b = pem.Decode(b)
|
|
for ; block != nil; block, b = pem.Decode(b) {
|
|
if block.Type == "CERTIFICATE" {
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certificates = append(certificates, cert)
|
|
} else {
|
|
return nil, fmt.Errorf("invalid pem block type: %s", block.Type)
|
|
}
|
|
}
|
|
|
|
return certificates, nil
|
|
}
|
|
|
|
// LoadCertificatePool loads a CA pool from the given file. The file should be pem encoded
|
|
// containing one or more certificates. The expected pem type is "CERTIFICATE".
|
|
func LoadCertificatePool(filename string) (*x509.CertPool, error) {
|
|
certs, err := LoadCertificateBundle(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pool := x509.NewCertPool()
|
|
for _, cert := range certs {
|
|
pool.AddCert(cert)
|
|
}
|
|
return pool, nil
|
|
}
|