github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/cert/cert.go (about) 1 // Copyright 2016 The Kubernetes Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cert 16 17 import ( 18 "crypto" 19 "crypto/ecdsa" 20 "crypto/elliptic" 21 "crypto/rand" 22 "crypto/rsa" 23 "crypto/x509" 24 "crypto/x509/pkix" 25 "encoding/pem" 26 "errors" 27 "fmt" 28 "math" 29 "math/big" 30 "net" 31 "os" 32 "path/filepath" 33 "time" 34 35 certutil "k8s.io/client-go/util/cert" 36 "k8s.io/client-go/util/keyutil" 37 ) 38 39 const ( 40 // PrivateKeyBlockType is a possible value for pem.Block.Type. 41 PrivateKeyBlockType = "PRIVATE KEY" 42 // PublicKeyBlockType is a possible value for pem.Block.Type. 43 PublicKeyBlockType = "PUBLIC KEY" 44 // CertificateBlockType is a possible value for pem.Block.Type. 45 CertificateBlockType = "CERTIFICATE" 46 // RSAPrivateKeyBlockType is a possible value for pem.Block.Type. 47 RSAPrivateKeyBlockType = "RSA PRIVATE KEY" 48 rsaKeySize = 2048 49 duration365d = time.Hour * 24 * 365 50 ) 51 52 // Config contains the basic fields required for creating a certificate 53 type Config struct { 54 Path string // Writeto Dir 55 DefaultPath string // Kubernetes default Dir 56 BaseName string // Writeto file name 57 CAName string // root ca map key 58 CommonName string 59 DNSNames []string 60 Organization []string 61 Year time.Duration 62 AltNames AltNames 63 Usages []x509.ExtKeyUsage 64 } 65 66 // AltNames contains the domain names and IP addresses that will be added 67 // to the API Server's x509 certificate SubAltNames field. The values will 68 // be passed directly to the x509.Certificate object. 69 type AltNames struct { 70 DNSNames map[string]string 71 IPs map[string]net.IP 72 } 73 74 // NewPrivateKey creates an RSA private key 75 func NewPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) { 76 if keyType == x509.ECDSA { 77 return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 78 } 79 80 return rsa.GenerateKey(rand.Reader, rsaKeySize) 81 } 82 83 // NewSelfSignedCACert creates a CA certificate 84 func NewSelfSignedCACert(key crypto.Signer, config Config) (*x509.Certificate, error) { 85 now := time.Now() 86 tmpl := x509.Certificate{ 87 SerialNumber: new(big.Int).SetInt64(0), 88 Subject: pkix.Name{ 89 CommonName: config.CommonName, 90 Organization: config.Organization, 91 }, 92 DNSNames: config.DNSNames, 93 NotBefore: now.UTC(), 94 NotAfter: now.Add(duration365d * config.Year).UTC(), 95 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, 96 BasicConstraintsValid: true, 97 IsCA: true, 98 } 99 100 certDERBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, key.Public(), key) 101 if err != nil { 102 return nil, err 103 } 104 return x509.ParseCertificate(certDERBytes) 105 } 106 107 // NewCaCertAndKey Create as ca. 108 func NewCaCertAndKey(cfg Config) (*x509.Certificate, crypto.Signer, error) { 109 _, err := os.Stat(pathForKey(cfg.Path, cfg.BaseName)) 110 if !os.IsNotExist(err) { 111 return LoadCaCertAndKeyFromDisk(cfg) 112 } 113 114 key, err := NewPrivateKey(x509.UnknownPublicKeyAlgorithm) 115 if err != nil { 116 return nil, nil, fmt.Errorf("unable to create private key while generating CA certificate %s", err) 117 } 118 cert, err := NewSelfSignedCACert(key, cfg) 119 if err != nil { 120 return nil, nil, fmt.Errorf("unable to create ca cert %s", err) 121 } 122 return cert, key, nil 123 } 124 125 // LoadCaCertAndKeyFromDisk load ca cert and key form disk. 126 func LoadCaCertAndKeyFromDisk(cfg Config) (*x509.Certificate, crypto.Signer, error) { 127 certs, err := certutil.CertsFromFile(pathForCert(cfg.Path, cfg.BaseName)) 128 if err != nil { 129 return nil, nil, err 130 } 131 caCert := certs[0] 132 133 cakey, err := TryLoadKeyFromDisk(pathForKey(cfg.Path, cfg.BaseName)) 134 if err != nil { 135 return nil, nil, err 136 } 137 return caCert, cakey, nil 138 } 139 140 // TryLoadKeyFromDisk tries to load the key from the disk and validates that it is valid 141 func TryLoadKeyFromDisk(pkiPath string) (crypto.Signer, error) { 142 // Parse the private key from a file 143 privKey, err := keyutil.PrivateKeyFromFile(pkiPath) 144 if err != nil { 145 return nil, fmt.Errorf("couldn't load the private key file %s", err) 146 } 147 148 // Allow RSA and ECDSA formats only 149 var key crypto.Signer 150 switch k := privKey.(type) { 151 case *rsa.PrivateKey: 152 key = k 153 case *ecdsa.PrivateKey: 154 key = k 155 default: 156 return nil, fmt.Errorf("couldn't convert the private key file %s", err) 157 } 158 159 return key, nil 160 } 161 162 // NewCaCertAndKeyFromRoot cmd/kubeadm/app/util/pkiutil/pki_helpers.go NewCertAndKey 163 func NewCaCertAndKeyFromRoot(cfg Config, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, crypto.Signer, error) { 164 key, err := NewPrivateKey(x509.UnknownPublicKeyAlgorithm) 165 if err != nil { 166 return nil, nil, fmt.Errorf("unable to create private key while generating CA certificate %s", err) 167 } 168 cert, err := NewSignedCert(cfg, key, caCert, caKey) 169 if err != nil { 170 return nil, nil, fmt.Errorf("new signed cert failed %s", err) 171 } 172 173 return cert, key, nil 174 } 175 176 // NewSignedCert creates a signed certificate using the given CA certificate and key 177 func NewSignedCert(cfg Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) { 178 serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) 179 if err != nil { 180 return nil, err 181 } 182 if len(cfg.CommonName) == 0 { 183 return nil, errors.New("must specify a CommonName") 184 } 185 if len(cfg.Usages) == 0 { 186 return nil, errors.New("must specify at least one ExtKeyUsage") 187 } 188 189 var dnsNames []string 190 var ips []net.IP 191 192 for _, v := range cfg.AltNames.DNSNames { 193 dnsNames = append(dnsNames, v) 194 } 195 for _, v := range cfg.AltNames.IPs { 196 ips = append(ips, v) 197 } 198 certTmpl := x509.Certificate{ 199 Subject: pkix.Name{ 200 CommonName: cfg.CommonName, 201 Organization: cfg.Organization, 202 }, 203 DNSNames: dnsNames, 204 IPAddresses: ips, 205 SerialNumber: serial, 206 NotBefore: caCert.NotBefore, 207 NotAfter: time.Now().Add(duration365d * cfg.Year).UTC(), 208 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 209 ExtKeyUsage: cfg.Usages, 210 } 211 certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, caCert, key.Public(), caKey) 212 if err != nil { 213 return nil, err 214 } 215 return x509.ParseCertificate(certDERBytes) 216 } 217 218 // WriteTofile 219 // WriteCertAndKey stores certificate and key at the specified location 220 func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key crypto.Signer) error { 221 if err := WriteKey(pkiPath, name, key); err != nil { 222 return err 223 } 224 225 return WriteCert(pkiPath, name, cert) 226 } 227 228 // WriteCert stores the given certificate at the given location 229 func WriteCert(pkiPath, name string, cert *x509.Certificate) error { 230 if cert == nil { 231 return errors.New("certificate cannot be nil when writing to file") 232 } 233 234 certificatePath := pathForCert(pkiPath, name) 235 if err := certutil.WriteCert(certificatePath, EncodeCertPEM(cert)); err != nil { 236 return fmt.Errorf("unable to write certificate to file %s %s", certificatePath, err) 237 } 238 239 return nil 240 } 241 242 // EncodeCertPEM returns PEM-endcoded certificate data 243 func EncodeCertPEM(cert *x509.Certificate) []byte { 244 block := pem.Block{ 245 Type: CertificateBlockType, 246 Bytes: cert.Raw, 247 } 248 return pem.EncodeToMemory(&block) 249 } 250 251 // WriteKey stores the given key at the given location 252 func WriteKey(pkiPath, name string, key crypto.Signer) error { 253 if key == nil { 254 return errors.New("private key cannot be nil when writing to file") 255 } 256 257 privateKeyPath := pathForKey(pkiPath, name) 258 encoded, err := keyutil.MarshalPrivateKeyToPEM(key) 259 if err != nil { 260 return fmt.Errorf("unable to marshal private key to PEM %s", err) 261 } 262 if err := keyutil.WriteKey(privateKeyPath, encoded); err != nil { 263 return fmt.Errorf("unable to write private key to file %s %s", privateKeyPath, err) 264 } 265 266 return nil 267 } 268 269 // WritePublicKey stores the given public key at the given location 270 func WritePublicKey(pkiPath, name string, key crypto.PublicKey) error { 271 if key == nil { 272 return errors.New("public key cannot be nil when writing to file") 273 } 274 275 publicKeyBytes, err := EncodePublicKeyPEM(key) 276 if err != nil { 277 return err 278 } 279 publicKeyPath := pathForPublicKey(pkiPath, name) 280 if err := keyutil.WriteKey(publicKeyPath, publicKeyBytes); err != nil { 281 return fmt.Errorf("unable to write public key to file %s %s", publicKeyPath, err) 282 } 283 284 return nil 285 } 286 287 func pathForPublicKey(pkiPath, name string) string { 288 return filepath.Join(pkiPath, fmt.Sprintf("%s.pub", name)) 289 } 290 291 // EncodePublicKeyPEM returns PEM-encoded public data 292 func EncodePublicKeyPEM(key crypto.PublicKey) ([]byte, error) { 293 der, err := x509.MarshalPKIXPublicKey(key) 294 if err != nil { 295 return []byte{}, err 296 } 297 block := pem.Block{ 298 Type: PublicKeyBlockType, 299 Bytes: der, 300 } 301 return pem.EncodeToMemory(&block), nil 302 } 303 304 func pathForCert(pkiPath, name string) string { 305 return filepath.Join(pkiPath, fmt.Sprintf("%s.crt", name)) 306 } 307 308 func pathForKey(pkiPath, name string) string { 309 return filepath.Join(pkiPath, fmt.Sprintf("%s.key", name)) 310 }