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