Fix CA startup with Vault RA configuration

pull/1803/head
Herman Slatman 2 weeks ago
parent 07279dd248
commit 6bc0a86207
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -8,6 +8,7 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"fmt"
"log"
"net/http"
"strings"
@ -447,6 +448,7 @@ func (a *Authority) init() error {
return err
}
a.rootX509Certs = append(a.rootX509Certs, resp.RootCertificate)
a.intermediateX509Certs = append(a.intermediateX509Certs, resp.IntermediateCertificates...)
}
}
@ -695,32 +697,42 @@ func (a *Authority) init() error {
options := &scep.Options{
Roots: a.rootX509Certs,
Intermediates: a.intermediateX509Certs,
SignerCert: a.intermediateX509Certs[0],
}
if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.IntermediateKey,
Password: a.password,
}); err != nil {
return err
// intermediate certificates can be empty in RA mode
if len(a.intermediateX509Certs) > 0 {
options.SignerCert = a.intermediateX509Certs[0]
}
// TODO(hs): instead of creating the decrypter here, pass the
// intermediate key + chain down to the SCEP authority,
// and only instantiate it when required there. Is that possible?
// Also with entering passwords?
// TODO(hs): if moving the logic, try improving the logic for the
// decrypter password too? Right now it needs to be entered multiple
// times; I've observed it to be three times maximum, every time
// the intermediate key is read.
_, isRSA := options.Signer.Public().(*rsa.PublicKey)
if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSA {
if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
DecryptionKey: a.config.IntermediateKey,
Password: a.password,
}); err == nil {
// only pass the decrypter down when it was successfully created,
// meaning it's an RSA key, and `CreateDecrypter` did not fail.
options.Decrypter = decrypter
options.DecrypterCert = options.Intermediates[0]
// attempt to create the (default) SCEP signer if the intermediate
// key is configured.
if a.config.IntermediateKey != "" {
if options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
SigningKey: a.config.IntermediateKey,
Password: a.password,
}); err != nil {
return err
}
// TODO(hs): instead of creating the decrypter here, pass the
// intermediate key + chain down to the SCEP authority,
// and only instantiate it when required there. Is that possible?
// Also with entering passwords?
// TODO(hs): if moving the logic, try improving the logic for the
// decrypter password too? Right now it needs to be entered multiple
// times; I've observed it to be three times maximum, every time
// the intermediate key is read.
_, isRSAKey := options.Signer.Public().(*rsa.PublicKey)
if km, ok := a.keyManager.(kmsapi.Decrypter); ok && isRSAKey {
if decrypter, err := km.CreateDecrypter(&kmsapi.CreateDecrypterRequest{
DecryptionKey: a.config.IntermediateKey,
Password: a.password,
}); err == nil {
// only pass the decrypter down when it was successfully created,
// meaning it's an RSA key, and `CreateDecrypter` did not fail.
options.Decrypter = decrypter
options.DecrypterCert = options.Intermediates[0]
}
}
}
@ -811,6 +823,26 @@ func (a *Authority) init() error {
return nil
}
func (a *Authority) Mode() string {
if a.isRA() {
return fmt.Sprintf("RA (%s)", casapi.TypeOf(a.x509CAService).Name())
}
return "CA" // TODO(hs): more info? I.e. KMS type?
}
func (a *Authority) isRA() bool {
if a.x509CAService == nil {
return false
}
switch casapi.TypeOf(a.x509CAService) {
case casapi.StepCAS, casapi.CloudCAS, casapi.VaultCAS, casapi.ExternalCAS:
return true
default:
return false
}
}
// initLogf is used to log initialization information. The output
// can be disabled by starting the CA with the `--quiet` flag.
func (a *Authority) initLogf(format string, v ...any) {

@ -425,6 +425,7 @@ func (ca *CA) Run() error {
log.Printf("Current context: %s", step.Contexts().GetCurrent().Name)
}
log.Printf("Config file: %s", ca.getConfigFileOutput())
log.Printf("Mode: %s", ca.auth.Mode())
baseURL := fmt.Sprintf("https://%s%s",
authorityInfo.DNSNames[0],
ca.config.Address[strings.LastIndex(ca.config.Address, ":"):])

@ -116,7 +116,8 @@ type GetCertificateAuthorityRequest struct {
// GetCertificateAuthorityResponse is the response that contains
// the root certificate.
type GetCertificateAuthorityResponse struct {
RootCertificate *x509.Certificate
RootCertificate *x509.Certificate
IntermediateCertificates []*x509.Certificate
}
// CreateKeyRequest is the request used to generate a new key using a KMS.

@ -67,6 +67,23 @@ func (t Type) String() string {
return strings.ToLower(string(t))
}
// Name returns the name of the CAS implementation.
func (t Type) Name() (name string) {
switch t {
case CloudCAS:
name = "GCP CAS"
case StepCAS:
name = "Step CAS"
case VaultCAS:
name = "Vault"
case ExternalCAS:
name = "External"
default:
name = "SoftCAS" // TODO(hs): different name? It's not a "CAS" CAS, really
}
return
}
// TypeOf returns the type of the given CertificateAuthorityService.
func TypeOf(c CertificateAuthorityService) Type {
if ct, ok := c.(interface{ Type() Type }); ok {

@ -165,7 +165,8 @@ func (v *VaultCAS) GetCertificateAuthority(*apiv1.GetCertificateAuthorityRequest
}
return &apiv1.GetCertificateAuthorityResponse{
RootCertificate: cert.root,
RootCertificate: cert.root,
IntermediateCertificates: cert.intermediates,
}, nil
}

@ -37,19 +37,21 @@ 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")
}
// 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")
// the signer is optional, but if it's set, its public key must match the signer
// certificate public key.
if o.Signer != nil {
// 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")
}
}
// decrypter can be nil in case a signing only key is used; validation complete.

Loading…
Cancel
Save