open-vault/website/content/docs/secrets/pki/rotation-primitives.mdx

425 lines
22 KiB
Plaintext
Raw Normal View History

---
layout: docs
page_title: 'PKI - Secrets Engine: Rotation Primitives'
description: The PKI secrets engine for Vault generates TLS certificates.
---
# PKI Secrets Engine - Rotation Primitives
Since Vault 1.11.0, Vault's PKI Secrets Engine supports multiple issuers in a
single mount point. By using the certificate types below, rotation can be
accomplished in various situations involving both root and intermediate CAs
managed by Vault.
## X.509 Certificate Fields
X.509 is a complex specification; modern implementations tend to refer to
[RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280) for specific
details. For validation of certificates, both RFC 5280 and the TLS
validation [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125) are
important for understanding how to achieve rotation.
The following is a simplification of these standards for the purpose of
this document.
Every X.509 certificate begins with an asymmetric key pair, using an algorithm
like RSA or ECDSA. This key pair is used to create a Certificate Signing
Request (CSR), which contains a set of fields the requester would like in the
final certificate (but, it is up to the Certificate Authority (CA) to decide what
fields to take from the CSR and which to override). The CSR also contains the
public key of the pair, which is signed by the private key of the key pair to
prove possession. Usually, the requester would ask for attributes in the
Subject field of the CSR or in the Subject Alternative Name extension CSR to
be respected in the final certificate. It is up to the CA if these values are
trusted or not. When approved by the issuing authority (which may be backed by
this asymmetric key itself in the case of a root self-signed certificate), the
authority attaches the Subject of _its_ certificate to the issued certificate in
the Issuer field, assigns a unique serial number to the issued certificate, and
signs the set of fields with its private key, thus creating the certificate.
There are some important restrictions here:
- One certificate can only have one Issuer, but this issuer is identified by
the Subject on the issuing certificate and its public key.
- One key pair can be used for multiple certificates, but one certificate can
only have one backing key material.
The following fields on the final certificate are relevant to rotation:
- The backing [public](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.7)
and private key material (Subject Public Key Info).
- Note that the private key is not included in the certificate but is
uniquely determined by the public key material.
- The [Subject](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6) of the certificate.
- This identifies the entity to which the certificate was issued. While the
SAN values (in the [Subject Alternative Name](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6)
extension) is useful when validating TLS Server certificates against the
negotiated hostname and URI, it isn't generally relevant for the purposes
of validating intermediate certificate chains or in rotation.
- The [Validity](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5)
period of this certificate.
- Notably, RFC 5280 does not place any requirements around the issued
certificate's validity period relative to the validity period of the
issuing certificate. However, it [does state](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5)
that certificates ought to be revoked if their status cannot be maintained
up to their notAfter date. This is why Vault 1.11's `/pki/issuer/:issuer_ref`
configuration endpoint maintains the `leaf_not_after_behavior` per-issuer
rather than per-role.
- Additionally, some browsers will place ultimate trust in the certificates
in their trust stores, even when these certificates are expired.
- Note that this only applies to certificates in the trust store; validity
periods will still be enforced for certificates not in the store (such
as intermediates).
- The [Issuer](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4) and
[signatureValue](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.1.3)
of this certificate.
- In the issued certificate's Issuer field, the issuing certificate places
its own Subject value. This allows the issuer to be identified later
(without having to try signature validation against every known local
certificate), when validating the presented certificate and chain.
- The signature over the entire certificate (by the issuer's private key)
is then placed in the signatureValue field.
- The optional [Authority Key Identifier](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1)
field.
- This field can contain either (or both) of two values:
- The hash of the issuer's public key. This extension is set and this
value is filled in by Vault.
- The Issuer's Subject and Serial Number. This value is not set by Vault.
- The latter is a dangerous restriction for the purposes of rotation: it
prevents cross-signing and reissuance as the new issuing certificates
(while having the same backing key material) will have different serial
numbers. See the [Limitations of Primitives](#limitations-of-primitives)
section below for more information on this restriction.
- The [Serial Number](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.2)
of this certificate.
- This field is unique to a specific issuer; when a certificate is
reissued by its parent authority, it will always have a different serial
number field.
- The [CRL distribution](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13)
point field.
- This is a field detailing where a CRL is expected to exist for this
certificate and under which CRL issuers (defaulting to the issuing
certificate itself) the CRL is expected to be signed by. This is mostly
informational and for server software like nginx, Vault's Cert Auth method,
and Apache, CRLs are provided to the server, rather than having the
server fetch CRLs for certificates automatically.
- Note that root certificates (in browsers trust stores) are generally not
considered revocable. However, if an intermediate is revoked by serial,
it will appear on its parent's CRL, and may prevent rotation from
happening.
## X.509 Rotation Primitives
Rotation (from an organizational standpoint) can only safely happen with
certain intermediate X.509 certificates being issued. To distinguish the two
types of certificates used to achieve rotation, this document notates them
as _primitives_.
Rotation of an end-entity certificate is trivial from an X.509 trust chain
perspective; this process happens every day and should only depend on what is
in the trust store and not the end-entity certificate itself. In Vault, the
requester would hit the various issuance endpoints (`/pki/issue/:name` or
`/pki/sign/:name` -- or use the unsafe `/pki/sign-verbatim`) and swap out the
old certificate with the new certificate and reload the configuration or
restart the service. Other parts of the organizations might use
[ACME](https://datatracker.ietf.org/doc/html/rfc8555) for certificate issuance
and rotation, especially if the service is public-facing (and thus needs to
be issued by a Public CA). Given it was signed by a trusted root, any devices
connecting to the service would not know the difference.
Rotation of intermediate certificates is almost as easy. Assuming a decent
operational setup (wherein during end-entity issuance, the full certificate
chain is updated in the service's configuration), this should be as easy as
creating a new intermediate CA, signing it against the root CA, and then
beginning issuance against the new intermediate certificate. In Vault, if
the intermediate is generated in an existing mount path (or is moved into
such), the requesting entity shouldn't care much. Under ACME, Let's Encrypt
has successfully rotated intermediates to present a cross-signed chain
([for older Android devices](https://letsencrypt.org/2020/12/21/extending-android-compatibility.html)).
Assuming the old intermediate's parent(s) are still valid and trusted,
certificates issued under old intermediates should continue to validate.
The hard part of rotation--calling for the use of these primitives--is
rotating root certificates. These live in every device's trust store and
are hard to update from an organization-wide operational perspective.
Unless the organization can swap out roots almost instantaneously and
simultaneously (e.g., via an agent) with no missed devices, this process
will likely span months.
To make this process lower risk, there are various primitive certificate
types that use the [above certificate fields](#x-509-certificate-fields).
Key to their success is the following note:
~> Note: While certificates are added to the trust store, it is ultimately
the associated key material that determines trust: two issuer certificates
with the same subject but different public keys cannot validate the same
leaf certificate; only if the keys are the same can this occur.
### Cross-Signed Primitive
This is the most common type of rotation primitive. A common CSR is signed by
two CAs, resulting in two certificates. These certificates must have the same
Subject (but may have different Issuers and will have different Serial Numbers)
and the same backing key material, to allow certificates they sign to be
trusted by either variant.
Note that, due to restrictions in how end-entity certificates are used and
validated (services and validation libraries expect only one), cross-signing
most typically only applies to intermediate.
#### Cross-Signed Roots
Technically, cross-signing can occur between two roots, allowing trust bundles
with either root to validate certs issued through the other. However, this
process creates a certificate that is effectively an intermediate (as it is
no longer self-signed) and usually must be served alongside the trust chain.
Given this restriction, it's preferable to instead cross-sign the top-level
intermediates under the root unless strictly necessary when the old root
certificate has been used to directly issue leaf certificates.
##### Process Flow
```
-------------------
| generate key pair | -------------> ...
------------------- ...
| | ...
-------------- -------------- ...
| generate CSR | | generate CSR | ...
-------------- -------------- ...
| | ...
----------- ----------- ...
| signed by | | signed by | ...
| root A | | root B | ...
----------- ----------- ...
```
Here, a key pair was generated at some point in time. Two CSRs are created and
sent to two different root authorities (Root A and Root B). These result in two
separate certificates (potentially with different validity periods) with the
same Subject and same backing key material.
Note that this cross-signing need not happen simultaneously; there could be a
gap of several years between the first and second certificate. Additionally,
there's no limit on the number of cross-signed "duplicate" (used loosely--with
the same subject and key material) certificates: this could be cross-signed
by many different root certificates if necessary and desired.
##### Certificate Hierarchy
```
-------- --------
| root A | | root B |
-------- --------
| |
---------------- ----------------
| intermediate C | <- same key material -> | intermediate D |
---------------- | ----------------
|
-------------------
| leaf certificates |
-------------------
```
The above process results in two trust paths: either of root A or root B (or
both) could exist in the client's trust stores and the leaf certificate would
validate correctly. Because the same key material is used for both intermediate
certificates (C and D), the issued leaf certificate's signature field would
be the same regardless of which intermediate was contacted.
Cross-signing is thus a unifying primitive; two separate trust paths now join
into a single one, by having leaf certificate's issuer field to point to two
separate paths (via duplication of the certificate in the chain) and would be
conditionally validated based on which root is present in the trust store.
This construct is documented and used in several places:
- https://letsencrypt.org/certificates/
- https://scotthelme.co.uk/cross-signing-alternate-trust-paths-how-they-work/
- https://security.stackexchange.com/questions/14043/what-is-the-use-of-cross-signing-certificates-in-x-509
#### Execution in Vault
To create a cross-signed certificate in Vault, use the [`/intermediate/cross-sign`
endpoint](/api-docs/secret/pki#generate-intermediate-csr). Here, when creating
a cross-signature to all `cert B` to be validated by `cert A`, provide the values
(`key_ref`, all Subject parts, &c) for `cert B` during intermediate generation.
Then sign this CSR (using the [`/issuer/:issuer_ref/sign-intermediate`
endpoint](/api-docs/secret/pki#sign-intermediate)) with `cert A`'s reference
and provide necessary values from `cert B` (e.g., Subject parts). `cert A` may
live outside Vault. Finally, import the cross-signed certificate into Vault
[using the `/issuers/import/cert` endpoint](/api-docs/secret/pki#import-ca-certificates-and-keys).
If this process succeeded, and both `cert A` and `cert B` and their key
material lives in Vault, the newly imported cross-signed certificate
will have a `ca_chain` response field [during read](/api-docs/secret/pki#read-issuer)
containing `cert A`, and `cert B`'s `ca_chain` will contain the cross-signed
cert and its `ca_chain` value.
~> Note: Regardless of issuer type, is important to provide all relevant
parameters as they were originally; Vault does not infer e.g., the Subject
name parameters from the existing issuer; it merely reuses the same key
material.
### Reissuance Primitive
The second most common type of rotation primitive. In this scheme, the existing
key material is used to generate a new certificate, usually at a much later
point in time from the existing issuance.
While similar to the cross-signed primitive, this one differs in that usually
the reissuance happens after the original certificate expires or is close to
expiration and is reissued by the original root CA. In the event of a
self-signed certificate (e.g., a root certificate), this parent certificate
would be itself. In both cases, this changes the contents of the certificate
(due to the new serial number) but allows all existing leaf signatures to
still validate.
Unlike the cross-signed primitive, this primitive type can be used on all
types of certificates (including leaves, intermediates, and roots).
#### Process Flow
```
-------------------
| generate key pair | ---------------> ...
------------------- ...
| | ...
-------------- -------------- ...
| generate CSR | <-> | generate CSR | ...
-------------- -------------- ...
| | ...
------------------ ------------------ ...
| signed by issuer | -> | signed by issuer | -> ...
------------------ ------------------ ...
```
In this process flow, a single key pair is generated at some point in time
and stored. The CSR (with same requested fields) is generated from this
common key material and signed by the same issuer at multiple points in
time, preserving all critical fields (Subject, Issuer, &c). While there is
strictly no limit on the number of times a key can be reissued, at some point
safety would dictate the key material should be rotated instead of being
continually reissued.
#### Certificate Hierarchy
```
------
-----------| root |-------------
/ ------ \
| |
--------------- ---------------
| original cert | <- same key material -> | reissued cert |
--------------- | ---------------
|
-------------------
| leaf certificates |
-------------------
```
Note that while this again results in two trust paths, depending on which
intermediate certificate is presented and is still valid, only a root need be
trusted. When a reissued certificate is a root certificate, the issuance link is
simply self-loop. But, in this case, note that both certificates are
(technically) valid issuers of each other. This means it should be possible to
provide a reissued root certificate in the TLS certificate chain and have it
chain back to an existing root certificate in a trust store.
This primitive type is thus an incrementing primitive; the life cycle of an
existing key is extended into the future by issuing a new certificate with the
same key material from the existing authority.
#### Execution in Vault
To create a reissued root certificate in Vault, use [`/issuers/generate/root/existing`
endpoint](/api-docs/secret/pki#generate-root). This allows the generation of a new
root certificate with the existing key material (via the `key_ref` request parameter).
If this process succeeded, when [reading the issuer](/api-docs/secret/pki#read-issuer)
(via `GET /issuer/:issuer_ref`), both issuers (old and reissued) will appear in
each others' `ca_chain` response field (unless prevented so by a `manual_chain`
value).
To create a reissued intermediate certificate in Vault, this is a three step
process:
1. Use the [`/issuers/generate/intermediate/existing`
endpoint](/api-docs/secret/pki#generate-intermediate-csr)
to generate a new CSR with the existing key material with the `key_ref`
request parameter.
2. Sign this CSR via the same signing process under the same issuer. This
step is specific to the parent CA, which may or may not be Vault.
3. Finally, use the [`/intermediate/set-signed` endpoint](/api-docs/secret/pki#import-ca-certificates-and-keys)
to import the signed certificate from step 2.
If the process to reissue an intermediate certificate succeeded, when
[reading the issuer](/api-docs/secret/pki#read-issuer) (via
`GET /issuer/:issuer_ref`), both issuers (old and reissued) will have
the same `ca_chain` response field, except for the first entry (unless
prevented so by a `manual_chain` value).
~> Note: Regardless of issuer type, is important to provide all relevant
parameters as they were originally; Vault does not infer e.g., the Subject
name parameters from the existing issuer; it merely reuses the same key
material.
### Temporal Primitives
We can use the above primitive types to rotate roots and intermediates to new
keys and extend their lifetimes. This time-based rotation is what ultimately
allows us to rotate root certificates.
There's two main variants of this: a **forward** primitive, wherein an old
certificate is used to bless new key material, and a **backwards** primitive,
wherein a new certificate is used to bless old key material. Both of these
primitives are independently used by Let's Encrypt in the aforementioned
chain of trust document:
- The link from DST Root CA X3 to ISRG Root X1 is an example of a forward
primitive.
- The link from ISRG Root X1 to R3 (which was originally signed by DST Root
CA X3) is an example of a backwards primitive.
For most organizations with a hierarchical structured CA setup, cross-signing
all intermediates with both the new and old root CAs is sufficient for root
rotation.
However, for organizations which have directly issued leaf certificates from a
root, the old root will need to be reissued under the new root (with shorter
duration) to allow these certificates to continue to validate. This combines
both of the above primitives (cross-signing and reissuance) into a single
backwards primitive step. In the future, these organizations should probably
move to a more standard, hierarchical setup.
### Limitations of Primitives
The certificate's [Authority Key Identifier](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1)
extension field may contain either or both of the issuer's keyIdentifier
(a hash of the public key) or both the issuer's Subject and Serial Number
fields. Generating certificates with the latter enabled (luckily not possible
in Vault, especially so since Vault uses strictly random serial numbers)
prevents building a proper cross-signed chain without re-issuing the same
serial number, which will not work with most browsers' trust stores and
validation engines, due to [caching of
certificates](https://support.mozilla.org/en-US/kb/Certificate-contains-the-same-serial-number-as-another-certificate)
used in successful validations. In the strictest sense, when using a
cross-signing primitive (from a different CA), the intermediate could be reissued
with the same serial number, assuming no previous certificate was issued by that
CA with that serial. This does not work when using a reissuance primitive as these
are technically the same authority and thus this authority must issue
certificates with unique serial numbers.
## Learn
Refer to the [Build Your Own Certificate Authority (CA)](https://learn.hashicorp.com/vault/secrets-management/sm-pki-engine)
guide for a step-by-step tutorial.
Have a look at the [PKI Secrets Engine with Managed Keys](https://learn.hashicorp.com/tutorials/vault/managed-key-pki?in=vault/enterprise)
for more about how to use externally managed keys with PKI.
## API
The PKI secrets engine has a full HTTP API. Please see the
[PKI secrets engine API](/api-docs/secret/pki) for more
details.