func newCertificateAuthority() (*x509.Certificate, *rsa.PrivateKey, error) {
	key, err := certs.NewPrivateKey()
	if err != nil {
		return nil, nil, err
	}
	c, err := newSelfSignedCACert(key)
	if err != nil {
		return nil, nil, err
	}
	return c, key, nil
}
newCertificateAuthority creates new certificate and private key for the certificate authority
package secret
import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/hex"
	"math/big"
	"path/filepath"
	"strings"
	"time"
	"github.com/pkg/errors"
	bootstrapv1 "github.com/zawachte-msft/cluster-api-k3s/bootstrap/api/v1alpha3"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/cert"
	clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
	"sigs.k8s.io/cluster-api/util/certs"
	"sigs.k8s.io/controller-runtime/pkg/client"
)
const (
	rootOwnerValue = "root:root"
	DefaultCertificatesDir = "/var/lib/rancher/k3s/server/tls"
)
var (
	ErrMissingCertificate = errors.New("missing certificate")
	ErrMissingCrt = errors.New("missing crt data")
	ErrMissingKey = errors.New("missing key data")
)
type Certificates []*Certificate
func NewCertificatesForInitialControlPlane() Certificates {
	certificatesDir := DefaultCertificatesDir
	certificates := Certificates{
		&Certificate{
			Purpose:  ClusterCA,
			CertFile: filepath.Join(certificatesDir, "server-ca.crt"),
			KeyFile:  filepath.Join(certificatesDir, "server-ca.key"),
		},
		&Certificate{
			Purpose:  ClientClusterCA,
			CertFile: filepath.Join(certificatesDir, "client-ca.crt"),
			KeyFile:  filepath.Join(certificatesDir, "client-ca.key"),
		},
	}
	return certificates
}
func (c Certificates) GetByPurpose(purpose Purpose) *Certificate {
	for _, certificate := range c {
		if certificate.Purpose == purpose {
			return certificate
		}
	}
	return nil
}
func (c Certificates) Lookup(ctx context.Context, ctrlclient client.Client, clusterName client.ObjectKey) error {
	for _, certificate := range c {
		s := &corev1.Secret{}
		key := client.ObjectKey{
			Name:      Name(clusterName.Name, certificate.Purpose),
			Namespace: clusterName.Namespace,
		}
		if err := ctrlclient.Get(ctx, key, s); err != nil {
			if apierrors.IsNotFound(err) {
				if certificate.External {
					return errors.WithMessage(err, "external certificate not found")
				}
				continue
			}
			return errors.WithStack(err)
		}
		kp, err := secretToKeyPair(s)
		if err != nil {
			return err
		}
		certificate.KeyPair = kp
	}
	return nil
}
func (c Certificates) EnsureAllExist() error {
	for _, certificate := range c {
		if certificate.KeyPair == nil {
			return ErrMissingCertificate
		}
		if len(certificate.KeyPair.Cert) == 0 {
			return errors.Wrapf(ErrMissingCrt, "for certificate: %s", certificate.Purpose)
		}
		if !certificate.External {
			if len(certificate.KeyPair.Key) == 0 {
				return errors.Wrapf(ErrMissingKey, "for certificate: %s", certificate.Purpose)
			}
		}
	}
	return nil
}
func (c Certificates) Generate() error {
	for _, certificate := range c {
		if certificate.KeyPair == nil {
			err := certificate.Generate()
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func (c Certificates) SaveGenerated(ctx context.Context, ctrlclient client.Client, clusterName client.ObjectKey, owner metav1.OwnerReference) error {
	for _, certificate := range c {
		if !certificate.Generated {
			continue
		}
		s := certificate.AsSecret(clusterName, owner)
		if err := ctrlclient.Create(ctx, s); err != nil {
			return errors.WithStack(err)
		}
	}
	return nil
}
func (c Certificates) LookupOrGenerate(ctx context.Context, ctrlclient client.Client, clusterName client.ObjectKey, owner metav1.OwnerReference) error {
	if err := c.Lookup(ctx, ctrlclient, clusterName); err != nil {
		return err
	}
	if err := c.Generate(); err != nil {
		return err
	}
	if err := c.SaveGenerated(ctx, ctrlclient, clusterName, owner); err != nil {
		return err
	}
	return nil
}
type Certificate struct {
	Generated         bool
	External          bool
	Purpose           Purpose
	KeyPair           *certs.KeyPair
	CertFile, KeyFile string
}
func (c *Certificate) Hashes() ([]string, error) {
	certificates, err := cert.ParseCertsPEM(c.KeyPair.Cert)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to parse %s certificate", c.Purpose)
	}
	out := make([]string, 0)
	for _, c := range certificates {
		out = append(out, hashCert(c))
	}
	return out, nil
}
func hashCert(certificate *x509.Certificate) string {
	spkiHash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
	return "sha256:" + strings.ToLower(hex.EncodeToString(spkiHash[:]))
}
func (c *Certificate) AsSecret(clusterName client.ObjectKey, owner metav1.OwnerReference) *corev1.Secret {
	s := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: clusterName.Namespace,
			Name:      Name(clusterName.Name, c.Purpose),
			Labels: map[string]string{
				clusterv1.ClusterLabelName: clusterName.Name,
			},
		},
		Data: map[string][]byte{
			TLSKeyDataName: c.KeyPair.Key,
			TLSCrtDataName: c.KeyPair.Cert,
		},
		Type: clusterv1.ClusterSecretType,
	}
	if c.Generated {
		s.OwnerReferences = []metav1.OwnerReference{owner}
	}
	return s
}
func (c *Certificate) AsFiles() []bootstrapv1.File {
	out := make([]bootstrapv1.File, 0)
	if len(c.KeyPair.Cert) > 0 {
		out = append(out, bootstrapv1.File{
			Path:        c.CertFile,
			Owner:       rootOwnerValue,
			Permissions: "0640",
			Content:     string(c.KeyPair.Cert),
		})
	}
	if len(c.KeyPair.Key) > 0 {
		out = append(out, bootstrapv1.File{
			Path:        c.KeyFile,
			Owner:       rootOwnerValue,
			Permissions: "0600",
			Content:     string(c.KeyPair.Key),
		})
	}
	return out
}
func (c *Certificate) Generate() error {
	if c.Purpose == APIServerEtcdClient {
		return nil
	}
	generator := generateCACert
	if c.Purpose == ServiceAccount {
		generator = generateServiceAccountKeys
	}
	kp, err := generator()
	if err != nil {
		return err
	}
	c.KeyPair = kp
	c.Generated = true
	return nil
}
func (c Certificates) AsFiles() []bootstrapv1.File {
	clusterCA := c.GetByPurpose(ClusterCA)
	clientClusterCA := c.GetByPurpose(ClientClusterCA)
	etcdCA := c.GetByPurpose(EtcdCA)
	certFiles := make([]bootstrapv1.File, 0)
	if clusterCA != nil {
		certFiles = append(certFiles, clusterCA.AsFiles()...)
	}
	if clientClusterCA != nil {
		certFiles = append(certFiles, clientClusterCA.AsFiles()...)
	}
	if etcdCA != nil {
		certFiles = append(certFiles, etcdCA.AsFiles()...)
	}
	apiserverEtcdClientCert := c.GetByPurpose(APIServerEtcdClient)
	if apiserverEtcdClientCert != nil {
		certFiles = append(certFiles, apiserverEtcdClientCert.AsFiles()...)
	}
	return certFiles
}
func secretToKeyPair(s *corev1.Secret) (*certs.KeyPair, error) {
	c, exists := s.Data[TLSCrtDataName]
	if !exists {
		return nil, errors.Errorf("missing data for key %s", TLSCrtDataName)
	}
	key, exists := s.Data[TLSKeyDataName]
	if !exists {
		key = []byte("")
	}
	return &certs.KeyPair{
		Cert: c,
		Key:  key,
	}, nil
}
func generateCACert() (*certs.KeyPair, error) {
	x509Cert, privKey, err := newCertificateAuthority()
	if err != nil {
		return nil, err
	}
	return &certs.KeyPair{
		Cert: certs.EncodeCertPEM(x509Cert),
		Key:  certs.EncodePrivateKeyPEM(privKey),
	}, nil
}
func generateServiceAccountKeys() (*certs.KeyPair, error) {
	saCreds, err := certs.NewPrivateKey()
	if err != nil {
		return nil, err
	}
	saPub, err := certs.EncodePublicKeyPEM(&saCreds.PublicKey)
	if err != nil {
		return nil, err
	}
	return &certs.KeyPair{
		Cert: saPub,
		Key:  certs.EncodePrivateKeyPEM(saCreds),
	}, nil
}