func (cm *CertificateManager) GenerateServer() error {
	serverCertRequest := ServerCertificateRequest{
		Subject: pkix.Name{
			Organization: []string{"Banzai Cloud"},
			CommonName:   "Banzai Cloud Generated Server Cert",
		},
		Validity:  cm.validityDuration,
		notBefore: cm.notBefore,
	}
	if len(cm.sHosts.WildCardHosts) != 0 {
		serverCertRequest.Subject.CommonName = cm.sHosts.WildCardHosts[0]
		serverCertRequest.DNSNames = append(serverCertRequest.DNSNames, cm.sHosts.WildCardHosts...)
	}
	serverCertRequest.IPAddresses = append(serverCertRequest.IPAddresses, cm.sHosts.IPs...)
	serverCertRequest.DNSNames = append(serverCertRequest.DNSNames, cm.sHosts.Hosts...)
	serverCert, err := GenerateServerCertificate(serverCertRequest, cm.caCertTemplate, cm.caKey)
	if err != nil {
		return err
	}
	cm.Chain.ServerKey = string(serverCert.Key)
	cm.Chain.ServerCert = string(serverCert.Certificate)
	return nil
}
GenerateServer will generate a new server TLS certificate signed by the CA within the chain
package tls
import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"strings"
	"time"
	"emperror.dev/errors"
)
var serialNumberLimit *big.Int
func init() {
	serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
}
const (
	defaultValidity = 365 * 24 * time.Hour
	defaultKeyBits  = 2048
)
var (
	ErrInvalidHostName = errors.New("invalid host name, this has been already covered by the wildcard")
	ErrInvalidCA = errors.New("the CA provided is not valid")
	ErrEmptyCA = errors.New("an empty CA was provided")
	ErrExpiredCA = errors.New("the CA provided will expired before the provided threshold")
)
type CertificateManager struct {
	caCertTemplate *x509.Certificate
	caKey          *rsa.PrivateKey
	sHosts *SeparatedCertHosts
	notBefore        time.Time
	validityDuration time.Duration
	notAfter         time.Time
	Chain *CertificateChain
}
func NewCertificateManager(hosts string, validity string) (*CertificateManager, error) {
	cm := CertificateManager{
		Chain: &CertificateChain{},
	}
	var err error
	cm.notBefore, cm.validityDuration, cm.notAfter, err = getTimes(validity)
	if err != nil {
		return nil, err
	}
	cm.sHosts = NewSeparatedCertHosts(hosts)
	err = cm.sHosts.Validate()
	if err != nil {
		return nil, err
	}
	return &cm, nil
}
type CertificateChain struct {
	CAKey      string `mapstructure:"caKey"`
	CACert     string `mapstructure:"caCert"`
	ServerKey  string `mapstructure:"serverKey"`
	ServerCert string `mapstructure:"serverCert"`
	ClientKey  string `mapstructure:"clientKey"`
	ClientCert string `mapstructure:"clientCert"`
	PeerKey    string `mapstructure:"peerKey"`
	PeerCert   string `mapstructure:"peerCert"`
}
type SeparatedCertHosts struct {
	WildCardHosts []string
	Hosts         []string
	IPs           []net.IP
}
func NewSeparatedCertHosts(hosts string) *SeparatedCertHosts {
	var sHosts SeparatedCertHosts
	for _, h := range strings.Split(hosts, ",") {
		if ip := net.ParseIP(h); ip != nil {
			sHosts.IPs = append(sHosts.IPs, ip)
		} else {
			if strings.HasPrefix(h, "*.") {
				sHosts.WildCardHosts = append(sHosts.WildCardHosts, h)
			} else {
				sHosts.Hosts = append(sHosts.Hosts, h)
			}
		}
	}
	return &sHosts
}
func (sh *SeparatedCertHosts) Validate() error {
	if len(sh.WildCardHosts) == 0 {
		return nil
	}
	for _, wildCardHost := range sh.WildCardHosts {
		hostWithoutWildCard := strings.ReplaceAll(wildCardHost, "*", "")
		for _, host := range sh.Hosts {
			if strings.Contains(host, hostWithoutWildCard) {
				if !strings.Contains(strings.ReplaceAll(host, hostWithoutWildCard, ""), ".") {
					return errors.WithStack(ErrInvalidHostName)
				}
			}
		}
	}
	return nil
}
func GetCertExpirationDate(certPEM []byte) (time.Time, error) {
	cert, err := PEMToCertificate(certPEM)
	if err != nil {
		return time.Time{}, err
	}
	return cert.NotAfter, nil
}
func PEMToCertificate(certPEM []byte) (*x509.Certificate, error) {
	block, _ := pem.Decode(certPEM)
	if block == nil {
		return nil, errors.Errorf("failed to parse certificate PEM")
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse certificate")
	}
	return cert, nil
}
func getTimes(validity string) (notBefore time.Time, validityDuration time.Duration, notAfter time.Time, err error) {
	notBefore = time.Now()
	validityDuration, err = time.ParseDuration(validity)
	if err != nil {
		err = errors.WithStack(err)
		return
	}
	notAfter = notBefore.Add(validityDuration)
	return
}
func (cm *CertificateManager) NewChain() error {
	err := cm.GenerateCA()
	if err != nil {
		return errors.Wrap(err, "error generating new certificate authority")
	}
	err = cm.GenerateServer()
	if err != nil {
		return errors.Wrap(err, "error generating new server certificate")
	}
	err = cm.GenerateClient()
	if err != nil {
		return errors.Wrap(err, "error generating new server certificate")
	}
	err = cm.GeneratePeer()
	if err != nil {
		return errors.Wrap(err, "error generating new server certificate")
	}
	return nil
}
func keyToBytes(key *rsa.PrivateKey) ([]byte, error) {
	keyBytes := x509.MarshalPKCS1PrivateKey(key)
	var buf bytes.Buffer
	if err := pem.Encode(&buf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes}); err != nil {
		return nil, errors.Wrap(err, "failed to PEM encode private key")
	}
	return buf.Bytes(), nil
}
func certToBytes(certBytes []byte) ([]byte, error) {
	var buf bytes.Buffer
	if err := pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}); err != nil {
		return nil, errors.Wrap(err, "failed to PEM encode certificate")
	}
	return buf.Bytes(), nil
}
func (cm *CertificateManager) LoadCA(caCertBytes []byte, caKeyBytes []byte, expirationThreshold time.Duration) error {
	if len(caCertBytes) == 0 || len(caKeyBytes) == 0 {
		return ErrEmptyCA
	}
	tlsExpiration, err := GetCertExpirationDate(caCertBytes)
	if err != nil {
		return errors.Wrap(err, "unable to get the CA expiration date")
	}
	if time.Until(tlsExpiration) < expirationThreshold {
		return ErrExpiredCA
	}
	caCertPem, _ := pem.Decode(caCertBytes)
	if caCertPem == nil {
		return errors.Wrap(ErrInvalidCA, "no PEM encoded CA certificate could be found")
	}
	caKeyPem, _ := pem.Decode(caKeyBytes)
	if caKeyPem == nil {
		return errors.Wrap(ErrInvalidCA, "no PEM encoded CA key could be found")
	}
	if caCertPem.Type != "CERTIFICATE" {
		return errors.Wrap(ErrInvalidCA, "the CA certificate was not of type CERTIFICATE")
	}
	if caKeyPem.Type != "RSA PRIVATE KEY" {
		return errors.Wrap(ErrInvalidCA, "the CA certificate was not of type RSA PRIVATE KEY")
	}
	caCert, err := x509.ParseCertificate(caCertPem.Bytes)
	if err != nil {
		return errors.Wrap(err, "the CA certificate was not not x509 parsable")
	}
	caKey, err := x509.ParsePKCS1PrivateKey(caKeyPem.Bytes)
	if err != nil {
		return errors.Wrap(err, "the CA key was not not PKCS1 parsable")
	}
	cm.caCertTemplate = caCert
	cm.caKey = caKey
	cm.Chain.CACert = string(caCertBytes)
	cm.Chain.CAKey = string(caKeyBytes)
	return nil
}
func (cm *CertificateManager) GenerateCA() error {
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return errors.Wrap(err, "failed to generate serial number")
	}
	key, err := rsa.GenerateKey(rand.Reader, defaultKeyBits)
	if err != nil {
		return errors.Wrap(err, "failed to generate rsa key")
	}
	keyBytes, err := keyToBytes(key)
	if err != nil {
		return err
	}
	caCertTemplate := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Banzai Cloud"},
			CommonName:   "Banzai Cloud Generated Root CA",
		},
		NotBefore:             cm.notBefore,
		NotAfter:              cm.notAfter,
		KeyUsage:              x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
		IsCA:                  true,
	}
	caCert, err := x509.CreateCertificate(rand.Reader, &caCertTemplate, &caCertTemplate, &key.PublicKey, key)
	if err != nil {
		return err
	}
	certBytes, err := certToBytes(caCert)
	if err != nil {
		return err
	}
	cm.caCertTemplate = &caCertTemplate
	cm.caKey = key
	cm.Chain.CACert = string(certBytes)
	cm.Chain.CAKey = string(keyBytes)
	return nil
}