...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/chaosctl/physicalmachine/pki.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/chaosctl/physicalmachine

     1  // Copyright 2021 Chaos Mesh 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  
    16  package physicalmachine
    17  
    18  import (
    19  	"crypto"
    20  	"crypto/ecdsa"
    21  	"crypto/elliptic"
    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  	"path/filepath"
    31  	"time"
    32  
    33  	"github.com/pkg/errors"
    34  	certutil "k8s.io/client-go/util/cert"
    35  	"k8s.io/client-go/util/keyutil"
    36  )
    37  
    38  const (
    39  	rsaKeySize    = 2048
    40  	ChaosdPkiName = "chaosd"
    41  	// CertificateBlockType is a possible value for pem.Block.Type.
    42  	CertificateBlockType = "CERTIFICATE"
    43  	// CertificateValidity defines the validity for all the signed certificates generated by kubeadm
    44  	CertificateValidity = time.Hour * 24 * 1825
    45  )
    46  
    47  func ParseCertAndKey(certData, keyData []byte) (*x509.Certificate, crypto.Signer, error) {
    48  	caCert, err := ParseCert(certData)
    49  	if err != nil {
    50  		return nil, nil, errors.Wrap(err, "parse certs pem failed")
    51  	}
    52  
    53  	caKey, err := ParsePrivateKey(keyData)
    54  	if err != nil {
    55  		return nil, nil, errors.Wrap(err, "parse ca key file failed")
    56  	}
    57  	return caCert, caKey, nil
    58  }
    59  
    60  func ParsePrivateKey(data []byte) (crypto.Signer, error) {
    61  	privKey, err := keyutil.ParsePrivateKeyPEM(data)
    62  	if err != nil {
    63  		return nil, errors.Errorf("error reading private key file: %v", err)
    64  	}
    65  
    66  	var key crypto.Signer
    67  	// Allow RSA and ECDSA formats only
    68  	switch k := privKey.(type) {
    69  	case *rsa.PrivateKey:
    70  		key = k
    71  	case *ecdsa.PrivateKey:
    72  		key = k
    73  	default:
    74  		return nil, errors.New("the private key file is neither in RSA nor ECDSA format")
    75  	}
    76  	return key, nil
    77  }
    78  
    79  func ParseCert(data []byte) (*x509.Certificate, error) {
    80  	caCerts, err := certutil.ParseCertsPEM(data)
    81  	if err != nil {
    82  		return nil, errors.Wrap(err, "parse certs pem failed")
    83  	}
    84  	return caCerts[0], nil
    85  }
    86  
    87  // NewCertAndKey creates new certificate and key by passing the certificate authority certificate and key
    88  func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, crypto.Signer, error) {
    89  	key, err := NewPrivateKey(x509.RSA)
    90  	if err != nil {
    91  		return nil, nil, errors.Wrap(err, "unable to create private key")
    92  	}
    93  
    94  	cert, err := NewSignedCert(key, caCert, caKey, false)
    95  	if err != nil {
    96  		return nil, nil, errors.Wrap(err, "unable to sign certificate")
    97  	}
    98  
    99  	return cert, key, nil
   100  }
   101  
   102  func NewPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) {
   103  	if keyType == x509.ECDSA {
   104  		return ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
   105  	}
   106  
   107  	return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
   108  }
   109  
   110  // NewSignedCert creates a signed certificate using the given CA certificate and key
   111  func NewSignedCert(key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer, isCA bool) (*x509.Certificate, error) {
   112  	serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	keyUsage := x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
   118  	if isCA {
   119  		keyUsage |= x509.KeyUsageCertSign
   120  	}
   121  
   122  	notAfter := time.Now().Add(CertificateValidity).UTC()
   123  
   124  	certTmpl := x509.Certificate{
   125  		Subject: pkix.Name{
   126  			CommonName: "chaosd.chaos-mesh.org",
   127  		},
   128  		DNSNames:              []string{"chaosd.chaos-mesh.org", "localhost"},
   129  		SerialNumber:          serial,
   130  		NotBefore:             caCert.NotBefore,
   131  		NotAfter:              notAfter,
   132  		KeyUsage:              keyUsage,
   133  		BasicConstraintsValid: true,
   134  		IsCA:                  isCA,
   135  	}
   136  	certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	return x509.ParseCertificate(certDERBytes)
   141  }
   142  
   143  // WriteCertAndKey stores certificate and key at the specified location
   144  func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key crypto.Signer) error {
   145  	if err := WriteKey(pkiPath, name, key); err != nil {
   146  		return errors.Wrap(err, "couldn't write key")
   147  	}
   148  
   149  	return WriteCert(pkiPath, name, cert)
   150  }
   151  
   152  // WriteCert stores the given certificate at the given location
   153  func WriteCert(pkiPath, name string, cert *x509.Certificate) error {
   154  	if cert == nil {
   155  		return errors.New("certificate cannot be nil when writing to file")
   156  	}
   157  
   158  	certificatePath := pathForCert(pkiPath, name)
   159  	if err := certutil.WriteCert(certificatePath, EncodeCertPEM(cert)); err != nil {
   160  		return errors.Wrapf(err, "unable to write certificate to file %s", certificatePath)
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  // WriteKey stores the given key at the given location
   167  func WriteKey(pkiPath, name string, key crypto.Signer) error {
   168  	if key == nil {
   169  		return errors.New("private key cannot be nil when writing to file")
   170  	}
   171  
   172  	privateKeyPath := pathForKey(pkiPath, name)
   173  	encoded, err := keyutil.MarshalPrivateKeyToPEM(key)
   174  	if err != nil {
   175  		return errors.Wrapf(err, "unable to marshal private key to PEM")
   176  	}
   177  	if err := keyutil.WriteKey(privateKeyPath, encoded); err != nil {
   178  		return errors.Wrapf(err, "unable to write private key to file %s", privateKeyPath)
   179  	}
   180  
   181  	return nil
   182  }
   183  
   184  // EncodeCertPEM returns PEM-endcoded certificate data
   185  func EncodeCertPEM(cert *x509.Certificate) []byte {
   186  	block := pem.Block{
   187  		Type:  CertificateBlockType,
   188  		Bytes: cert.Raw,
   189  	}
   190  	return pem.EncodeToMemory(&block)
   191  }
   192  
   193  func pathForCert(pkiPath, name string) string {
   194  	return filepath.Join(pkiPath, fmt.Sprintf("%s.crt", name))
   195  }
   196  
   197  func pathForKey(pkiPath, name string) string {
   198  	return filepath.Join(pkiPath, fmt.Sprintf("%s.key", name))
   199  }
   200