blob: fb7f5facc7ce2f1fad1f28ea5121767273f3b2ad [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
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/ecdsa"
22 "crypto/elliptic"
23 cryptorand "crypto/rand"
24 "crypto/rsa"
25 "crypto/x509"
26 "crypto/x509/pkix"
27 "encoding/pem"
28 "errors"
29 "fmt"
30 "math"
31 "math/big"
32 "net"
33 "time"
34)
35
36const (
37 rsaKeySize = 2048
38 duration365d = time.Hour * 24 * 365
39)
40
41// Config contains the basic fields required for creating a certificate
42type Config struct {
43 CommonName string
44 Organization []string
45 AltNames AltNames
46 Usages []x509.ExtKeyUsage
47}
48
49// AltNames contains the domain names and IP addresses that will be added
50// to the API Server's x509 certificate SubAltNames field. The values will
51// be passed directly to the x509.Certificate object.
52type AltNames struct {
53 DNSNames []string
54 IPs []net.IP
55}
56
57// NewPrivateKey creates an RSA private key
58func NewPrivateKey() (*rsa.PrivateKey, error) {
59 return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
60}
61
62// NewSelfSignedCACert creates a CA certificate
63func NewSelfSignedCACert(cfg Config, key *rsa.PrivateKey) (*x509.Certificate, error) {
64 now := time.Now()
65 tmpl := x509.Certificate{
66 SerialNumber: new(big.Int).SetInt64(0),
67 Subject: pkix.Name{
68 CommonName: cfg.CommonName,
69 Organization: cfg.Organization,
70 },
71 NotBefore: now.UTC(),
72 NotAfter: now.Add(duration365d * 10).UTC(),
73 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
74 BasicConstraintsValid: true,
75 IsCA: true,
76 }
77
78 certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
79 if err != nil {
80 return nil, err
81 }
82 return x509.ParseCertificate(certDERBytes)
83}
84
85// NewSignedCert creates a signed certificate using the given CA certificate and key
86func NewSignedCert(cfg Config, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
87 serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
88 if err != nil {
89 return nil, err
90 }
91 if len(cfg.CommonName) == 0 {
92 return nil, errors.New("must specify a CommonName")
93 }
94 if len(cfg.Usages) == 0 {
95 return nil, errors.New("must specify at least one ExtKeyUsage")
96 }
97
98 certTmpl := x509.Certificate{
99 Subject: pkix.Name{
100 CommonName: cfg.CommonName,
101 Organization: cfg.Organization,
102 },
103 DNSNames: cfg.AltNames.DNSNames,
104 IPAddresses: cfg.AltNames.IPs,
105 SerialNumber: serial,
106 NotBefore: caCert.NotBefore,
107 NotAfter: time.Now().Add(duration365d).UTC(),
108 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
109 ExtKeyUsage: cfg.Usages,
110 }
111 certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
112 if err != nil {
113 return nil, err
114 }
115 return x509.ParseCertificate(certDERBytes)
116}
117
118// MakeEllipticPrivateKeyPEM creates an ECDSA private key
119func MakeEllipticPrivateKeyPEM() ([]byte, error) {
120 privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
121 if err != nil {
122 return nil, err
123 }
124
125 derBytes, err := x509.MarshalECPrivateKey(privateKey)
126 if err != nil {
127 return nil, err
128 }
129
130 privateKeyPemBlock := &pem.Block{
131 Type: ECPrivateKeyBlockType,
132 Bytes: derBytes,
133 }
134 return pem.EncodeToMemory(privateKeyPemBlock), nil
135}
136
137// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
138// Host may be an IP or a DNS name
139// You may also specify additional subject alt names (either ip or dns names) for the certificate
140func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
141 caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
142 if err != nil {
143 return nil, nil, err
144 }
145
146 caTemplate := x509.Certificate{
147 SerialNumber: big.NewInt(1),
148 Subject: pkix.Name{
149 CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
150 },
151 NotBefore: time.Now(),
152 NotAfter: time.Now().Add(time.Hour * 24 * 365),
153
154 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
155 BasicConstraintsValid: true,
156 IsCA: true,
157 }
158
159 caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
160 if err != nil {
161 return nil, nil, err
162 }
163
164 caCertificate, err := x509.ParseCertificate(caDERBytes)
165 if err != nil {
166 return nil, nil, err
167 }
168
169 priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
170 if err != nil {
171 return nil, nil, err
172 }
173
174 template := x509.Certificate{
175 SerialNumber: big.NewInt(2),
176 Subject: pkix.Name{
177 CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
178 },
179 NotBefore: time.Now(),
180 NotAfter: time.Now().Add(time.Hour * 24 * 365),
181
182 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
183 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
184 BasicConstraintsValid: true,
185 }
186
187 if ip := net.ParseIP(host); ip != nil {
188 template.IPAddresses = append(template.IPAddresses, ip)
189 } else {
190 template.DNSNames = append(template.DNSNames, host)
191 }
192
193 template.IPAddresses = append(template.IPAddresses, alternateIPs...)
194 template.DNSNames = append(template.DNSNames, alternateDNS...)
195
196 derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
197 if err != nil {
198 return nil, nil, err
199 }
200
201 // Generate cert, followed by ca
202 certBuffer := bytes.Buffer{}
203 if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
204 return nil, nil, err
205 }
206 if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
207 return nil, nil, err
208 }
209
210 // Generate key
211 keyBuffer := bytes.Buffer{}
212 if err := pem.Encode(&keyBuffer, &pem.Block{Type: RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
213 return nil, nil, err
214 }
215
216 return certBuffer.Bytes(), keyBuffer.Bytes(), nil
217}
218
219// FormatBytesCert receives byte array certificate and formats in human-readable format
220func FormatBytesCert(cert []byte) (string, error) {
221 block, _ := pem.Decode(cert)
222 c, err := x509.ParseCertificate(block.Bytes)
223 if err != nil {
224 return "", fmt.Errorf("failed to parse certificate [%v]", err)
225 }
226 return FormatCert(c), nil
227}
228
229// FormatCert receives certificate and formats in human-readable format
230func FormatCert(c *x509.Certificate) string {
231 var ips []string
232 for _, ip := range c.IPAddresses {
233 ips = append(ips, ip.String())
234 }
235 altNames := append(ips, c.DNSNames...)
236 res := fmt.Sprintf(
237 "Issuer: CN=%s | Subject: CN=%s | CA: %t\n",
238 c.Issuer.CommonName, c.Subject.CommonName, c.IsCA,
239 )
240 res += fmt.Sprintf("Not before: %s Not After: %s", c.NotBefore, c.NotAfter)
241 if len(altNames) > 0 {
242 res += fmt.Sprintf("\nAlternate Names: %v", altNames)
243 }
244 return res
245}