Skip to content

[Bug]: cloudCAS GetCertificateAuthority doesn't return IntermediateCertificates, breaking SCEP in RA mode #2509

Description

@civicactionsbot

Steps to Reproduce

  1. Set up GCP Certificate Authority Service with a subordinate (intermediate) CA
  2. Configure step-ca in RA mode with cloudCAS:
{
  "authority": {
    "type": "cloudcas",
    "certificateAuthority": "projects/my-project/locations/us-central1/caPools/my-pool/certificateAuthorities/my-intermediate-ca",
    "config": {
      "credentialsFile": "/path/to/gcp-sa-key.json"
    },
    "provisioners": [
      {
        "type": "SCEP",
        "name": "scep",
        "challenge": "secret",
        "minimumPublicKeyLength": 2048,
        "encryptionAlgorithmIdentifier": 2,
        "decrypterCertificate": "-----BEGIN CERTIFICATE-----\n...",
        "decrypterKeyPEM": "-----BEGIN RSA PRIVATE KEY-----\n..."
      }
    ]
  }
}
  1. Start step-ca

Your Environment

  • OS - macOS (also reproducible on Linux via Helm chart)
  • step-ca Version - 0.29.0

Expected Behavior

step-ca should start successfully with SCEP provisioner in cloudCAS RA mode, similar to how VaultCAS works after the fix in PR #1803.

Actual Behavior

step-ca panics on startup:

panic: runtime error: index out of range [0] with length 0

goroutine 1 [running]:
github.com/smallstep/certificates/authority.(*Authority).init(0x14000732280)
        /path/to/certificates/authority/authority.go:760 +0x...

The panic occurs because a.intermediateX509Certs is empty when initializing the SCEP authority.

In cas/cloudcas/cloudcas.go, the GetCertificateAuthority() function only returns RootCertificate but not IntermediateCertificates:

func (c *CloudCAS) GetCertificateAuthority(req *apiv1.GetCertificateAuthorityRequest) (*apiv1.GetCertificateAuthorityResponse, error) {
    // ...
    // Last certificate in the chain is the root.
    root, err := parseCertificate(resp.PemCaCertificates[len(resp.PemCaCertificates)-1])
    if err != nil {
        return nil, err
    }

    return &apiv1.GetCertificateAuthorityResponse{
        RootCertificate: root,
        // IntermediateCertificates is NOT populated!
    }, nil
}

GCP CAS does provide the full certificate chain in PemCaCertificates (verified via gcloud privateca subordinates describe), but cloudCAS doesn't extract the intermediate certificates.

This was fixed for VaultCAS in PR #1803, but cloudCAS was not updated.

Additional Context

Suggested fix would be to update GetCertificateAuthority() in cas/cloudcas/cloudcas.go to parse and return intermediate certificates, similar to what was done for VaultCAS in #1803:

func (c *CloudCAS) GetCertificateAuthority(req *apiv1.GetCertificateAuthorityRequest) (*apiv1.GetCertificateAuthorityResponse, error) {
    // ... existing code to get resp ...

    // Last certificate in the chain is the root.
    root, err := parseCertificate(resp.PemCaCertificates[len(resp.PemCaCertificates)-1])
    if err != nil {
        return nil, err
    }

    // Parse intermediate certificates (all except the last/root)
    var intermediates []*x509.Certificate
    for i := 0; i < len(resp.PemCaCertificates)-1; i++ {
        cert, err := parseCertificate(resp.PemCaCertificates[i])
        if err != nil {
            return nil, err
        }
        intermediates = append(intermediates, cert)
    }

    return &apiv1.GetCertificateAuthorityResponse{
        RootCertificate:          root,
        IntermediateCertificates: intermediates,
    }, nil
}

Contributing

Vote on this issue by adding a 👍 reaction.
To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

Activity

  1. grugnog commented on Jan 1, 2026

    @grugnog

    Just noticed this was opened using the wrong account - it's actually me (not a bot!) opening this :)

  2. self-assigned this
    on Jan 5, 2026
  3. hslatman commented on Jan 6, 2026

    @hslatman
    Member

    Hey @grugnog, thank you for reporting the issue. I opened a PR with the fix based on your suggestion: #2517. Can't currently test it myself with a CAS instance unfortunately, but it should be good to go.

    The SCEP functionality assumes the first intermediate is used as the signer (and decrypter, by default), so there could be scenarios in which you'll need a separate SCEP decrypter too. Might be good to give the changes a quick test, if you haven't done so already to make sure it works as expected with your configuration.

  4. added this to the v0.29.1 milestone on Jan 6, 2026
  5. grugnog commented on Jan 9, 2026

    @grugnog

    @hslatman thanks so much! I will test this PR out when I get a change and share notes there.

  6. grugnog commented on Jan 11, 2026

    @grugnog

    @hslatman confirmed this is working - was able to issue SCEP certs. Thanks you!

  7. hslatman commented on Jan 12, 2026

    @hslatman
    Member

    Awesome 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugneeds triageWaiting for discussion / prioritization by team

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions