mirror of https://github.com/kubernetes/kops.git
				
				
				
			
		
			
				
	
	
		
			160 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2020 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package pki
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"crypto"
 | |
| 	"crypto/x509"
 | |
| 	"crypto/x509/pkix"
 | |
| 	"fmt"
 | |
| 	"math/big"
 | |
| 	"net"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| var wellKnownCertificateTypes = map[string]string{
 | |
| 	"ca":           "CA,KeyUsageCRLSign,KeyUsageCertSign",
 | |
| 	"client":       "ExtKeyUsageClientAuth,KeyUsageDigitalSignature",
 | |
| 	"clientServer": "ExtKeyUsageClientAuth,ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
 | |
| 	"server":       "ExtKeyUsageServerAuth,KeyUsageDigitalSignature,KeyUsageKeyEncipherment",
 | |
| }
 | |
| 
 | |
| type IssueCertRequest struct {
 | |
| 	// Signer is the keypair to use to sign. Ignored if Type is "CA", in which case the cert will be self-signed.
 | |
| 	Signer string
 | |
| 	// Type is the type of certificate i.e. CA, server, client etc.
 | |
| 	Type string
 | |
| 	// Subject is the certificate subject.
 | |
| 	Subject pkix.Name
 | |
| 	// AlternateNames is a list of alternative names for this certificate.
 | |
| 	AlternateNames []string
 | |
| 
 | |
| 	// PublicKey is the public key for this certificate. If nil, it will be calculated from PrivateKey.
 | |
| 	PublicKey crypto.PublicKey
 | |
| 	// PrivateKey is the private key for this certificate. If both this and PublicKey are nil, a new private key will be generated.
 | |
| 	PrivateKey *PrivateKey
 | |
| 	// Validity is the certificate validity. The default is 10 years.
 | |
| 	Validity time.Duration
 | |
| 
 | |
| 	// Serial is the certificate serial number. If nil, a random number will be generated.
 | |
| 	Serial *big.Int
 | |
| }
 | |
| 
 | |
| type Keystore interface {
 | |
| 	// FindPrimaryKeypair finds a cert & private key, returning nil where either is not found
 | |
| 	// (if the certificate is found but not keypair, that is not an error: only the cert will be returned).
 | |
| 	// Also note that if the keypair is not found at all, this returns (nil, nil, nil)
 | |
| 	FindPrimaryKeypair(ctx context.Context, name string) (*Certificate, *PrivateKey, error)
 | |
| }
 | |
| 
 | |
| // IssueCert issues a certificate, either a self-signed CA or from a CA in a keystore.
 | |
| func IssueCert(ctx context.Context, request *IssueCertRequest, keystore Keystore) (issuedCertificate *Certificate, issuedKey *PrivateKey, caCertificate *Certificate, err error) {
 | |
| 	certificateType := request.Type
 | |
| 	if expanded, found := wellKnownCertificateTypes[certificateType]; found {
 | |
| 		certificateType = expanded
 | |
| 	}
 | |
| 
 | |
| 	template := &x509.Certificate{
 | |
| 		BasicConstraintsValid: true,
 | |
| 		IsCA:                  false,
 | |
| 		SerialNumber:          request.Serial,
 | |
| 	}
 | |
| 
 | |
| 	tokens := strings.Split(certificateType, ",")
 | |
| 	for _, t := range tokens {
 | |
| 		if strings.HasPrefix(t, "KeyUsage") {
 | |
| 			ku, found := parseKeyUsage(t)
 | |
| 			if !found {
 | |
| 				return nil, nil, nil, fmt.Errorf("unrecognized certificate option: %v", t)
 | |
| 			}
 | |
| 			template.KeyUsage |= ku
 | |
| 		} else if strings.HasPrefix(t, "ExtKeyUsage") {
 | |
| 			ku, found := parseExtKeyUsage(t)
 | |
| 			if !found {
 | |
| 				return nil, nil, nil, fmt.Errorf("unrecognized certificate option: %v", t)
 | |
| 			}
 | |
| 			template.ExtKeyUsage = append(template.ExtKeyUsage, ku)
 | |
| 		} else if t == "CA" {
 | |
| 			template.IsCA = true
 | |
| 		} else {
 | |
| 			return nil, nil, nil, fmt.Errorf("unrecognized certificate option: %q", t)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	template.Subject = request.Subject
 | |
| 
 | |
| 	var alternateNames []string
 | |
| 	alternateNames = append(alternateNames, request.AlternateNames...)
 | |
| 
 | |
| 	for _, san := range alternateNames {
 | |
| 		san = strings.TrimSpace(san)
 | |
| 		if san == "" {
 | |
| 			continue
 | |
| 		}
 | |
| 		if ip := net.ParseIP(san); ip != nil {
 | |
| 			template.IPAddresses = append(template.IPAddresses, ip)
 | |
| 		} else {
 | |
| 			template.DNSNames = append(template.DNSNames, san)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var caPrivateKey *PrivateKey
 | |
| 	var signer *x509.Certificate
 | |
| 	if !template.IsCA {
 | |
| 		var err error
 | |
| 		caCertificate, caPrivateKey, err = keystore.FindPrimaryKeypair(ctx, request.Signer)
 | |
| 		if err != nil {
 | |
| 			return nil, nil, nil, err
 | |
| 		}
 | |
| 		if caPrivateKey == nil {
 | |
| 			return nil, nil, nil, fmt.Errorf("ca key for %q was not found; cannot issue certificates", request.Signer)
 | |
| 		}
 | |
| 		if caCertificate == nil {
 | |
| 			return nil, nil, nil, fmt.Errorf("ca certificate for %q was not found; cannot issue certificates", request.Signer)
 | |
| 		}
 | |
| 		signer = caCertificate.Certificate
 | |
| 	}
 | |
| 
 | |
| 	privateKey := request.PrivateKey
 | |
| 	if request.PublicKey != nil {
 | |
| 		template.PublicKey = request.PublicKey
 | |
| 	} else if privateKey == nil {
 | |
| 		var err error
 | |
| 		privateKey, err = GeneratePrivateKey()
 | |
| 		if err != nil {
 | |
| 			return nil, nil, nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if request.Validity != 0 {
 | |
| 		template.NotAfter = time.Now().Add(request.Validity).UTC()
 | |
| 	}
 | |
| 
 | |
| 	certificate, err := signNewCertificate(privateKey, template, signer, caPrivateKey)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, nil, err
 | |
| 	}
 | |
| 
 | |
| 	if signer == nil {
 | |
| 		caCertificate = certificate
 | |
| 	}
 | |
| 
 | |
| 	return certificate, privateKey, caCertificate, err
 | |
| }
 |