certificates/scep/options.go

78 lines
3 KiB
Go
Raw Normal View History

2021-03-21 15:42:41 +00:00
package scep
import (
"crypto"
"crypto/rsa"
2021-03-21 15:42:41 +00:00
"crypto/x509"
"errors"
2021-03-21 15:42:41 +00:00
)
type Options struct {
// Roots contains the (federated) CA roots certificate(s)
Roots []*x509.Certificate `json:"-"`
// Intermediates points issuer certificate, along with any other bundled certificates
// to be returned in the chain for consumers.
Intermediates []*x509.Certificate `json:"-"`
// SignerCert points to the certificate of the CA signer. It usually is the same as the
// first certificate in the CertificateChain.
SignerCert *x509.Certificate `json:"-"`
2021-03-26 14:22:04 +00:00
// Signer signs CSRs in SCEP. Configured in the ca.json key property.
2021-03-21 15:42:41 +00:00
Signer crypto.Signer `json:"-"`
2021-03-26 14:22:04 +00:00
// Decrypter decrypts encrypted SCEP messages. Configured in the ca.json key property.
2021-03-21 15:42:41 +00:00
Decrypter crypto.Decrypter `json:"-"`
}
type comparablePublicKey interface {
Equal(crypto.PublicKey) bool
}
2021-03-21 15:42:41 +00:00
// Validate checks the fields in Options.
func (o *Options) Validate() error {
switch {
case len(o.Intermediates) == 0:
return errors.New("no intermediate certificate available for SCEP authority")
case o.Signer == nil:
return errors.New("no signer available for SCEP authority")
case o.SignerCert == nil:
return errors.New("no signer certificate available for SCEP authority")
2021-03-26 14:22:04 +00:00
}
// check if the signer (intermediate CA) certificate has the same public key as
// the signer. According to the RFC it seems valid to have different keys for
// the intermediate and the CA signing new certificates, so this might change
// in the future.
signerPublicKey := o.Signer.Public().(comparablePublicKey)
if !signerPublicKey.Equal(o.SignerCert.PublicKey) {
return errors.New("mismatch between signer certificate and public key")
2021-03-26 14:22:04 +00:00
}
// decrypter can be nil in case a signing only key is used; validation complete.
if o.Decrypter == nil {
return nil
}
// If a decrypter is available, check that it's backed by an RSA key. According to the
// RFC: https://tools.ietf.org/html/rfc8894#section-3.1, SCEP can be used with something
// different than RSA, but requires the encryption to be performed using the challenge
// password in that case. An older version of specification states that only RSA is
// supported: https://tools.ietf.org/html/draft-nourse-scep-23#section-2.1.1. Other
// algorithms do not seem to be supported in certnanny/sscep, but it might work
2021-03-26 14:22:04 +00:00
// in micromdm/scep. Currently only RSA is allowed, but it might be an option
// to try other algorithms in the future.
decrypterPublicKey, ok := o.Decrypter.Public().(*rsa.PublicKey)
if !ok {
return errors.New("only RSA keys are (currently) supported as decrypters")
}
2021-03-26 14:22:04 +00:00
2021-03-26 14:24:27 +00:00
// check if intermediate public key is the same as the decrypter public key.
2021-03-26 14:22:04 +00:00
// In certnanny/sscep it's mentioned that the signing key can be different
// from the decrypting (and encrypting) key. These options are only used and
// validated when the intermediate CA is also used as the decrypter, though,
// so they should match.
if !decrypterPublicKey.Equal(o.SignerCert.PublicKey) {
return errors.New("mismatch between certificate chain and decrypter public keys")
}
2021-03-26 14:22:04 +00:00
2021-03-21 15:42:41 +00:00
return nil
}