k8s.io/client-go@v0.22.2/util/cert/cert.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     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
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    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.
    15  */
    16  
    17  package cert
    18  
    19  import (
    20  	"bytes"
    21  	"crypto"
    22  	cryptorand "crypto/rand"
    23  	"crypto/rsa"
    24  	"crypto/x509"
    25  	"crypto/x509/pkix"
    26  	"encoding/pem"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"math/big"
    30  	"net"
    31  	"path/filepath"
    32  	"strings"
    33  	"time"
    34  
    35  	"k8s.io/client-go/util/keyutil"
    36  )
    37  
    38  const duration365d = time.Hour * 24 * 365
    39  
    40  // Config contains the basic fields required for creating a certificate
    41  type Config struct {
    42  	CommonName   string
    43  	Organization []string
    44  	AltNames     AltNames
    45  	Usages       []x509.ExtKeyUsage
    46  }
    47  
    48  // AltNames contains the domain names and IP addresses that will be added
    49  // to the API Server's x509 certificate SubAltNames field. The values will
    50  // be passed directly to the x509.Certificate object.
    51  type AltNames struct {
    52  	DNSNames []string
    53  	IPs      []net.IP
    54  }
    55  
    56  // NewSelfSignedCACert creates a CA certificate
    57  func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) {
    58  	now := time.Now()
    59  	tmpl := x509.Certificate{
    60  		SerialNumber: new(big.Int).SetInt64(0),
    61  		Subject: pkix.Name{
    62  			CommonName:   cfg.CommonName,
    63  			Organization: cfg.Organization,
    64  		},
    65  		DNSNames:              []string{cfg.CommonName},
    66  		NotBefore:             now.UTC(),
    67  		NotAfter:              now.Add(duration365d * 10).UTC(),
    68  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    69  		BasicConstraintsValid: true,
    70  		IsCA:                  true,
    71  	}
    72  
    73  	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return x509.ParseCertificate(certDERBytes)
    78  }
    79  
    80  // GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
    81  // Host may be an IP or a DNS name
    82  // You may also specify additional subject alt names (either ip or dns names) for the certificate.
    83  func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
    84  	return GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, "")
    85  }
    86  
    87  // GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
    88  // Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
    89  // for the certificate.
    90  //
    91  // If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
    92  // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
    93  // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
    94  // Certs/keys not existing in that directory are created.
    95  func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
    96  	validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
    97  	maxAge := time.Hour * 24 * 365          // one year self-signed certs
    98  
    99  	baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
   100  	certFixturePath := filepath.Join(fixtureDirectory, baseName+".crt")
   101  	keyFixturePath := filepath.Join(fixtureDirectory, baseName+".key")
   102  	if len(fixtureDirectory) > 0 {
   103  		cert, err := ioutil.ReadFile(certFixturePath)
   104  		if err == nil {
   105  			key, err := ioutil.ReadFile(keyFixturePath)
   106  			if err == nil {
   107  				return cert, key, nil
   108  			}
   109  			return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
   110  		}
   111  		maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
   112  	}
   113  
   114  	caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
   115  	if err != nil {
   116  		return nil, nil, err
   117  	}
   118  
   119  	caTemplate := x509.Certificate{
   120  		SerialNumber: big.NewInt(1),
   121  		Subject: pkix.Name{
   122  			CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
   123  		},
   124  		NotBefore: validFrom,
   125  		NotAfter:  validFrom.Add(maxAge),
   126  
   127  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
   128  		BasicConstraintsValid: true,
   129  		IsCA:                  true,
   130  	}
   131  
   132  	caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
   133  	if err != nil {
   134  		return nil, nil, err
   135  	}
   136  
   137  	caCertificate, err := x509.ParseCertificate(caDERBytes)
   138  	if err != nil {
   139  		return nil, nil, err
   140  	}
   141  
   142  	priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
   143  	if err != nil {
   144  		return nil, nil, err
   145  	}
   146  
   147  	template := x509.Certificate{
   148  		SerialNumber: big.NewInt(2),
   149  		Subject: pkix.Name{
   150  			CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
   151  		},
   152  		NotBefore: validFrom,
   153  		NotAfter:  validFrom.Add(maxAge),
   154  
   155  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   156  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   157  		BasicConstraintsValid: true,
   158  	}
   159  
   160  	if ip := net.ParseIP(host); ip != nil {
   161  		template.IPAddresses = append(template.IPAddresses, ip)
   162  	} else {
   163  		template.DNSNames = append(template.DNSNames, host)
   164  	}
   165  
   166  	template.IPAddresses = append(template.IPAddresses, alternateIPs...)
   167  	template.DNSNames = append(template.DNSNames, alternateDNS...)
   168  
   169  	derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
   170  	if err != nil {
   171  		return nil, nil, err
   172  	}
   173  
   174  	// Generate cert, followed by ca
   175  	certBuffer := bytes.Buffer{}
   176  	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
   177  		return nil, nil, err
   178  	}
   179  	if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
   180  		return nil, nil, err
   181  	}
   182  
   183  	// Generate key
   184  	keyBuffer := bytes.Buffer{}
   185  	if err := pem.Encode(&keyBuffer, &pem.Block{Type: keyutil.RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
   186  		return nil, nil, err
   187  	}
   188  
   189  	if len(fixtureDirectory) > 0 {
   190  		if err := ioutil.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
   191  			return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
   192  		}
   193  		if err := ioutil.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0644); err != nil {
   194  			return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
   195  		}
   196  	}
   197  
   198  	return certBuffer.Bytes(), keyBuffer.Bytes(), nil
   199  }
   200  
   201  func ipsToStrings(ips []net.IP) []string {
   202  	ss := make([]string, 0, len(ips))
   203  	for _, ip := range ips {
   204  		ss = append(ss, ip.String())
   205  	}
   206  	return ss
   207  }