Verify full decrypter/signer configuration at usage time

When changing the SCEP configuration it is possible that one
or both of the decrypter configurations required are not available
or have been provided in a way that's not usable for actual SCEP
requests.

Instead of failing hard when provisioners are loaded,
which could result in the CA not starting properly, this type of
problematic configuration errors will now be handled at usage
time instead.
pull/1523/head
Herman Slatman 10 months ago
parent 88ed900dc3
commit c0a1837cd9
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -3,6 +3,7 @@ package provisioner
import ( import (
"context" "context"
"crypto" "crypto"
"crypto/rsa"
"crypto/subtle" "crypto/subtle"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
@ -177,23 +178,23 @@ func (s *SCEP) Init(config Config) (err error) {
if s.MinimumPublicKeyLength == 0 { if s.MinimumPublicKeyLength == 0 {
s.MinimumPublicKeyLength = 2048 s.MinimumPublicKeyLength = 2048
} }
if s.MinimumPublicKeyLength%8 != 0 { if s.MinimumPublicKeyLength%8 != 0 {
return errors.Errorf("%d bits is not exactly divisible by 8", s.MinimumPublicKeyLength) return errors.Errorf("%d bits is not exactly divisible by 8", s.MinimumPublicKeyLength)
} }
// Set the encryption algorithm to use
s.encryptionAlgorithm = s.EncryptionAlgorithmIdentifier // TODO(hs): we might want to upgrade the default security to AES-CBC? s.encryptionAlgorithm = s.EncryptionAlgorithmIdentifier // TODO(hs): we might want to upgrade the default security to AES-CBC?
if s.encryptionAlgorithm < 0 || s.encryptionAlgorithm > 4 { if s.encryptionAlgorithm < 0 || s.encryptionAlgorithm > 4 {
return errors.New("only encryption algorithm identifiers from 0 to 4 are valid") return errors.New("only encryption algorithm identifiers from 0 to 4 are valid")
} }
// Prepare the SCEP challenge validator
s.challengeValidationController = newChallengeValidationController( s.challengeValidationController = newChallengeValidationController(
config.WebhookClient, config.WebhookClient,
s.GetOptions().GetWebhooks(), s.GetOptions().GetWebhooks(),
) )
skip := false // TODO(hs): remove this; currently a helper for debugging if decryptionKey := s.DecrypterKey; decryptionKey != "" {
if decryptionKey := s.DecrypterKey; decryptionKey != "" && !skip {
u, err := uri.Parse(s.DecrypterKey) u, err := uri.Parse(s.DecrypterKey)
if err != nil { if err != nil {
return fmt.Errorf("failed parsing decrypter key: %w", err) return fmt.Errorf("failed parsing decrypter key: %w", err)
@ -226,7 +227,7 @@ func (s *SCEP) Init(config Config) (err error) {
return fmt.Errorf("failed creating decrypter: %w", err) return fmt.Errorf("failed creating decrypter: %w", err)
} }
if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ if s.signer, err = s.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: decryptionKey, // TODO(hs): support distinct signer key SigningKey: decryptionKey, // TODO(hs): support distinct signer key in the future?
Password: []byte(s.DecrypterKeyPassword), Password: []byte(s.DecrypterKeyPassword),
}); err != nil { }); err != nil {
return fmt.Errorf("failed creating signer: %w", err) return fmt.Errorf("failed creating signer: %w", err)
@ -248,23 +249,19 @@ func (s *SCEP) Init(config Config) (err error) {
} }
// TODO(hs): alternatively, check if the KMS keyManager is a CertificateManager // TODO(hs): alternatively, check if the KMS keyManager is a CertificateManager
// and load the certificate corresponding to the decryption key. // and load the certificate corresponding to the decryption key?
// final validation for the decrypter // Final validation for the decrypter. If both the decrypter and the certificate
if s.decrypter != nil { // are available, the public keys must match. We currently allow the decrypter to
// // TODO(hs): enable this validation again // be set without a certificate without warning the user, but
// if s.decrypterCertificate == nil { if s.decrypter != nil && s.decrypterCertificate != nil {
// // TODO: don't hard skip at init? decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey)
// return fmt.Errorf("no decrypter certificate available for decrypter in %q", s.Name) if !ok {
// } return fmt.Errorf("only RSA keys are supported")
// // validate the decrypter key }
// decrypterPublicKey, ok := s.decrypter.Public().(*rsa.PublicKey) if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) {
// if !ok { return errors.New("mismatch between decryption certificate and decrypter public keys")
// return fmt.Errorf("only RSA keys are supported") }
// }
// if !decrypterPublicKey.Equal(s.decrypterCertificate.PublicKey) {
// return errors.New("mismatch between decryption certificate and decrypter public keys")
// }
} }
// TODO: add other, SCEP specific, options? // TODO: add other, SCEP specific, options?
@ -350,10 +347,19 @@ func (s *SCEP) selectValidationMethod() validationMethod {
return validationMethodNone return validationMethodNone
} }
// GetDecrypter returns the provisioner specific decrypter,
// used to decrypt SCEP request messages sent by a SCEP client.
// The decrypter consists of a crypto.Decrypter (a private key)
// and a certificate for the public key corresponding to the
// private key.
func (s *SCEP) GetDecrypter() (*x509.Certificate, crypto.Decrypter) { func (s *SCEP) GetDecrypter() (*x509.Certificate, crypto.Decrypter) {
return s.decrypterCertificate, s.decrypter return s.decrypterCertificate, s.decrypter
} }
// GetSigner returns the provisioner specific signer, used to
// sign SCEP response messages for the client. The signer consists
// of a crypto.Signer and a certificate for the public key
// corresponding to the private key.
func (s *SCEP) GetSigner() (*x509.Certificate, crypto.Signer) { func (s *SCEP) GetSigner() (*x509.Certificate, crypto.Signer) {
return s.decrypterCertificate, s.signer return s.decrypterCertificate, s.signer
} }

