Skip to content

Commit

Permalink
CSR recipients refactor (#166)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jessepeterson authored Mar 25, 2021
1 parent 377df44 commit 9b519a2
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 34 deletions.
40 changes: 10 additions & 30 deletions cmd/scepclient/scepclient.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"bytes"
"context"
"crypto/sha256"
"crypto/x509"
Expand Down Expand Up @@ -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 {
Expand All @@ -156,7 +149,7 @@ func run(cfg runCfg) error {

tmpl := &scep.PKIMessage{
MessageType: msgType,
Recipients: recipients,
Recipients: certs,
SignerKey: key,
SignerCert: signerCert,
}
Expand All @@ -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")
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
19 changes: 18 additions & 1 deletion scep/certs_selector.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package scep

import "crypto/x509"
import (
"crypto/sha256"
"crypto/x509"
)

// A CertsSelector filters certificates.
type CertsSelector interface {
Expand Down Expand Up @@ -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
}
}
11 changes: 8 additions & 3 deletions scep/scep.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -633,6 +637,7 @@ func NewCSRRequest(csr *x509.CertificateRequest, tmpl *PKIMessage, opts ...Optio
TransactionID: tID,
SenderNonce: sn,
CSRReqMessage: cr,
Recipients: recipients,
logger: conf.logger,
}

Expand Down

0 comments on commit 9b519a2

Please sign in to comment.