From 781f8042a79cabcf61a5e6c01affdbadcb785932 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Mon, 15 Jul 2024 19:44:41 +0200 Subject: [PATCH] Add `-key-encipherment-selector` and use CA certs for verification (#217) When a SCEP server returns multiple certificates, it is possible that not all certificates can or should be used for encryption. There already was a `KeyEnciphermentsSelector`, but that wasn't readily usable with the provided SCEP client. A new flag was added to enable this selector: `-key-encipherment-selector`. It will filter out certificates that aren't marked as being usable for key or data encryption. When verifying the `PKIMessage` from the CA only the `Recipients` in the outgoing message were being looked through when selecting a certificate to verify the signature. This commit changes that by including all certificates returned by the CA when the client performs the `GetCACerts` operation. --- cmd/scepclient/scepclient.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/cmd/scepclient/scepclient.go b/cmd/scepclient/scepclient.go index b54ffa7..277b2c4 100644 --- a/cmd/scepclient/scepclient.go +++ b/cmd/scepclient/scepclient.go @@ -115,15 +115,15 @@ func run(cfg runCfg) error { if err != nil { return err } - var certs []*x509.Certificate + var caCerts []*x509.Certificate { if certNum > 1 { - certs, err = scep.CACerts(resp) + caCerts, err = scep.CACerts(resp) if err != nil { return err } } else { - certs, err = x509.ParseCertificates(resp) + caCerts, err = x509.ParseCertificates(resp) if err != nil { return err } @@ -131,7 +131,7 @@ func run(cfg runCfg) error { } if cfg.debug { - logCerts(level.Debug(logger), certs) + logCerts(level.Debug(logger), caCerts) } var signerCert *x509.Certificate @@ -155,7 +155,7 @@ func run(cfg runCfg) error { tmpl := &scep.PKIMessage{ MessageType: msgType, - Recipients: certs, + Recipients: caCerts, SignerKey: key, SignerCert: signerCert, } @@ -182,7 +182,7 @@ func run(cfg runCfg) error { return errors.Wrapf(err, "PKIOperation for %s", msgType) } - respMsg, err = scep.ParsePKIMessage(respBytes, scep.WithLogger(logger), scep.WithCACerts(msg.Recipients)) + respMsg, err = scep.ParsePKIMessage(respBytes, scep.WithLogger(logger), scep.WithCACerts(caCerts)) if err != nil { return errors.Wrapf(err, "parsing pkiMessage response %s", msgType) } @@ -253,7 +253,7 @@ func validateFingerprint(fingerprint string) (hash []byte, err error) { return } -func validateFlags(keyPath, serverURL string) error { +func validateFlags(keyPath, serverURL, caFingerprint string, useKeyEnciphermentSelector bool) error { if keyPath == "" { return errors.New("must specify private key path") } @@ -264,6 +264,9 @@ func validateFlags(keyPath, serverURL string) error { if err != nil { return fmt.Errorf("invalid server-url flag parameter %s", err) } + if caFingerprint != "" && useKeyEnciphermentSelector { + return errors.New("ca-fingerprint and key-encipherment-selector can't be used at the same time") + } return nil } @@ -285,12 +288,15 @@ func main() { flDNSName = flag.String("dnsname", "", "DNS name to be included in the certificate (SAN)") // in case of multiple certificate authorities, we need to figure out who the recipient of the encrypted - // data is. - flCAFingerprint = flag.String("ca-fingerprint", "", "SHA-256 digest of CA certificate for NDES server. Note: Changed from MD5.") + // data is. This can be done using either the CA fingerprint, or based on the key usage encoded in the + // certificates returned by the authority. + flCAFingerprint = flag.String("ca-fingerprint", "", "SHA-256 digest of CA certificate for NDES server. Note: Changed from MD5.") + flKeyEnciphermentSelector = flag.Bool("key-encipherment-selector", false, "Filter CA certificates by key encipherment usage") flDebugLogging = flag.Bool("debug", false, "enable debug logging") flLogJSON = flag.Bool("log-json", false, "use JSON for log output") ) + flag.Parse() // print version information @@ -299,19 +305,22 @@ func main() { os.Exit(0) } - if err := validateFlags(*flPKeyPath, *flServerURL); err != nil { + if err := validateFlags(*flPKeyPath, *flServerURL, *flCAFingerprint, *flKeyEnciphermentSelector); err != nil { fmt.Println(err) os.Exit(1) } caCertsSelector := scep.NopCertsSelector() - if *flCAFingerprint != "" { + switch { + case *flCAFingerprint != "": hash, err := validateFingerprint(*flCAFingerprint) if err != nil { fmt.Printf("invalid fingerprint: %s\n", err) os.Exit(1) } caCertsSelector = scep.FingerprintCertsSelector(fingerprintHashType, hash) + case *flKeyEnciphermentSelector: + caCertsSelector = scep.EnciphermentCertsSelector() } dir := filepath.Dir(*flPKeyPath)