@ -486,19 +486,22 @@ func (a *Authority) ValidateChallenge(ctx context.Context, challenge, transactio
func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.Decrypter, err error) { func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate, pkey crypto.Decrypter, err error) {
p := provisionerFromContext(ctx) p := provisionerFromContext(ctx)
cert, pkey = p.GetDecrypter()
// return provisioner specific decrypter, if available switch {
if cert, pkey = p.GetDecrypter(); cert != nil && pkey != nil { case cert != nil && pkey != nil:
return return
case cert == nil && pkey != nil:
return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter certificate available", p.GetName())
case cert != nil && pkey == nil:
return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter available", p.GetName())
} }
// fallback to the CA wide RSA decrypter, which is the cert, pkey = a.signerCertificate, a.defaultDecrypter
// intermediate CA. switch {
cert = a.signerCertificate case cert == nil && pkey != nil:
pkey = a.defaultDecrypter return nil, nil, fmt.Errorf("provisioner %q does not have a default decrypter certificate available", p.GetName())
case cert != nil && pkey == nil:
if cert == nil || pkey == nil { return nil, nil, fmt.Errorf("provisioner %q does not have a default decrypter available", p.GetName())
return nil, nil, fmt.Errorf("provisioner %q does not have a decrypter available", p.GetName())
} }
return return
@ -506,19 +509,22 @@ func (a *Authority) selectDecrypter(ctx context.Context) (cert *x509.Certificate
func (a *Authority) selectSigner(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) { func (a *Authority) selectSigner(ctx context.Context) (cert *x509.Certificate, pkey crypto.PrivateKey, err error) {
p := provisionerFromContext(ctx) p := provisionerFromContext(ctx)
cert, pkey = p.GetSigner()
// return provisioner specific signer, if available switch {
if cert, pkey = p.GetSigner(); cert != nil && pkey != nil { case cert != nil && pkey != nil:
return return
case cert == nil && pkey != nil:
return nil, nil, fmt.Errorf("provisioner %q does not have a signer certificate available", p.GetName())
case cert != nil && pkey == nil:
return nil, nil, fmt.Errorf("provisioner %q does not have a signer available", p.GetName())
} }
// fallback to the CA wide RSA signer, which is the cert, pkey = a.signerCertificate, a.defaultSigner
// intermediate CA. switch {
cert = a.signerCertificate case cert == nil && pkey != nil:
pkey = a.defaultSigner return nil, nil, fmt.Errorf("provisioner %q does not have a default signer certificate available", p.GetName())
case cert != nil && pkey == nil:
if cert == nil || pkey == nil { return nil, nil, fmt.Errorf("provisioner %q does not have a default signer available", p.GetName())
return nil, nil, fmt.Errorf("provisioner %q does not have a signer available", p.GetName())
} }
return return

Loading…
Cancel
Save