From 9b519a22d4aff541642611e8e84b4eda9d9a2020 Mon Sep 17 00:00:00 2001 From: Jesse Peterson Date: Thu, 25 Mar 2021 10:31:03 -0700 Subject: [PATCH] CSR recipients refactor (#166) CSR recipients refactor: * Populate (optionally selected) recipients in PKIMessage * Use SHA2 CertsSelector by way of scep library, rather than separately * Be specific about lack of recipients in error responses --- cmd/scepclient/scepclient.go | 40 +++++++++--------------------------- scep/certs_selector.go | 19 ++++++++++++++++- scep/scep.go | 11 +++++++--- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/cmd/scepclient/scepclient.go b/cmd/scepclient/scepclient.go index 67dad3f..b26139b 100644 --- a/cmd/scepclient/scepclient.go +++ b/cmd/scepclient/scepclient.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "context" "crypto/sha256" "crypto/x509" @@ -129,12 +128,6 @@ func run(cfg runCfg) error { logCerts(level.Debug(logger), certs) } - // pick the CA/RA cert using our CertsSelector - recipients := cfg.caCertsSelector.SelectCerts(certs) - if len(recipients) < 1 { - return errors.New("no CA/RA certificates found") - } - var signerCert *x509.Certificate { if cert != nil { @@ -156,7 +149,7 @@ func run(cfg runCfg) error { tmpl := &scep.PKIMessage{ MessageType: msgType, - Recipients: recipients, + Recipients: certs, SignerKey: key, SignerCert: signerCert, } @@ -167,7 +160,7 @@ func run(cfg runCfg) error { } } - msg, err := scep.NewCSRRequest(csr, tmpl, scep.WithLogger(logger)) + msg, err := scep.NewCSRRequest(csr, tmpl, scep.WithLogger(logger), scep.WithCertsSelector(cfg.caCertsSelector)) if err != nil { return errors.Wrap(err, "creating csr pkiMessage") } @@ -183,7 +176,7 @@ func run(cfg runCfg) error { return errors.Wrapf(err, "PKIOperation for %s", msgType) } - respMsg, err = scep.ParsePKIMessage(respBytes, scep.WithLogger(logger), scep.WithCACerts(recipients)) + respMsg, err = scep.ParsePKIMessage(respBytes, scep.WithLogger(logger), scep.WithCACerts(msg.Recipients)) if err != nil { return errors.Wrapf(err, "parsing pkiMessage response %s", msgType) } @@ -232,34 +225,21 @@ func logCerts(logger log.Logger, certs []*x509.Certificate) { } } -// sha256FingerprintCertsSelector returns a CertsSelector that matches a SHA-256 fingerprint -func sha256FingerprintCertsSelector(hash []byte) scep.CertsSelectorFunc { - return func(certs []*x509.Certificate) (selected []*x509.Certificate) { - for _, cert := range certs { - sum := sha256.Sum256(cert.Raw) - if bytes.Compare(sum[:], hash) == 0 { - selected = append(selected, cert) - return - } - } - return - } -} - // validateSHA256Fingerprint makes sure fingerprint looks like a SHA-256 hash. -// We take out spaces and colons from fingerprint as it may come in various forms: +// We remove spaces and colons from fingerprint as it may come in various forms: // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 // E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 // e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855 // e3:b0:c4:42:98:fc:1c:14:9a:fb:f4:c8:99:6f:b9:24:27:ae:41:e4:64:9b:93:4c:a4:95:99:1b:78:52:b8:55 -func validateSHA256Fingerprint(fingerprint string) (bytes []byte, err error) { +func validateSHA256Fingerprint(fingerprint string) (hash [32]byte, err error) { fingerprint = strings.NewReplacer(" ", "", ":", "").Replace(fingerprint) - bytes, err = hex.DecodeString(fingerprint) + byteSlice, err := hex.DecodeString(fingerprint) + copy(hash[:], byteSlice) if err != nil { return } // check for length of SHA-256 - if len(bytes) != 32 { + if len(byteSlice) != 32 { err = errors.New("invalid SHA-256 hash length") } return @@ -317,12 +297,12 @@ func main() { caCertsSelector := scep.NopCertsSelector() if *flCAFingerprint != "" { - hashBytes, err := validateSHA256Fingerprint(*flCAFingerprint) + hash, err := validateSHA256Fingerprint(*flCAFingerprint) if err != nil { fmt.Println(fmt.Errorf("invalid fingerprint: %v", err)) os.Exit(1) } - caCertsSelector = sha256FingerprintCertsSelector(hashBytes) + caCertsSelector = scep.SHA256FingerprintCertsSelector(hash) } dir := filepath.Dir(*flPKeyPath) diff --git a/scep/certs_selector.go b/scep/certs_selector.go index 0e22029..4cc8cea 100644 --- a/scep/certs_selector.go +++ b/scep/certs_selector.go @@ -1,6 +1,9 @@ package scep -import "crypto/x509" +import ( + "crypto/sha256" + "crypto/x509" +) // A CertsSelector filters certificates. type CertsSelector interface { @@ -35,3 +38,17 @@ func EnciphermentCertsSelector() CertsSelectorFunc { return selected } } + +// SHA256FingerprintCertsSelector selects a certificate that matches +// a SHA-256 hash of the raw certificate DER bytes +func SHA256FingerprintCertsSelector(hash [32]byte) CertsSelectorFunc { + return func(certs []*x509.Certificate) (selected []*x509.Certificate) { + for _, cert := range certs { + if sha256.Sum256(cert.Raw) == hash { + selected = append(selected, cert) + return + } + } + return + } +} diff --git a/scep/scep.go b/scep/scep.go index 6aae59e..8015b75 100644 --- a/scep/scep.go +++ b/scep/scep.go @@ -189,7 +189,7 @@ type PKIMessage struct { // decrypted enveloped content pkiEnvelope []byte - // Used to sign message + // Used to encrypt message Recipients []*x509.Certificate // Signer info @@ -565,8 +565,12 @@ func NewCSRRequest(csr *x509.CertificateRequest, tmpl *PKIMessage, opts ...Optio derBytes := csr.Raw recipients := conf.certsSelector.SelectCerts(tmpl.Recipients) - if len(recipients) == 0 { - return nil, errors.New("no recipients that can be used for KeyEncipherment.") + if len(recipients) < 1 { + if len(tmpl.Recipients) >= 1 { + // our certsSelector eliminated any CA/RA recipients + return nil, errors.New("no selected CA/RA recipients") + } + return nil, errors.New("no CA/RA recipients") } e7, err := pkcs7.Encrypt(derBytes, recipients) if err != nil { @@ -633,6 +637,7 @@ func NewCSRRequest(csr *x509.CertificateRequest, tmpl *PKIMessage, opts ...Optio TransactionID: tID, SenderNonce: sn, CSRReqMessage: cr, + Recipients: recipients, logger: conf.logger, }