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  }