blob: 3429c82cdff90d86b17cbc80c104f4e52a51db80 [file] [log] [blame]
sslobodrd046be82019-01-16 10:02:22 -05001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package cert
18
19import (
20 "bytes"
21 "crypto"
22 "crypto/ecdsa"
23 "crypto/elliptic"
24 "crypto/rand"
25 cryptorand "crypto/rand"
26 "crypto/rsa"
27 "crypto/x509"
28 "crypto/x509/pkix"
29 "encoding/pem"
30 "errors"
31 "fmt"
32 "io/ioutil"
33 "math"
34 "math/big"
35 "net"
36 "path"
37 "strings"
38 "time"
39)
40
41const (
42 rsaKeySize = 2048
43 duration365d = time.Hour * 24 * 365
44)
45
46// Config contains the basic fields required for creating a certificate
47type Config struct {
48 CommonName string
49 Organization []string
50 AltNames AltNames
51 Usages []x509.ExtKeyUsage
52}
53
54// AltNames contains the domain names and IP addresses that will be added
55// to the API Server's x509 certificate SubAltNames field. The values will
56// be passed directly to the x509.Certificate object.
57type AltNames struct {
58 DNSNames []string
59 IPs []net.IP
60}
61
62// NewPrivateKey creates an RSA private key
63func NewPrivateKey() (*rsa.PrivateKey, error) {
64 return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
65}
66
67// NewSelfSignedCACert creates a CA certificate
68func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) {
69 now := time.Now()
70 tmpl := x509.Certificate{
71 SerialNumber: new(big.Int).SetInt64(0),
72 Subject: pkix.Name{
73 CommonName: cfg.CommonName,
74 Organization: cfg.Organization,
75 },
76 NotBefore: now.UTC(),
77 NotAfter: now.Add(duration365d * 10).UTC(),
78 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
79 BasicConstraintsValid: true,
80 IsCA: true,
81 }
82
83 certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
84 if err != nil {
85 return nil, err
86 }
87 return x509.ParseCertificate(certDERBytes)
88}
89
90// NewSignedCert creates a signed certificate using the given CA certificate and key
91func NewSignedCert(cfg Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
92 serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
93 if err != nil {
94 return nil, err
95 }
96 if len(cfg.CommonName) == 0 {
97 return nil, errors.New("must specify a CommonName")
98 }
99 if len(cfg.Usages) == 0 {
100 return nil, errors.New("must specify at least one ExtKeyUsage")
101 }
102
103 certTmpl := x509.Certificate{
104 Subject: pkix.Name{
105 CommonName: cfg.CommonName,
106 Organization: cfg.Organization,
107 },
108 DNSNames: cfg.AltNames.DNSNames,
109 IPAddresses: cfg.AltNames.IPs,
110 SerialNumber: serial,
111 NotBefore: caCert.NotBefore,
112 NotAfter: time.Now().Add(duration365d).UTC(),
113 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
114 ExtKeyUsage: cfg.Usages,
115 }
116 certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
117 if err != nil {
118 return nil, err
119 }
120 return x509.ParseCertificate(certDERBytes)
121}
122
123// MakeEllipticPrivateKeyPEM creates an ECDSA private key
124func MakeEllipticPrivateKeyPEM() ([]byte, error) {
125 privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
126 if err != nil {
127 return nil, err
128 }
129
130 derBytes, err := x509.MarshalECPrivateKey(privateKey)
131 if err != nil {
132 return nil, err
133 }
134
135 privateKeyPemBlock := &pem.Block{
136 Type: ECPrivateKeyBlockType,
137 Bytes: derBytes,
138 }
139 return pem.EncodeToMemory(privateKeyPemBlock), nil
140}
141
142// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
143// Host may be an IP or a DNS name
144// You may also specify additional subject alt names (either ip or dns names) for the certificate.
145func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
146 return GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, "")
147}
148
149// GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
150// Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
151// for the certificate.
152//
153// If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
154// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
155// <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
156// Certs/keys not existing in that directory are created.
157func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
158 validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
159 maxAge := time.Hour * 24 * 365 // one year self-signed certs
160
161 baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
162 certFixturePath := path.Join(fixtureDirectory, baseName+".crt")
163 keyFixturePath := path.Join(fixtureDirectory, baseName+".key")
164 if len(fixtureDirectory) > 0 {
165 cert, err := ioutil.ReadFile(certFixturePath)
166 if err == nil {
167 key, err := ioutil.ReadFile(keyFixturePath)
168 if err == nil {
169 return cert, key, nil
170 }
171 return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
172 }
173 maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
174 }
175
176 caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
177 if err != nil {
178 return nil, nil, err
179 }
180
181 caTemplate := x509.Certificate{
182 SerialNumber: big.NewInt(1),
183 Subject: pkix.Name{
184 CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
185 },
186 NotBefore: validFrom,
187 NotAfter: validFrom.Add(maxAge),
188
189 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
190 BasicConstraintsValid: true,
191 IsCA: true,
192 }
193
194 caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
195 if err != nil {
196 return nil, nil, err
197 }
198
199 caCertificate, err := x509.ParseCertificate(caDERBytes)
200 if err != nil {
201 return nil, nil, err
202 }
203
204 priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
205 if err != nil {
206 return nil, nil, err
207 }
208
209 template := x509.Certificate{
210 SerialNumber: big.NewInt(2),
211 Subject: pkix.Name{
212 CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
213 },
214 NotBefore: validFrom,
215 NotAfter: validFrom.Add(maxAge),
216
217 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
218 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
219 BasicConstraintsValid: true,
220 }
221
222 if ip := net.ParseIP(host); ip != nil {
223 template.IPAddresses = append(template.IPAddresses, ip)
224 } else {
225 template.DNSNames = append(template.DNSNames, host)
226 }
227
228 template.IPAddresses = append(template.IPAddresses, alternateIPs...)
229 template.DNSNames = append(template.DNSNames, alternateDNS...)
230
231 derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
232 if err != nil {
233 return nil, nil, err
234 }
235
236 // Generate cert, followed by ca
237 certBuffer := bytes.Buffer{}
238 if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
239 return nil, nil, err
240 }
241 if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
242 return nil, nil, err
243 }
244
245 // Generate key
246 keyBuffer := bytes.Buffer{}
247 if err := pem.Encode(&keyBuffer, &pem.Block{Type: RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
248 return nil, nil, err
249 }
250
251 if len(fixtureDirectory) > 0 {
252 if err := ioutil.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
253 return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
254 }
255 if err := ioutil.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0644); err != nil {
256 return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
257 }
258 }
259
260 return certBuffer.Bytes(), keyBuffer.Bytes(), nil
261}
262
263func ipsToStrings(ips []net.IP) []string {
264 ss := make([]string, 0, len(ips))
265 for _, ip := range ips {
266 ss = append(ss, ip.String())
267 }
268 return ss
269}