diff --git a/app/controlplane/pkg/ca/ca.go b/app/controlplane/pkg/ca/ca.go index 93f81c94f..fab1775db 100644 --- a/app/controlplane/pkg/ca/ca.go +++ b/app/controlplane/pkg/ca/ca.go @@ -53,8 +53,9 @@ func NewCertificateAuthoritiesFromConfig(configCAs []*conf.CA, logger *log.Helpe var authority CertificateAuthority if configCA.GetFileCa() != nil { fileCa := configCA.GetFileCa() - logger.Log(log.LevelInfo, "msg", "Keyless: File CA configured") - authority, err = fileca.New(fileCa.GetCertPath(), fileCa.GetKeyPath(), fileCa.GetKeyPass(), false) + logger.Log(log.LevelInfo, "msg", fmt.Sprintf("Keyless: File CA configured (issuer = %v)", configCA.Issuer)) + // load the CA and verify it if it's configured as an issuer CA + authority, err = fileca.New(fileCa.GetCertPath(), fileCa.GetKeyPath(), fileCa.GetKeyPass(), configCA.Issuer) } if configCA.GetEjbcaCa() != nil { diff --git a/app/controlplane/pkg/ca/fileca/fileca.go b/app/controlplane/pkg/ca/fileca/fileca.go index a79cf6483..3a996627b 100644 --- a/app/controlplane/pkg/ca/fileca/fileca.go +++ b/app/controlplane/pkg/ca/fileca/fileca.go @@ -16,13 +16,20 @@ package fileca import ( + "bytes" "context" + "crypto" "crypto/x509" + "errors" "fmt" + "os" + "path/filepath" fulcioca "github.com/sigstore/fulcio/pkg/ca" - fulciofileca "github.com/sigstore/fulcio/pkg/ca/fileca" + "github.com/sigstore/fulcio/pkg/ca/baseca" "github.com/sigstore/fulcio/pkg/identity" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "go.step.sm/crypto/pemutil" ) const CAName = "fileCA" @@ -31,16 +38,57 @@ type FileCA struct { ca fulcioca.CertificateAuthority } -func New(certPath, keyPath, keyPass string, watch bool) (*FileCA, error) { - wrappedCa, err := fulciofileca.NewFileCA(certPath, keyPath, keyPass, watch) +func New(certPath, keyPath, keyPass string, verify bool) (*FileCA, error) { + var err error + baseCA := &baseca.BaseCA{} + baseCA.SignerWithChain, err = loadKeyPair(certPath, keyPath, keyPass) if err != nil { - return nil, fmt.Errorf("failed to create file CA: %w", err) + return nil, err } + + if verify { + // if the CA is a signer, verify the chain + chain, signer := baseCA.GetSignerWithChain() + if err := fulcioca.VerifyCertChain(chain, signer); err != nil { + return nil, err + } + } + return &FileCA{ - ca: wrappedCa, + ca: baseCA, }, nil } +func loadKeyPair(certPath, keyPath, keyPass string) (*fulcioca.SignerCerts, error) { + var ( + certs []*x509.Certificate + err error + key crypto.Signer + ) + + data, err := os.ReadFile(filepath.Clean(certPath)) + if err != nil { + return nil, err + } + certs, err = cryptoutils.LoadCertificatesFromPEM(bytes.NewReader(data)) + if err != nil { + return nil, err + } + + opaqueKey, err := pemutil.Read(keyPath, pemutil.WithPassword([]byte(keyPass))) + if err != nil { + return nil, err + } + + var ok bool + key, ok = opaqueKey.(crypto.Signer) + if !ok { + return nil, errors.New(`fileca: loaded private key can't be used to sign`) + } + + return &fulcioca.SignerCerts{Certs: certs, Signer: key}, nil +} + func (f FileCA) CreateCertificateFromCSR(ctx context.Context, principal identity.Principal, csr *x509.CertificateRequest) (*fulcioca.CodeSigningCertificate, error) { return f.ca.CreateCertificate(ctx, principal, csr.PublicKey) } diff --git a/go.mod b/go.mod index 78b164a28..fa7b8c58f 100644 --- a/go.mod +++ b/go.mod @@ -99,6 +99,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 github.com/zricethezav/gitleaks/v8 v8.30.0 gitlab.com/gitlab-org/security-products/analyzers/report/v5 v5.3.0 + go.step.sm/crypto v0.75.0 google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b google.golang.org/genproto/googleapis/bytestream v0.0.0-20251222181119-0a764e51fe1b ) @@ -302,7 +303,6 @@ require ( go.opentelemetry.io/otel/sdk v1.39.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect - go.step.sm/crypto v0.75.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect