blob: 3dcca33cb180e2a8b2509f660dc46403ac4bac15 [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001package libtrust
2
3import (
4 "crypto/rand"
5 "crypto/x509"
6 "crypto/x509/pkix"
7 "encoding/pem"
8 "fmt"
9 "io/ioutil"
10 "math/big"
11 "net"
12 "time"
13)
14
15type certTemplateInfo struct {
16 commonName string
17 domains []string
18 ipAddresses []net.IP
19 isCA bool
20 clientAuth bool
21 serverAuth bool
22}
23
24func generateCertTemplate(info *certTemplateInfo) *x509.Certificate {
25 // Generate a certificate template which is valid from the past week to
26 // 10 years from now. The usage of the certificate depends on the
27 // specified fields in the given certTempInfo object.
28 var (
29 keyUsage x509.KeyUsage
30 extKeyUsage []x509.ExtKeyUsage
31 )
32
33 if info.isCA {
34 keyUsage = x509.KeyUsageCertSign
35 }
36
37 if info.clientAuth {
38 extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageClientAuth)
39 }
40
41 if info.serverAuth {
42 extKeyUsage = append(extKeyUsage, x509.ExtKeyUsageServerAuth)
43 }
44
45 return &x509.Certificate{
46 SerialNumber: big.NewInt(0),
47 Subject: pkix.Name{
48 CommonName: info.commonName,
49 },
50 NotBefore: time.Now().Add(-time.Hour * 24 * 7),
51 NotAfter: time.Now().Add(time.Hour * 24 * 365 * 10),
52 DNSNames: info.domains,
53 IPAddresses: info.ipAddresses,
54 IsCA: info.isCA,
55 KeyUsage: keyUsage,
56 ExtKeyUsage: extKeyUsage,
57 BasicConstraintsValid: info.isCA,
58 }
59}
60
61func generateCert(pub PublicKey, priv PrivateKey, subInfo, issInfo *certTemplateInfo) (cert *x509.Certificate, err error) {
62 pubCertTemplate := generateCertTemplate(subInfo)
63 privCertTemplate := generateCertTemplate(issInfo)
64
65 certDER, err := x509.CreateCertificate(
66 rand.Reader, pubCertTemplate, privCertTemplate,
67 pub.CryptoPublicKey(), priv.CryptoPrivateKey(),
68 )
69 if err != nil {
70 return nil, fmt.Errorf("failed to create certificate: %s", err)
71 }
72
73 cert, err = x509.ParseCertificate(certDER)
74 if err != nil {
75 return nil, fmt.Errorf("failed to parse certificate: %s", err)
76 }
77
78 return
79}
80
81// GenerateSelfSignedServerCert creates a self-signed certificate for the
82// given key which is to be used for TLS servers with the given domains and
83// IP addresses.
84func GenerateSelfSignedServerCert(key PrivateKey, domains []string, ipAddresses []net.IP) (*x509.Certificate, error) {
85 info := &certTemplateInfo{
86 commonName: key.KeyID(),
87 domains: domains,
88 ipAddresses: ipAddresses,
89 serverAuth: true,
90 }
91
92 return generateCert(key.PublicKey(), key, info, info)
93}
94
95// GenerateSelfSignedClientCert creates a self-signed certificate for the
96// given key which is to be used for TLS clients.
97func GenerateSelfSignedClientCert(key PrivateKey) (*x509.Certificate, error) {
98 info := &certTemplateInfo{
99 commonName: key.KeyID(),
100 clientAuth: true,
101 }
102
103 return generateCert(key.PublicKey(), key, info, info)
104}
105
106// GenerateCACert creates a certificate which can be used as a trusted
107// certificate authority.
108func GenerateCACert(signer PrivateKey, trustedKey PublicKey) (*x509.Certificate, error) {
109 subjectInfo := &certTemplateInfo{
110 commonName: trustedKey.KeyID(),
111 isCA: true,
112 }
113 issuerInfo := &certTemplateInfo{
114 commonName: signer.KeyID(),
115 }
116
117 return generateCert(trustedKey, signer, subjectInfo, issuerInfo)
118}
119
120// GenerateCACertPool creates a certificate authority pool to be used for a
121// TLS configuration. Any self-signed certificates issued by the specified
122// trusted keys will be verified during a TLS handshake
123func GenerateCACertPool(signer PrivateKey, trustedKeys []PublicKey) (*x509.CertPool, error) {
124 certPool := x509.NewCertPool()
125
126 for _, trustedKey := range trustedKeys {
127 cert, err := GenerateCACert(signer, trustedKey)
128 if err != nil {
129 return nil, fmt.Errorf("failed to generate CA certificate: %s", err)
130 }
131
132 certPool.AddCert(cert)
133 }
134
135 return certPool, nil
136}
137
138// LoadCertificateBundle loads certificates from the given file. The file should be pem encoded
139// containing one or more certificates. The expected pem type is "CERTIFICATE".
140func LoadCertificateBundle(filename string) ([]*x509.Certificate, error) {
141 b, err := ioutil.ReadFile(filename)
142 if err != nil {
143 return nil, err
144 }
145 certificates := []*x509.Certificate{}
146 var block *pem.Block
147 block, b = pem.Decode(b)
148 for ; block != nil; block, b = pem.Decode(b) {
149 if block.Type == "CERTIFICATE" {
150 cert, err := x509.ParseCertificate(block.Bytes)
151 if err != nil {
152 return nil, err
153 }
154 certificates = append(certificates, cert)
155 } else {
156 return nil, fmt.Errorf("invalid pem block type: %s", block.Type)
157 }
158 }
159
160 return certificates, nil
161}
162
163// LoadCertificatePool loads a CA pool from the given file. The file should be pem encoded
164// containing one or more certificates. The expected pem type is "CERTIFICATE".
165func LoadCertificatePool(filename string) (*x509.CertPool, error) {
166 certs, err := LoadCertificateBundle(filename)
167 if err != nil {
168 return nil, err
169 }
170 pool := x509.NewCertPool()
171 for _, cert := range certs {
172 pool.AddCert(cert)
173 }
174 return pool, nil
175}