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 }