mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			216 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
 | 
						|
Package ocsp exposes OCSP signing functionality, much like the signer
 | 
						|
package does for certificate signing.  It also provies a basic OCSP
 | 
						|
responder stack for serving pre-signed OCSP responses.
 | 
						|
 | 
						|
*/
 | 
						|
package ocsp
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto"
 | 
						|
	"crypto/x509"
 | 
						|
	"crypto/x509/pkix"
 | 
						|
	"io/ioutil"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	cferr "github.com/cloudflare/cfssl/errors"
 | 
						|
	"github.com/cloudflare/cfssl/helpers"
 | 
						|
	"github.com/cloudflare/cfssl/log"
 | 
						|
	"golang.org/x/crypto/ocsp"
 | 
						|
)
 | 
						|
 | 
						|
// revocationReasonCodes is a map between string reason codes
 | 
						|
// to integers as defined in RFC 5280
 | 
						|
var revocationReasonCodes = map[string]int{
 | 
						|
	"unspecified":          ocsp.Unspecified,
 | 
						|
	"keycompromise":        ocsp.KeyCompromise,
 | 
						|
	"cacompromise":         ocsp.CACompromise,
 | 
						|
	"affiliationchanged":   ocsp.AffiliationChanged,
 | 
						|
	"superseded":           ocsp.Superseded,
 | 
						|
	"cessationofoperation": ocsp.CessationOfOperation,
 | 
						|
	"certificatehold":      ocsp.CertificateHold,
 | 
						|
	"removefromcrl":        ocsp.RemoveFromCRL,
 | 
						|
	"privilegewithdrawn":   ocsp.PrivilegeWithdrawn,
 | 
						|
	"aacompromise":         ocsp.AACompromise,
 | 
						|
}
 | 
						|
 | 
						|
// StatusCode is a map between string statuses sent by cli/api
 | 
						|
// to ocsp int statuses
 | 
						|
var StatusCode = map[string]int{
 | 
						|
	"good":    ocsp.Good,
 | 
						|
	"revoked": ocsp.Revoked,
 | 
						|
	"unknown": ocsp.Unknown,
 | 
						|
}
 | 
						|
 | 
						|
// SignRequest represents the desired contents of a
 | 
						|
// specific OCSP response.
 | 
						|
type SignRequest struct {
 | 
						|
	Certificate *x509.Certificate
 | 
						|
	Status      string
 | 
						|
	Reason      int
 | 
						|
	RevokedAt   time.Time
 | 
						|
	Extensions  []pkix.Extension
 | 
						|
	// IssuerHash is the hashing function used to hash the issuer subject and public key
 | 
						|
	// in the OCSP response. Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384,
 | 
						|
	// and crypto.SHA512. If zero, the default is crypto.SHA1.
 | 
						|
	IssuerHash crypto.Hash
 | 
						|
	// If provided ThisUpdate will override the default usage of time.Now().Truncate(time.Hour)
 | 
						|
	ThisUpdate *time.Time
 | 
						|
	// If provided NextUpdate will override the default usage of ThisUpdate.Add(signerInterval)
 | 
						|
	NextUpdate *time.Time
 | 
						|
}
 | 
						|
 | 
						|
// Signer represents a general signer of OCSP responses.  It is
 | 
						|
// responsible for populating all fields in the OCSP response that
 | 
						|
// are not reflected in the SignRequest.
 | 
						|
type Signer interface {
 | 
						|
	Sign(req SignRequest) ([]byte, error)
 | 
						|
}
 | 
						|
 | 
						|
// StandardSigner is the default concrete type of OCSP signer.
 | 
						|
// It represents a single responder (represented by a key and certificate)
 | 
						|
// speaking for a single issuer (certificate).  It is assumed that OCSP
 | 
						|
// responses are issued at a regular interval, which is used to compute
 | 
						|
// the nextUpdate value based on the current time.
 | 
						|
type StandardSigner struct {
 | 
						|
	issuer    *x509.Certificate
 | 
						|
	responder *x509.Certificate
 | 
						|
	key       crypto.Signer
 | 
						|
	interval  time.Duration
 | 
						|
}
 | 
						|
 | 
						|
// ReasonStringToCode tries to convert a reason string to an integer code
 | 
						|
