Add configuration option for specifying the minimum public key length

Instead of using the defaultPublicKeyValidator a new validator called
publicKeyMinimumLengthValidator has been implemented that uses a
configurable minimum length for public keys in CSRs.

It's also an option to alter the defaultPublicKeyValidator to also
take a parameter, but that would touch quite some lines of code. This
might be a viable option after merging SCEP support.
This commit is contained in:
Herman Slatman 2021-05-06 22:56:28 +02:00 committed by max furman
parent 4168449935
commit 2beea1aa89
2 changed files with 47 additions and 4 deletions

View file

@ -2,6 +2,7 @@ package provisioner
import (
"context"
"fmt"
"time"
"github.com/pkg/errors"
@ -17,9 +18,11 @@ type SCEP struct {
ForceCN bool `json:"forceCN,omitempty"`
ChallengePassword string `json:"challenge,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
Options *Options `json:"options,omitempty"`
Claims *Claims `json:"claims,omitempty"`
claimer *Claimer
// MinimumPublicKeyLength is the minimum length for public keys in CSRs
MinimumPublicKeyLength int `json:"minimumPublicKeyLength,omitempty"`
Options *Options `json:"options,omitempty"`
Claims *Claims `json:"claims,omitempty"`
claimer *Claimer
secretChallengePassword string
}
@ -79,6 +82,15 @@ func (s *SCEP) Init(config Config) (err error) {
s.secretChallengePassword = s.ChallengePassword
s.ChallengePassword = "*** redacted ***"
// Default to 2048 bits minimum public key length (for CSRs) if not set
if s.MinimumPublicKeyLength == 0 {
s.MinimumPublicKeyLength = 2048
}
if s.MinimumPublicKeyLength%8 != 0 {
return fmt.Errorf("only minimum public keys exactly divisible by 8 are supported; %d is not exactly divisibly by 8", s.MinimumPublicKeyLength)
}
// TODO: add other, SCEP specific, options?
return err
@ -94,7 +106,7 @@ func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e
newForceCNOption(s.ForceCN),
profileDefaultDuration(s.claimer.DefaultTLSCertDuration()),
// validators
defaultPublicKeyValidator{},
newPublicKeyMinimumLengthValidator(s.MinimumPublicKeyLength),
newValidityValidator(s.claimer.MinTLSCertDuration(), s.claimer.MaxTLSCertDuration()),
}, nil
}

View file

@ -8,6 +8,7 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/json"
"fmt"
"net"
"net/url"
"reflect"
@ -117,6 +118,36 @@ func (v defaultPublicKeyValidator) Valid(req *x509.CertificateRequest) error {
return nil
}
// publicKeyMinimumLengthValidator validates the length (in bits) of the public key
// of a certificate request is at least a certain length
type publicKeyMinimumLengthValidator struct {
length int
}
// newPublicKeyMinimumLengthValidator creates a new publicKeyMinimumLengthValidator
// with the given length as its minimum value
// TODO: change the defaultPublicKeyValidator to have a configurable length instead?
func newPublicKeyMinimumLengthValidator(length int) publicKeyMinimumLengthValidator {
return publicKeyMinimumLengthValidator{
length: length,
}
}
// Valid checks that certificate request common name matches the one configured.
func (v publicKeyMinimumLengthValidator) Valid(req *x509.CertificateRequest) error {
switch k := req.PublicKey.(type) {
case *rsa.PublicKey:
minimumLengthInBytes := v.length / 8
if k.Size() < minimumLengthInBytes {
return fmt.Errorf("rsa key in CSR must be at least %d bits (%d bytes)", v.length, minimumLengthInBytes)
}
case *ecdsa.PublicKey, ed25519.PublicKey:
default:
return errors.Errorf("unrecognized public key of type '%T' in CSR", k)
}
return nil
}
// commonNameValidator validates the common name of a certificate request.
type commonNameValidator string