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 }