func ReasonStringToCode(reason string) (reasonCode int, err error) {
 | 
						|
	// default to 0
 | 
						|
	if reason == "" {
 | 
						|
		return 0, nil
 | 
						|
	}
 | 
						|
 | 
						|
	reasonCode, present := revocationReasonCodes[strings.ToLower(reason)]
 | 
						|
	if !present {
 | 
						|
		reasonCode, err = strconv.Atoi(reason)
 | 
						|
		if err != nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
		if reasonCode >= ocsp.AACompromise || reasonCode <= ocsp.Unspecified {
 | 
						|
			return 0, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// NewSignerFromFile reads the issuer cert, the responder cert and the responder key
 | 
						|
// from PEM files, and takes an interval in seconds
 | 
						|
func NewSignerFromFile(issuerFile, responderFile, keyFile string, interval time.Duration) (Signer, error) {
 | 
						|
	log.Debug("Loading issuer cert: ", issuerFile)
 | 
						|
	issuerBytes, err := helpers.ReadBytes(issuerFile)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	log.Debug("Loading responder cert: ", responderFile)
 | 
						|
	responderBytes, err := ioutil.ReadFile(responderFile)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	log.Debug("Loading responder key: ", keyFile)
 | 
						|
	keyBytes, err := ioutil.ReadFile(keyFile)
 | 
						|
	if err != nil {
 | 
						|
		return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err)
 | 
						|
	}
 | 
						|
 | 
						|
	issuerCert, err := helpers.ParseCertificatePEM(issuerBytes)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	responderCert, err := helpers.ParseCertificatePEM(responderBytes)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	key, err := helpers.ParsePrivateKeyPEM(keyBytes)
 | 
						|
	if err != nil {
 | 
						|
		log.Debug("Malformed private key %v", err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return NewSigner(issuerCert, responderCert, key, interval)
 | 
						|
}
 | 
						|
 | 
						|
// NewSigner simply constructs a new StandardSigner object from the inputs,
 | 
						|
// taking the interval in seconds
 | 
						|
func NewSigner(issuer, responder *x509.Certificate, key crypto.Signer, interval time.Duration) (Signer, error) {
 | 
						|
	return &StandardSigner{
 | 
						|
		issuer:    issuer,
 | 
						|
		responder: responder,
 | 
						|
		key:       key,
 | 
						|
		interval:  interval,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// Sign is used with an OCSP signer to request the issuance of
 | 
						|
// an OCSP response.
 | 
						|
func (s StandardSigner) Sign(req SignRequest) ([]byte, error) {
 | 
						|
	if req.Certificate == nil {
 | 
						|
		return nil, cferr.New(cferr.OCSPError, cferr.ReadFailed)
 | 
						|
	}
 | 
						|
 | 
						|
	// Verify that req.Certificate is issued under s.issuer
 | 
						|
	if bytes.Compare(req.Certificate.RawIssuer, s.issuer.RawSubject) != 0 {
 | 
						|
		return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch)
 | 
						|
	}
 | 
						|
 | 
						|
	err := req.Certificate.CheckSignatureFrom(s.issuer)
 | 
						|
	if err != nil {
 | 
						|
		return nil, cferr.Wrap(cferr.OCSPError, cferr.VerifyFailed, err)
 | 
						|
	}
 | 
						|
 | 
						|
	var thisUpdate, nextUpdate time.Time
 | 
						|
	if req.ThisUpdate != nil {
 | 
						|
		thisUpdate = *req.ThisUpdate
 | 
						|
	} else {
 | 
						|
		// Round thisUpdate times down to the nearest hour
 | 
						|
		thisUpdate = time.Now().Truncate(time.Hour)
 | 
						|
	}
 | 
						|
	if req.NextUpdate != nil {
 | 
						|
		nextUpdate = *req.NextUpdate
 | 
						|
	} else {
 | 
						|
		nextUpdate = thisUpdate.Add(s.interval)
 | 
						|
	}
 | 
						|
 | 
						|
	status, ok := StatusCode[req.Status]
 | 
						|
	if !ok {
 | 
						|
		return nil, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
 | 
						|
	}
 | 
						|
 | 
						|
	// If the OCSP responder is the same as the issuer, there is no need to
 | 
						|
	// include any certificate in the OCSP response, which decreases the byte size
 | 
						|
	// of OCSP responses dramatically.
 | 
						|
	certificate := s.responder
 | 
						|
	if s.issuer == s.responder || bytes.Equal(s.issuer.Raw, s.responder.Raw) {
 | 
						|
		certificate = nil
 | 
						|
	}
 | 
						|
 | 
						|
	template := ocsp.Response{
 | 
						|
		Status:          status,
 | 
						|
		SerialNumber:    req.Certificate.SerialNumber,
 | 
						|
		ThisUpdate:      thisUpdate,
 | 
						|
		NextUpdate:      nextUpdate,
 | 
						|
		Certificate:     certificate,
 | 
						|
		ExtraExtensions: req.Extensions,
 | 
						|
		IssuerHash:      req.IssuerHash,
 | 
						|
	}
 | 
						|
 | 
						|
	if status == ocsp.Revoked {
 | 
						|
		template.RevokedAt = req.RevokedAt
 | 
						|
		template.RevocationReason = req.Reason
 | 
						|
	}
 | 
						|
 | 
						|
	return ocsp.CreateResponse(s.issuer, s.responder, template, s.key)
 | 
						|
}
 |