github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/certs/certs.go (about)

     1  package certs
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"crypto/rand"
     7  	"crypto/sha256"
     8  	"crypto/x509"
     9  	"crypto/x509/pkix"
    10  	"encoding/hex"
    11  	"encoding/pem"
    12  	"fmt"
    13  	"math"
    14  	"math/big"
    15  	"time"
    16  )
    17  
    18  type CertGenerator interface {
    19  	Generate(notAfter time.Time, organization string, ca *KeyPair, hosts []string) (*KeyPair, error)
    20  }
    21  
    22  type CertGeneratorFunc func(notAfter time.Time, organization string, ca *KeyPair, hosts []string) (*KeyPair, error)
    23  
    24  func (f CertGeneratorFunc) Generate(notAfter time.Time, organization string, ca *KeyPair, hosts []string) (*KeyPair, error) {
    25  	return f(notAfter, organization, ca, hosts)
    26  }
    27  
    28  var _ CertGenerator = CertGeneratorFunc(CreateSignedServingPair)
    29  
    30  // KeyPair stores an x509 certificate and its ECDSA private key
    31  type KeyPair struct {
    32  	Cert *x509.Certificate
    33  	Priv *ecdsa.PrivateKey
    34  }
    35  
    36  // ToPEM returns the PEM encoded cert pair
    37  func (kp *KeyPair) ToPEM() (certPEM []byte, privPEM []byte, err error) {
    38  	// PEM encode private key
    39  	privDER, err := x509.MarshalECPrivateKey(kp.Priv)
    40  	if err != nil {
    41  		return
    42  	}
    43  	privBlock := &pem.Block{
    44  		Type:  "EC PRIVATE KEY",
    45  		Bytes: privDER,
    46  	}
    47  	privPEM = pem.EncodeToMemory(privBlock)
    48  
    49  	// PEM encode cert
    50  	certBlock := &pem.Block{
    51  		Type:  "CERTIFICATE",
    52  		Bytes: kp.Cert.Raw,
    53  	}
    54  	certPEM = pem.EncodeToMemory(certBlock)
    55  
    56  	return
    57  }
    58  
    59  // GenerateCA generates a self-signed CA cert/key pair that expires in expiresIn days
    60  func GenerateCA(notAfter time.Time, organization string) (*KeyPair, error) {
    61  	notBefore := time.Now()
    62  	if notAfter.Before(notBefore) {
    63  		return nil, fmt.Errorf("invalid notAfter: %s before %s", notAfter.String(), notBefore.String())
    64  	}
    65  
    66  	serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	caDetails := &x509.Certificate{
    72  		SerialNumber: serial,
    73  		Subject: pkix.Name{
    74  			CommonName:   fmt.Sprintf("olm-selfsigned-%x", serial),
    75  			Organization: []string{organization},
    76  		},
    77  		NotBefore:             notBefore,
    78  		NotAfter:              notAfter,
    79  		IsCA:                  true,
    80  		KeyUsage:              x509.KeyUsageCertSign,
    81  		BasicConstraintsValid: true,
    82  	}
    83  
    84  	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	publicKey := &privateKey.PublicKey
    90  	certRaw, err := x509.CreateCertificate(rand.Reader, caDetails, caDetails, publicKey, privateKey)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	cert, err := x509.ParseCertificate(certRaw)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	ca := &KeyPair{
   101  		Cert: cert,
   102  		Priv: privateKey,
   103  	}
   104  
   105  	return ca, nil
   106  }
   107  
   108  // CreateSignedServingPair creates a serving cert/key pair signed by the given ca
   109  func CreateSignedServingPair(notAfter time.Time, organization string, ca *KeyPair, hosts []string) (*KeyPair, error) {
   110  	notBefore := time.Now()
   111  	if notAfter.Before(notBefore) {
   112  		return nil, fmt.Errorf("invalid notAfter: %s before %s", notAfter.String(), notBefore.String())
   113  	}
   114  
   115  	serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	certDetails := &x509.Certificate{
   121  		SerialNumber: serial,
   122  		Subject: pkix.Name{
   123  			CommonName:   hosts[0],
   124  			Organization: []string{organization},
   125  		},
   126  		NotBefore:             notBefore,
   127  		NotAfter:              notAfter,
   128  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   129  		BasicConstraintsValid: true,
   130  		DNSNames:              hosts,
   131  	}
   132  
   133  	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	publicKey := &privateKey.PublicKey
   139  	certRaw, err := x509.CreateCertificate(rand.Reader, certDetails, ca.Cert, publicKey, ca.Priv)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	cert, err := x509.ParseCertificate(certRaw)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	servingCert := &KeyPair{
   150  		Cert: cert,
   151  		Priv: privateKey,
   152  	}
   153  
   154  	return servingCert, nil
   155  }
   156  
   157  // PEMToCert converts the PEM block of the given byte array to an x509 certificate
   158  func PEMToCert(certPEM []byte) (*x509.Certificate, error) {
   159  	block, _ := pem.Decode(certPEM)
   160  	if block == nil {
   161  		return nil, fmt.Errorf("cert PEM empty")
   162  	}
   163  
   164  	cert, err := x509.ParseCertificate(block.Bytes)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	return cert, nil
   170  }
   171  
   172  // VerifyCert checks that the given cert is signed and trusted by the given CA
   173  func VerifyCert(ca, cert *x509.Certificate, host string) error {
   174  	roots := x509.NewCertPool()
   175  	roots.AddCert(ca)
   176  
   177  	opts := x509.VerifyOptions{
   178  		DNSName: host,
   179  		Roots:   roots,
   180  	}
   181  
   182  	if _, err := cert.Verify(opts); err != nil {
   183  		return err
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  // Active checks if the given cert is within its valid time window
   190  func Active(cert *x509.Certificate) bool {
   191  	now := time.Now()
   192  	active := now.After(cert.NotBefore) && now.Before(cert.NotAfter)
   193  	return active
   194  }
   195  
   196  // PEMHash returns a hash of the given PEM encoded cert
   197  type PEMHash func(certPEM []byte) (hash string)
   198  
   199  // PEMSHA256 returns the hex encoded SHA 256 hash of the given PEM encoded cert
   200  func PEMSHA256(certPEM []byte) (hash string) {
   201  	hasher := sha256.New()
   202  	hasher.Write(certPEM)
   203  	hash = hex.EncodeToString(hasher.Sum(nil))
   204  	return
   205  }