Keep a CA's key in AWS KMS
A CA configured with a key store has its key pair created inside an HSM, and the private key never leaves it. Strongbox stores a key reference and delegates every signature to the key store. This guide sets up such a CA backed by an asymmetric AWS KMS key, which is what a Control Tower hosted in AWS should use.
The concepts are covered in Hardware-backed CA keys, and everything a CA does apart from where its key lives is covered in SSL/TLS CA.
Prerequisites
The Control Tower needs AWS credentials that may use KMS. With the
default credentials: instance-role, the EC2 instance role (via
instance metadata) is used; credentials: environment reads
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and optionally
AWS_SESSION_TOKEN from the Control Tower's environment.
The role or user needs:
kms:CreateKey, unless you adopt an existing key withkey-idkms:GetPublicKeyandkms:Signon the CA keys
Scope the Sign permission to the CA keys (for example by tag or by
ARN once created) rather than granting it account-wide: whoever may
sign with the key may issue certificates.
Create the CA
supctl create strongbox tls ca <<EOF
name: hardware-root
subject: Example Root CA
ttl: 3650d
distribute:
to: all
key-store:
aws-kms:
region: eu-north-1
EOF
Strongbox creates an asymmetric SIGN_VERIFY key in KMS matching the
CA's key type (ECDSA P-256 by default; P-384/P-521 and RSA are also
supported), builds the self-signed root around the key store's public
key, and stores only the key ARN.
Distribution is cert-only for a key-store CA: sites receive the certificate, chain and revocations (and serve CRL/OCSP for the root locally), but never the key reference. Enrollment against the CA at a site is refused with a pointer to the Control Tower; issue an intermediate for site-side issuance.
To adopt a key you have already created (for example one provisioned by your security team, or held in a KMS custom key store backed by CloudHSM), name it instead of letting Strongbox create one:
key-store:
aws-kms:
region: eu-north-1
key-id: arn:aws:kms:eu-north-1:111122223333:key/1234abcd-...
The key must be an asymmetric SIGN_VERIFY key whose spec matches the
CA's cert-key-type.
In a VPC without internet egress, point the CA at your KMS VPC
endpoint with endpoint: <hostname>.
Issue intermediates for the sites
The root itself never leaves the Control Tower, so day-to-day issuance at sites goes through an ordinary intermediate:
supctl create strongbox tls ca <<EOF
name: site-issuing
issuing-ca: hardware-root
distribute:
to: all
EOF
The intermediate has a normal Strongbox-held key and behaves exactly like any other CA: it is distributed, applications get their certificates from it through vault secrets, and devices can enroll against it with EST or SCEP. The root's certificate is embedded in the intermediate's chain, so sites can build and serve the full chain even though the root itself is not distributed; a relying party can anchor its trust at the root certificate or at the intermediate. This split also keeps the HSM out of the hot path: KMS signs intermediates and the root's CRLs, not every issued certificate.
Verify
supctl show strongbox tls ca hardware-root
shows the CA certificate and its key-store configuration; there is
no private key to show.
Watch how the key is used
Every key store operation is written to the audit trail of the tenant
owning the CA (system:audit-trail-log) as a key-store-operation
event: which operation, which key, which CA, the outcome and how long
the key store took. The same operations appear in CloudTrail with the
calling identity, so the two logs can be reconciled: a Sign in
CloudTrail that the audit trail does not account for is a signature
made outside the platform.
The Control Tower also watches the pattern itself and raises a
key-store-anomaly alert on system:alerts when
- the key signs more often than
max-signatures-per-minuteallows, measured over a five minute window, or - the key store rejects several operations in a row, which is what a revoked credential, a disabled key or a changed key policy looks like from the platform side.
The rate limit is per CA and defaults to 20 signatures per minute:
key-store:
max-signatures-per-minute: 5
aws-kms:
region: eu-north-1
A root that only signs intermediates and CRLs can be set low, which is
what makes the alert meaningful. Raise it for a key store CA that
issues end entity certificates directly, or set it to 0 to turn the
rate check off; the failure check stays on either way. The limit can be
changed on a running CA with supctl merge; it takes effect
immediately.
Revocation
Revocation works as for any CA: revoke-cert on the root takes effect
in its CRL, which KMS signs. Relying parties fetch the CRL from the
advertised distribution points as usual.
Serving revocation status does not put KMS in the request path. The
CRL is signed when it changes and then served from cache until it
nears its next update, and OCSP is answered by a delegated responder:
a key held by the Control Tower, certified by the CA for OCSP signing
only (RFC 6960), reissued about once a week. So a fleet polling the
CRL distribution point or the OCSP responder costs no KMS calls, which
also keeps that traffic from tripping max-signatures-per-minute.
Sites serve the CRL too. They receive the signed list along with the certificate and the revocations, so revocation checking at the edge keeps working while a site is cut off from the Control Tower. That is signed data, not a signing capability: no key of any kind leaves the Control Tower, and a site refuses rather than serves a list whose next update has passed. OCSP is answered by the Control Tower only.
Planning revocation checking
Status for a certificate always comes from whoever issued it, so with a hardware-backed root and an intermediate per site the two levels are served in different places.
| Certificate | Status comes from | Served at the site | Works disconnected |
|---|---|---|---|
| Issued by a site's intermediate | that intermediate | CRL and OCSP | yes |
| The intermediate itself | the root | CRL only | yes, over CRL |
Certificates carry both the Control Tower's address and
api.internal, the per-site name, in their distribution points and
AIA, so a relying party running at a site reaches a local endpoint
without being configured for it.
What follows from the table:
- Checking the certificates your applications and devices actually present is fully local, over either mechanism, and survives a site losing contact with the Control Tower.
- Checking the intermediate itself is local over CRL. A relying party
that insists on OCSP for the whole chain reaches the Control Tower,
and that check is what breaks while a site is cut off. Either allow
CRL for the chain, or check only the leaf, which is what most TLS
stacks do by default (
openssl verify -crl_checkinspects the leaf;-crl_check_allwalks the chain). - OCSP stapling avoids the question for the leaf: the server fetches its own status from the local intermediate and staples it, so the client asks nobody. Stapling the intermediate's status as well needs multi-stapling (RFC 6961), which few clients support.
An intermediate per site also gives you a way to cut a site off: revoke its intermediate, and everything it ever issued stops validating. That revocation lives in the root's CRL, which every site serves. Note what that means for a site you no longer trust: it cannot forge the list, but it can serve the copy it already has until that copy expires. The window is the CRL's validity, since a site refuses to serve an expired list. Shorten the root's CRL lifetime, or point critical relying parties at the Control Tower's URL, if that window is too wide for you.
Auto-unseal with a seal key store
The same key-store pattern protects the seal key. With a
seal-key-store block in the Control Tower's site settings, the
unseal-token shares are wrapped by a symmetric KMS key and the
Control Tower unseals itself at boot, with no operator interaction:
seal-key-store:
aws-kms:
region: eu-north-1
key-id: arn:aws:kms:eu-north-1:111122223333:key/abcd-...
Be explicit about the trade: unsealing now requires only the instance credentials, so the protection boundary is your IAM policy, and every wrap and unwrap appears in CloudTrail. A key-store failure is an error, never a downgrade to TPM or plaintext protection. Shamir recovery shares remain available as the fallback path.
Backup and disaster recovery
The private key is not part of Control Tower backups, by design: it cannot be exported. Protecting it is the key store's job.
- For disaster recovery across regions, use a KMS multi-region key and configure the replica region in the restored Control Tower.
- A restored Control Tower must run with credentials that reach the same KMS keys. A backup restored outside that boundary has a CA certificate but no signing capability; that this cannot be circumvented is the point of using an HSM.
- Deleting the KMS key permanently disables the CA. Use KMS deletion protection windows and treat scheduled key deletion as the destructive operation it is.
Current limitations
- Rotation and automatic renewal create a new key inside the key store for each new CA version; old versions keep their keys until the versions are retired.
- ML-DSA key types are not yet supported for key-store CAs.
- The key store is configured per CA. If several CAs should share one configuration, configure each explicitly.
- Sites serve the root's CRL but not its OCSP responder, so a relying party that requires OCSP for every certificate in the chain depends on reaching the Control Tower.