knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/certificates/resources/certs.go (about) 1 /* 2 Copyright 2019 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package resources 18 19 import ( 20 "context" 21 "crypto/ecdsa" 22 "crypto/elliptic" 23 "crypto/rand" 24 "crypto/x509" 25 "crypto/x509/pkix" 26 "encoding/pem" 27 "errors" 28 "math/big" 29 "time" 30 31 "go.uber.org/zap" 32 33 "knative.dev/pkg/logging" 34 "knative.dev/pkg/network" 35 ) 36 37 const ( 38 organization = "knative.dev" 39 ) 40 41 // Create the common parts of the cert. These don't change between 42 // the root/CA cert and the server cert. 43 func createCertTemplate(name, namespace string, notAfter time.Time) (*x509.Certificate, error) { 44 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 45 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 46 if err != nil { 47 return nil, errors.New("failed to generate serial number: " + err.Error()) 48 } 49 50 serviceName := name + "." + namespace 51 commonName := serviceName + ".svc" 52 serviceHostname := network.GetServiceHostname(name, namespace) 53 serviceNames := []string{ 54 name, 55 serviceName, 56 commonName, 57 serviceHostname, 58 } 59 60 tmpl := x509.Certificate{ 61 SerialNumber: serialNumber, 62 Subject: pkix.Name{ 63 Organization: []string{organization}, 64 CommonName: commonName, 65 }, 66 SignatureAlgorithm: x509.ECDSAWithSHA256, 67 NotBefore: time.Now(), 68 NotAfter: notAfter, 69 BasicConstraintsValid: true, 70 DNSNames: serviceNames, 71 } 72 return &tmpl, nil 73 } 74 75 // Create cert template suitable for CA and hence signing 76 func createCACertTemplate(name, namespace string, notAfter time.Time) (*x509.Certificate, error) { 77 rootCert, err := createCertTemplate(name, namespace, notAfter) 78 if err != nil { 79 return nil, err 80 } 81 // Make it into a CA cert and change it so we can use it to sign certs 82 rootCert.IsCA = true 83 rootCert.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature 84 rootCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} 85 return rootCert, nil 86 } 87 88 // Create cert template that we can use on the server for TLS 89 func createServerCertTemplate(name, namespace string, notAfter time.Time) (*x509.Certificate, error) { 90 serverCert, err := createCertTemplate(name, namespace, notAfter) 91 if err != nil { 92 return nil, err 93 } 94 serverCert.KeyUsage = x509.KeyUsageDigitalSignature 95 serverCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} 96 return serverCert, err 97 } 98 99 // Actually sign the cert and return things in a form that we can use later on 100 func createCert(template, parent *x509.Certificate, pub, parentPriv interface{}) ( 101 cert *x509.Certificate, certPEM []byte, err error, 102 ) { 103 certDER, err := x509.CreateCertificate(rand.Reader, template, parent, pub, parentPriv) 104 if err != nil { 105 return cert, certPEM, err 106 } 107 cert, err = x509.ParseCertificate(certDER) 108 if err != nil { 109 return cert, certPEM, err 110 } 111 b := pem.Block{Type: "CERTIFICATE", Bytes: certDER} 112 certPEM = pem.EncodeToMemory(&b) 113 return cert, certPEM, err 114 } 115 116 func createCA(ctx context.Context, name, namespace string, notAfter time.Time) (*ecdsa.PrivateKey, *x509.Certificate, []byte, error) { 117 logger := logging.FromContext(ctx) 118 privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 119 if err != nil { 120 logger.Errorw("error generating random key", zap.Error(err)) 121 return nil, nil, nil, err 122 } 123 publicKey := privateKey.Public() 124 125 rootCertTmpl, err := createCACertTemplate(name, namespace, notAfter) 126 if err != nil { 127 logger.Errorw("error generating CA cert", zap.Error(err)) 128 return nil, nil, nil, err 129 } 130 131 rootCert, rootCertPEM, err := createCert(rootCertTmpl, rootCertTmpl, publicKey, privateKey) 132 if err != nil { 133 logger.Errorw("error signing the CA cert", zap.Error(err)) 134 return nil, nil, nil, err 135 } 136 return privateKey, rootCert, rootCertPEM, nil 137 } 138 139 // CreateCerts creates and returns a CA certificate and certificate and 140 // key for the server. serverKey and serverCert are used by the server 141 // to establish trust for clients, CA certificate is used by the 142 // client to verify the server authentication chain. notAfter specifies 143 // the expiration date. 144 func CreateCerts(ctx context.Context, name, namespace string, notAfter time.Time) (serverKey, serverCert, caCert []byte, err error) { 145 logger := logging.FromContext(ctx) 146 // First create a CA certificate and private key 147 caKey, caCertificate, caCertificatePEM, err := createCA(ctx, name, namespace, notAfter) 148 if err != nil { 149 return nil, nil, nil, err 150 } 151 152 // Then create the private key for the serving cert 153 privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 154 if err != nil { 155 logger.Errorw("error generating random key", zap.Error(err)) 156 return nil, nil, nil, err 157 } 158 publicKey := privateKey.Public() 159 160 servCertTemplate, err := createServerCertTemplate(name, namespace, notAfter) 161 if err != nil { 162 logger.Errorw("failed to create the server certificate template", zap.Error(err)) 163 return nil, nil, nil, err 164 } 165 166 // create a certificate which wraps the server's public key, sign it with the CA private key 167 _, servCertPEM, err := createCert(servCertTemplate, caCertificate, publicKey, caKey) 168 if err != nil { 169 logger.Errorw("error signing server certificate template", zap.Error(err)) 170 return nil, nil, nil, err 171 } 172 privKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) 173 if err != nil { 174 logger.Errorw("error marshaling private key", zap.Error(err)) 175 return nil, nil, nil, err 176 } 177 servKeyPEM := pem.EncodeToMemory(&pem.Block{ 178 Type: "PRIVATE KEY", Bytes: privKeyBytes, 179 }) 180 return servKeyPEM, servCertPEM, caCertificatePEM, nil 181 }