2 Copyright 2014 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
25 cryptorand "crypto/rand"
43 duration365d = time.Hour * 24 * 365
46 // Config contains the basic fields required for creating a certificate
51 Usages []x509.ExtKeyUsage
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.
57 type AltNames struct {
62 // NewPrivateKey creates an RSA private key
63 func NewPrivateKey() (*rsa.PrivateKey, error) {
64 return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
67 // NewSelfSignedCACert creates a CA certificate
68 func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) {
70 tmpl := x509.Certificate{
71 SerialNumber: new(big.Int).SetInt64(0),
73 CommonName: cfg.CommonName,
74 Organization: cfg.Organization,
77 NotAfter: now.Add(duration365d * 10).UTC(),
78 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
79 BasicConstraintsValid: true,
83 certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
87 return x509.ParseCertificate(certDERBytes)
90 // NewSignedCert creates a signed certificate using the given CA certificate and key
91 func 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))
96 if len(cfg.CommonName) == 0 {
97 return nil, errors.New("must specify a CommonName")
99 if len(cfg.Usages) == 0 {
100 return nil, errors.New("must specify at least one ExtKeyUsage")
103 certTmpl := x509.Certificate{
105 CommonName: cfg.CommonName,
106 Organization: cfg.Organization,
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,
116 certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
120 return x509.ParseCertificate(certDERBytes)
123 // MakeEllipticPrivateKeyPEM creates an ECDSA private key
124 func MakeEllipticPrivateKeyPEM() ([]byte, error) {
125 privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
130 derBytes, err := x509.MarshalECPrivateKey(privateKey)
135 privateKeyPemBlock := &pem.Block{
136 Type: ECPrivateKeyBlockType,
139 return pem.EncodeToMemory(privateKeyPemBlock), nil
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.
145 func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
146 return GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, "")
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.
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.
157 func 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
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)
167 key, err := ioutil.ReadFile(keyFixturePath)
169 return cert, key, nil
171 return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
173 maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
176 caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
181 caTemplate := x509.Certificate{
182 SerialNumber: big.NewInt(1),
184 CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
186 NotBefore: validFrom,
187 NotAfter: validFrom.Add(maxAge),
189 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
190 BasicConstraintsValid: true,
194 caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
199 caCertificate, err := x509.ParseCertificate(caDERBytes)
204 priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
209 template := x509.Certificate{
210 SerialNumber: big.NewInt(2),
212 CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
214 NotBefore: validFrom,
215 NotAfter: validFrom.Add(maxAge),
217 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
218 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
219 BasicConstraintsValid: true,
222 if ip := net.ParseIP(host); ip != nil {
223 template.IPAddresses = append(template.IPAddresses, ip)
225 template.DNSNames = append(template.DNSNames, host)
228 template.IPAddresses = append(template.IPAddresses, alternateIPs...)
229 template.DNSNames = append(template.DNSNames, alternateDNS...)
231 derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
236 // Generate cert, followed by ca
237 certBuffer := bytes.Buffer{}
238 if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
241 if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
246 keyBuffer := bytes.Buffer{}
247 if err := pem.Encode(&keyBuffer, &pem.Block{Type: RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
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)
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)
260 return certBuffer.Bytes(), keyBuffer.Bytes(), nil
263 func ipsToStrings(ips []net.IP) []string {
264 ss := make([]string, 0, len(ips))
265 for _, ip := range ips {
266 ss = append(ss, ip.String())