Build your own certificate authority (CA)
- 58min
- |
- VaultVault
- VideoVideo
- InteractiveInteractive
Deployment
Vault's PKI secrets engine can dynamically generate X.509 certificates on demand. Services can request certificates without going through a manual process of generating a private key and Certificate Signing Request (CSR), submitting to a Certificate Authority (CA), and then waiting for the verification and signing process to complete.
Personas
The steps described in this tutorial are typically performed by a security engineer.
Challenge
Organizations should protect their website, but the traditional PKI process workflow takes a long time, which motivates organizations to create certificates which do not expire for a year or more.
Solution
Use Vault to create X.509 certificates for usage in Mutual Transport Layer Security (MTLS) or other arbitrary PKI encryption. You can use this solution to create web server certificates, but if users do not import the CA chains, browsers do complain about the self-signed certificates.
Creating PKI certificates is generally a cumbersome process using traditional
tools like openssl
or even more advanced frameworks like Cloudflare's PKI and TLS toolkit
(CFSSL). These tools also require human effort to verify certificate distribution meets
organizational security policies.
Vault's PKI secrets engine makes this a lot simpler. The PKI secrets engine can be an intermediate-only certificate authority, which potentially allows for higher levels of security.
- Store CA outside of Vault (air-gapped).
- Create CSRs for the intermediate CA.
- Sign CSR outside Vault and import intermediate CA.
- Issue leaf certificates from the Intermediate CA.
Refer to Build Certificate Authority (CA) in Vault with an offline Root for an example of using a root CA external to Vault.
Prerequisites
To perform the tasks described in this tutorial, you need:
A Vault environment. Refer to the Vault install guide to install Vault.
The API and CLI versions of the example scenario use the
jq
tool to parse JSON output. Installjq
in your Vault environment to follow the examples which use this tool.The web UI OpenSSL tool is used for some parts of the Web UI version of this tutorial.
Launch Terminal
This tutorial includes a free interactive command-line lab that lets you follow along on actual cloud infrastructure.
Policy requirements
Note
For the purposes of this tutorial, you can use a root
token to
work with Vault. However, it is recommended that root tokens are used just for initial setup or in emergencies. As a best practice, use tokens
with appropriate set of policies based on your role in the organization.
To perform all tasks demonstrated in this tutorial, your policy must include the following capabilities:
# Enable secrets engine
path "sys/mounts/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# List enabled secrets engine
path "sys/mounts" {
capabilities = [ "read", "list" ]
}
# Work with pki secrets engine
path "pki*" {
capabilities = [ "create", "read", "update", "delete", "list", "sudo", "patch" ]
}
If you are not familiar with policies, complete the policies tutorial.
Scenario introduction
In this tutorial, you are going to first generate a self-signed root certificate.
Then you are going to generate an intermediate certificate that is signed by
the root. You can then create a role and generate a certificate for the test.example.com
domain.
You can choose to complete this tutorial at this point or you can continue with the advanced section on multiple issuer functionality beginning with step 7.
There, you'll learn how enable a second root CA in the same secrets engine mount. You also learn how to rotate the older root CA, and begin issuing certificates from the existing role with the new root CA. Finally, you learn how to sunset the older CA.
In this tutorial, you perform the following operations:
- Generate Root CA
- Generate Intermediate CA
- Create a Role
- Request Certificates
- Revoke Certificates
- Remove Expired Certificates
- Enable new Root CA (optional)
- Create a Root Bridge CA (optional)
- Create a cross-signed intermediate (optional)
- Set default issuer and issue certificate (optional)
- Sunset older Root CA (optional)
Notice about multi-issuer functionality
Vault version 1.11.0 and greater allows a single PKI mount to have many Certificate Authority (CA) certificates ("issuers") in a single mount. All issuers within a single mount get treated as a single Authority, meaning that:
- Certificate Revocation List (CRL) configuration is common to all issuers/
- All authority access URLs are common to all issuers.
- Issued certificates' serial numbers are unique across all issuers.
However, since each issuer may have a distinct subject and keys, different issuers may have different CRLs.
It is strongly encouraged to limit the scope of CAs within a mount and not to mix different types of CAs (roots and intermediates).
Note
Some functionality does not work if a default issuer is not configured. Vault automatically selects the default issuer from the current issuing certificate on migration from an older Vault version (Vault < 1.11.0).
Lab setup
Tip
If you do not have access to an HCP Vault Dedicated cluster, visit the Create a Vault Cluster on HCP tutorial.
Launch the HCP Portal and login.
Click Vault in the left navigation pane.
In the Vault clusters pane, click vault-cluster.
Under Cluster URLs, click Public Cluster URL.
In a terminal, set the
VAULT_ADDR
environment variable to the copied address.$ export VAULT_ADDR=<Public_Cluster_URL>
Return to the Overview page and click Generate token.
Within a few moments, a new token is generated.
Copy the Admin Token.
Return to the terminal and set the
VAULT_TOKEN
environment variable.$ export VAULT_TOKEN=<token>
Set the
VAULT_NAMESPACE
environment variable toadmin
.$ export VAULT_NAMESPACE=admin
The
admin
namespace is the top-level namespace automatically created by HCP Vault. All CLI operations default to use the namespace defined in this environment variable.Type
vault status
to verify your connectivity to the Vault cluster.$ vault status Key Value --- ----- Recovery Seal Type shamir Initialized true Sealed false Total Recovery Shares 1 Threshold 1 Version 1.13.0+ent Storage Type raft ...snipped...
The Vault Dedicated server is ready.
Step 1: generate root CA
In this step, you are going to generate a self-signed root certificate using PKI secrets engine.
Enable the
pki
secrets engine at thepki
path.$ vault secrets enable pki
Example output:
Success! Enabled the pki secrets engine at: pki/
Tune the
pki
secrets engine to issue certificates with a maximum time-to-live (TTL) of 87600 hours.$ vault secrets tune -max-lease-ttl=87600h pki
Example output:
Success! Tuned the secrets engine at: pki/
Generate the example.com root CA, give it an issuer name, and save its certificate in the file
root_2023_ca.crt
.$ vault write -field=certificate pki/root/generate/internal \ common_name="example.com" \ issuer_name="root-2023" \ ttl=87600h > root_2023_ca.crt
This command is not expected to produce any output.
This generates a new self-signed CA certificate and private key. Vault automatically revokes the generated root at the end of its lease period (TTL). The CA certificate signs its own Certificate Revocation List (CRL).
List the issuer information for the root CA.
$ vault list pki/issuers/ Keys ---- 09c2c9a0-a874-36d2-de85-d79a7a51e373
Copy this key, you'll use this key in the next step.
You can read the issuer with its ID to get the certificates and other metadata about the issuer. Let's skip the certificate output, but list the issuer metadata and usage information.
$ vault read pki/issuer/$(vault list -format=json pki/issuers/ | jq -r '.[]') \ | tail -n 6
Example output:
leaf_not_after_behavior err manual_chain <nil> ocsp_servers [] revocation_signature_algorithm SHA256WithRSA revoked false usage crl-signing,issuing-certificates,ocsp-signing,read-only
Create a role for the root CA. Creating this role allows for specifying an issuer when necessary for the purposes of this scenario. This also provides a simple way to transition from one issuer to another by referring to it by name.
$ vault write pki/roles/2023-servers allow_any_name=true Success! Data written to: pki/roles/2023-servers
Example output:
Key Value --- ----- allow_any_name true allow_bare_domains false allow_glob_domains false allow_ip_sans true allow_localhost true allow_subdomains false allow_token_displayname false allow_wildcard_certificates true allowed_domains [] allowed_domains_template false allowed_other_sans [] allowed_serial_numbers [] allowed_uri_sans [] allowed_uri_sans_template false allowed_user_ids [] basic_constraints_valid_for_non_ca false client_flag true cn_validations [email hostname] code_signing_flag false country [] email_protection_flag false enforce_hostnames true ext_key_usage [] ext_key_usage_oids [] generate_lease false issuer_ref default key_bits 2048 key_type rsa key_usage [DigitalSignature KeyAgreement KeyEncipherment] locality [] max_ttl 0s no_store false not_after n/a not_before_duration 30s organization [] ou [] policy_identifiers [] postal_code [] province [] require_cn true server_flag true signature_bits 256 street_address [] ttl 0s use_csr_common_name true use_csr_sans true use_pss false
Note
This role is overly permissive in the names that it allows just for the purposes of simplification in this tutorial.
Configure the CA and CRL URLs.
$ vault write pki/config/urls \ issuing_certificates="$VAULT_ADDR/v1/pki/ca" \ crl_distribution_points="$VAULT_ADDR/v1/pki/crl"
Example output:
Key Value --- ----- crl_distribution_points [http://127.0.0.1:8200/v1/pki/crl] enable_templating false issuing_certificates [http://127.0.0.1:8200/v1/pki/ca] ocsp_servers []
Note
When using multiple issuers in the same mount, it is suggested to use the per-issuer AIA fields rather than the global (
/config/urls
) variant. This is for correctness: these fields are used for chain building and automatic CRL detection in certain applications. If they point to the wrong issuer's information, these applications may break.
Enable the
pki
secrets engine atpki
path using/sys/mounts
endpoint.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data '{"type":"pki"}' \ $VAULT_ADDR/v1/sys/mounts/pki
See the documentation for all available parameters to enable secrets engines.
Tune the
pki
secrets engine to issue certificates with a maximum time-to-live (TTL) of 87600 hours.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data '{"max_lease_ttl":"87600h"}' \ $VAULT_ADDR/v1/sys/mounts/pki/tune
Create an API request payload containing the common name to set.
$ tee payload.json <<EOF { "common_name": "example.com", "issuer_name": "root-2023", "ttl": "87600h" } EOF
Generate the root certificate and extract the CA certificate and save it as
root_2023_ca.crt
.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data @payload.json \ $VAULT_ADDR/v1/pki/root/generate/internal \ | jq -r ".data.certificate" > root_2023_ca.crt
This generates a new self-signed CA certificate and private key. Vault automatically revokes the generated root at the end of its lease period (TTL). The CA certificate signs its own Certificate Revocation List (CRL).
List the issuers.
$ curl \ --silent \ --header "X-Vault-Request: true" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --header "X-Vault-Token: $(vault print token)" \ "$VAULT_ADDR"/v1/pki/issuers\?list=true \ | jq
Example output:
{ "request_id": "65f2151a-7b22-b00e-8ae5-b0d5c23e7644", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "key_info": { "09c2c9a0-a874-36d2-de85-d79a7a51e373": { "is_default": true, "issuer_name": "root-2023" } }, "keys": [ "09c2c9a0-a874-36d2-de85-d79a7a51e373" ] }, "wrap_info": null, "warnings": null, "auth": null }
Read the issuer with its ID to get the certificates and other metadata about the issuer. Let's skip the certificate output, but list the issuer metadata and usage information.
$ curl \ --silent \ --header "X-Vault-Token: $(vault print token)" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --header "X-Vault-Request: true" \ $VAULT_ADDR/v1/pki/issuer/09c2c9a0-a874-36d2-de85-d79a7a51e373 \ | jq
Example output:
{ "request_id": "dff49c60-125e-79f2-5ca0-c1165a196ed9", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "ca_chain": [ "-----BEGIN CERTIFICATE-----\nMIIDNTCCAh2gAwIBAgIUa1fnttKmxiB1kV8EaWHAVqe1mrEwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTgyMDIzWhcNMzIw\nNjE3MTgyMDUyWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBALhtn4dJrslD2s00tt9yilRCmtS3/kJWHu/wjkBF\nkCUQ4eba/PhXKHbSntD+XVV+FYZsLLOpvSxJ3htR1ebaXKFbS6VWZRmEtbfmnYoh\nyZZFC0eK+82xa/DDuJsdGa6pMaeFA7RV50LuICZoHDBzlj416HUih8v3wEqdcD16\nb7Pu1Oro3qcj/xBQ9HOQ8qTT/FyWrUgWnk/N6y7BX2fpWYH91aiNjov2QzuFTOer\n8NBJTjqbfIJK5xb9lmuKJZ2P0715UJYhAtdgJaEcOd5pzQw6Wutnh/0Z93ryQozJ\n9Q67+XgRNCDsG6CpfVIc40x7nvC0s0EUCM/hwlgf1tdEBWcCAwEAAaN7MHkwDgYD\nVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGsBBdaz20yO\nL3NvzkcI5UU0eHWrMB8GA1UdIwQYMBaAFGsBBdaz20yOL3NvzkcI5UU0eHWrMBYG\nA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCztaw07tp3\nN7oRV2eiHYnvS0r3667IWqYz0T7+epnN5YuOXSNzjgB8Zbytk2mMKDCLg5mczITi\n/naybO9dczbx2SD9FhQwR3vjy+ykcI5NLjgjQ7EZZO6tyS8ZOjK9igrcp+5XG9fw\nkmp9q64em5ZKYwhR6OP2VgUrgn6MR40BFvhRA5oXH6M+UmYebWNqBB8R2z0BTf+U\nOeO9jpsLDVuhQlis91TKJXTZS6MuZwJB5e8H5/foQXKrrprmKZf2nn95qUApwn7H\nd6y6jaa9zBu8w+HZsamZM9Qi93Vg/5XoGcuTm4XE9zcM2DKPtn79TlrElpdXEH+/\nbs7CfJ5Phq5t\n-----END CERTIFICATE-----\n" ], "certificate": "-----BEGIN CERTIFICATE-----\nMIIDNTCCAh2gAwIBAgIUa1fnttKmxiB1kV8EaWHAVqe1mrEwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTgyMDIzWhcNMzIw\nNjE3MTgyMDUyWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBALhtn4dJrslD2s00tt9yilRCmtS3/kJWHu/wjkBF\nkCUQ4eba/PhXKHbSntD+XVV+FYZsLLOpvSxJ3htR1ebaXKFbS6VWZRmEtbfmnYoh\nyZZFC0eK+82xa/DDuJsdGa6pMaeFA7RV50LuICZoHDBzlj416HUih8v3wEqdcD16\nb7Pu1Oro3qcj/xBQ9HOQ8qTT/FyWrUgWnk/N6y7BX2fpWYH91aiNjov2QzuFTOer\n8NBJTjqbfIJK5xb9lmuKJZ2P0715UJYhAtdgJaEcOd5pzQw6Wutnh/0Z93ryQozJ\n9Q67+XgRNCDsG6CpfVIc40x7nvC0s0EUCM/hwlgf1tdEBWcCAwEAAaN7MHkwDgYD\nVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGsBBdaz20yO\nL3NvzkcI5UU0eHWrMB8GA1UdIwQYMBaAFGsBBdaz20yOL3NvzkcI5UU0eHWrMBYG\nA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCztaw07tp3\nN7oRV2eiHYnvS0r3667IWqYz0T7+epnN5YuOXSNzjgB8Zbytk2mMKDCLg5mczITi\n/naybO9dczbx2SD9FhQwR3vjy+ykcI5NLjgjQ7EZZO6tyS8ZOjK9igrcp+5XG9fw\nkmp9q64em5ZKYwhR6OP2VgUrgn6MR40BFvhRA5oXH6M+UmYebWNqBB8R2z0BTf+U\nOeO9jpsLDVuhQlis91TKJXTZS6MuZwJB5e8H5/foQXKrrprmKZf2nn95qUApwn7H\nd6y6jaa9zBu8w+HZsamZM9Qi93Vg/5XoGcuTm4XE9zcM2DKPtn79TlrElpdXEH+/\nbs7CfJ5Phq5t\n-----END CERTIFICATE-----\n", "issuer_id": "09c2c9a0-a874-36d2-de85-d79a7a51e373", "issuer_name": "root-2023", "key_id": "4616f6dc-17a4-a137-79b3-82f6b04f38d7", "leaf_not_after_behavior": "err", "manual_chain": null, "usage": "issuing-certificates,crl-signing,read-only" }, "wrap_info": null, "warnings": null, "auth": null }
Create a role for the root CA; creating this role allows for specifying an issuer when necessary for the purposes of this scenario. This also provides a simple way to transition from one issuer to another by referring to it by name.
$ curl \ --silent \ --request PUT \ --header "X-Vault-Token: $(vault print token)" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --header "X-Vault-Request: true" \ --data '{"allow_any_name":"true", "issuer_ref": "root-2023"}' \ "$VAULT_ADDR"/v1/pki/roles/2023-servers
This command is not expected to produce any output.
Note
This role is overly permissive in the names that it allows just for the purposes of simplification in this tutorial.
Create an API request payload containing the URLs to set.
$ tee payload-url.json <<EOF { "issuing_certificates": "$VAULT_ADDR/v1/pki/ca", "crl_distribution_points": "$VAULT_ADDR/v1/pki/crl" } EOF
Configure the CA and CRL URLs.
$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data @payload-url.json \ $VAULT_ADDR/v1/pki/config/urls
Note
When using multiple issuers in the same mount, it is suggested to use the per-issuer AIA fields rather than the global (
/config/urls
) variant. This is for correctness: these fields are used for chain building and automatic CRL detection in certain applications. If they point to the wrong issuer's information, these applications may break.
- Open a web browser and launch the Vault UI.
- Login by entering the
root
(for Vault in dev mode) or the admin token (for Vault Dedicated) in the Token field. - Select Enable new engine.
- Select PKI Certificates from the list, and then click Next.
- Expand Method Options.
- Enable Max Lease TTL and set the value to
87600 hours
. - Click Enable Engine to complete. This sets the path to be
pki
. - Select the Overview tab and then Configure PKI.
- Click Generate root.
- Under Type, select internal.
- Enter
example.com
in the Common name field. - Enter
root-2023
in the Issuer name field. - Under Not valid after, set the value of TTL to 87600 hours.
- Scroll down to Issuer URLs.
- Enter
http://localhost:8200/v1/pki/ca
in the Issuing certificates field. - Enter
http://localhost:8200/v1/pki/crl
in the CRL Distribution Points field. - Enter
http://localhost:8200/v1/ocsp
in the OCSP Servers field. - Click Done.
- Click the clipboard icon to copy the root CA certificate, and save in a file named
root_2023_ca.crt
. - Add a role to the root CA for convenience in this scenario when rotating the root CA.
- Click pki.
- Click Roles.
- Click Create role.
- Enter
2023-servers
into the Role name field. - Click Create
OpenSSL enables you to view all the metadata of the generated certificate.
Print the certificate in text form.
$ openssl x509 -in root_2023_ca.crt -text
To understand this section, you run the terraform configuration to create the appropriate resources, and examine the relevant blocks of HCL code to discuss what they do.
Clone the repository
learn-vault-pki-engine
.$ git clone https://github.com/hashicorp-education/learn-vault-pki-engine
Move into the working directory.
$ cd learn-vault-pki-engine/terraform
Now initialize the directory.
$ terraform init ... Terraform has been successfully initialized! ...
After initialization, apply the plan and confirm by inputting
yes
when prompted.View the
terraform plan
.$ terraform plan ... Plan: 23 to add, 0 to change, 0 to destroy.
Now run the apply.
$ terraform apply -auto-approve ... Apply complete! Resources: 23 added, 0 changed, 0 destroyed. ...
Examine the main.tf and locate the
vault_mount pki
block. This creates and tunes the new secrets engine.resource "vault_mount" "pki" { path = "pki" type = "pki" description = "This is an example PKI mount" default_lease_ttl_seconds = 86400 max_lease_ttl_seconds = 315360000 }
Note
The CLI allows you to specify the TTL in hours, minutes, and seconds, but the Terraform provider only allows you to specify the TTL in seconds. For a CLI command flag
-max-lease-ttl=87600h
specifying the TTL in hours, you use 87600 hours x 60 minutes x 60 seconds or 315360000 seconds for the value.The
vault_pki_secret_backend_root_cert
creates the example.com root certificate.resource "vault_pki_secret_backend_root_cert" "root_2023" { backend = vault_mount.pki.path type = "internal" common_name = "example.com" ttl = 315360000 issuer_name = "root-2023" }
This generates a new self-signed CA certificate and private key. Vault automatically revokes the generated root at the end of its lease period (TTL). The CA certificate signs its own Certificate Revocation List (CRL).
The resource
vault_pki_secret_backend_issuer
manages an existing issuer. Notice how this resource block refers to the issuer created invault_pki_secret_backend_root_cert.root_2023
.resource "vault_pki_secret_backend_issuer" "root_2023" { backend = vault_mount.pki.path issuer_ref = vault_pki_secret_backend_root_cert.root_2023.issuer_id issuer_name = vault_pki_secret_backend_root_cert.root_2023.issuer_name revocation_signature_algorithm = "SHA256WithRSA" }
Using the
vault_pki_secret_backend_role
resource creates a role for the root CA; creating this role allows for specifying an issuer when necessary for the purposes of this scenario. This also provides a simple way to transition from one issuer to another by referring to it by name.resource "vault_pki_secret_backend_role" "role" { backend = vault_mount.pki.path name = "2023-servers" ttl = 86400 allow_ip_sans = true key_type = "rsa" key_bits = 4096 allowed_domains = ["example.com", "my.domain"] allow_subdomains = true allow_any_name = true }
The vault_pki_secret_backend_config_urls configures CA and CRL URLs.
resource "vault_pki_secret_backend_config_urls" "config-urls" { backend = vault_mount.pki.path issuing_certificates = ["http://localhost:8200/v1/pki/ca"] crl_distribution_points = ["http://localhost:8200/v1/pki/crl"] }
Step 2: generate intermediate CA
Now, you are going to create an intermediate CA using the root CA you regenerated in the first step.
Tip
If you have Vault version 1.13.0 or later, you can use the new CLI helper pki issue
to generate your intermediate CA. Follow the steps in the pki issue command tab to use the helper instead of the standard CLI workflow.
First, enable the
pki
secrets engine at thepki_int
path.$ vault secrets enable -path=pki_int pki
Successful output example:
Success! Enabled the pki secrets engine at: pki_int/
Tune the
pki_int
secrets engine to issue certificates with a maximum time-to-live (TTL) of 43800 hours.$ vault secrets tune -max-lease-ttl=43800h pki_int
Successful output example:
Success! Tuned the secrets engine at: pki_int/
Execute the following command to generate an intermediate and save the CSR as
pki_intermediate.csr
.$ vault write -format=json pki_int/intermediate/generate/internal \ common_name="example.com Intermediate Authority" \ issuer_name="example-dot-com-intermediate" \ | jq -r '.data.csr' > pki_intermediate.csr
This command is expected to produce no output.
Sign the intermediate certificate with the root CA private key, and save the generated certificate as
intermediate.cert.pem
.$ vault write -format=json pki/root/sign-intermediate \ issuer_ref="root-2023" \ csr=@pki_intermediate.csr \ format=pem_bundle ttl="43800h" \ | jq -r '.data.certificate' > intermediate.cert.pem
This command is expected to produce no output.
Once the CSR is signed and the root CA returns a certificate, it can be imported back into Vault.
$ vault write pki_int/intermediate/set-signed certificate=@intermediate.cert.pem
Successful output example:
Key Value --- ----- imported_issuers [ffefa5b2-ae7c-b765-1672-c275fec29dc7 276a666f-c8da-20c5-c5a4-e01dff8ff2ea] imported_keys <nil> mapping map[276a666f-c8da-20c5-c5a4-e01dff8ff2ea: ffefa5b2-ae7c-b765-1672-c275fec29dc7:0f021093-a446-5b8e-70e8-a6bbda6f1a99]
Enable the
pki
secrets engine at thepki_int
path.$ vault secrets enable -path=pki_int pki
Successful output example:
Success! Enabled the pki secrets engine at: pki_int/
Tune the
pki_int
secrets engine to issue certificates with a maximum time-to-live (TTL) of 43800 hours.$ vault secrets tune -max-lease-ttl=43800h pki_int
Successful output example:
Success! Tuned the secrets engine at: pki_int/
Use the
pki issue
command to handle the intermediate CA generation process for you. The example parameters match this PKI secrets engine instance in this tutorial.$ vault pki issue \ --issuer_name=example-dot-com-intermediate \ /pki/issuer/a13b7db8-58f2-d926-64fc-bd39c9e6950c \ /pki_int/ \ common_name="example.com Intermediate Authority" \ o="example" \ ou="education" \ key_type="rsa" \ key_bits="4096" \ max_depth_len=1 \ permitted_dns_domains="test.example.com" \ ttl="43800h"
Abbreviated output example:
Key Value --- ----- ca_chain [-----BEGIN CERTIFICATE----- MIIE3jCCA8agAwIBAgIUWR5g4+h92GL/7DndQfaecf3DoZIwDQYJKoZIhvcNAQEL ...snip... crl_distribution_points [] issuer_id e48e21f4-7690-9c3c-acb8-2d60450f4150 issuer_name example-dot-com-intermediate issuing_certificates [] key_id d50faa6c-1f3c-e197-a205-16e6903c64f7 leaf_not_after_behavior err manual_chain <nil> ocsp_servers [] revocation_signature_algorithm n/a revoked false usage crl-signing,issuing-certificates,ocsp-signing,read-only ...snip...
Enable the
pki
secrets engine atpki_int
path.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data '{"type":"pki"}' \ $VAULT_ADDR/v1/sys/mounts/pki_int
Tune the
pki_int
secrets engine to issue certificates with a maximum time-to-live (TTL) of 43800 hours.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data '{"max_lease_ttl":"43800h"}' \ $VAULT_ADDR/v1/sys/mounts/pki_int/tune
Create an API request payload containing the common name to set.
$ tee payload-int.json <<EOF { "common_name": "example.com Intermediate Authority", "issuer_name": "example-dot-com-intermediate" } EOF
Generate an intermediate using the
/pki_int/intermediate/generate/internal
endpoint and save it aspki_intermediate.csr
.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data @payload-int.json \ $VAULT_ADDR/v1/pki_int/intermediate/generate/internal | jq -c '.data | .csr' >> pki_intermediate.csr
Create an API request payload to sign the CSR.
$ tee payload-int-cert.json <<EOF { "csr": $(cat pki_intermediate.csr), "format": "pem_bundle", "ttl": "43800h" } EOF
Sign the intermediate certificate with the root CA private key, and save the certificate as
intermediate.cert.pem
.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data @payload-int-cert.json \ $VAULT_ADDR/v1/pki/root/sign-intermediate | jq '.data | .certificate' >> intermediate.cert.pem
Note
The
format
in the payload specifies the format of the returned data. Whenpem_bundle
, the certificate field holds the certificate.Once the CSR is signed and the root CA returns a certificate, it can be imported back into Vault using the
/pki_int/intermediate/set-signed
endpoint.Create an API request payload containing the certificate you obtained.
$ tee payload-signed.json <<EOF { "certificate": $(cat intermediate.cert.pem) } EOF
Submit the signed certificate.
$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data @payload-signed.json \ $VAULT_ADDR/v1/pki_int/intermediate/set-signed
- From Secrets, select Enable new engine.
- Select the PKI Certificates radio button, and click Next.
- Enter
pki_int
in the Path field. - Set the Max lease TTL to
43800 hours
. - Click Enable Engine.
- Click Configure PKI.
- Select Generate intermediate CSR.
- Select internal as the Type.
- Enter
example.com Intermediate Authority
in the Common name field. - Click Generate.
- Click the clipboard icon to save the CSR in a file named
pki_intermediate.csr
. - Click secrets.
- Click pki.
- Click Issuers.
- Click root-123.
- Click Sign intermediate.
- Paste in the CSR you just copied into the Certificate Signing Request (CSR) field.
- Enter
example.com
in the Common name. - Select pem_bundle from the Format drop-down list, and then click Sign.
- Click Download save the generated certificate in a file,
intermediate.cert.pem
. - Click secrets.
- Select pki_int from the Secrets tab to return to the intermediate CA.
- Select the Overview tab and then Configure PKI.
- Click Import a CA.
- Select the file
intermediate.cert.pem
in the PEM Bundle field, and then click Import issuer.
Examine the main.tf and locate
resource "vault_mount" "pki_int"
. This enables a secondpki
engine to act an intermediate authority with thepki_int
path. It is set with an (TTL) of 43800 hours or 157680000 seconds.resource "vault_mount" "pki_int" { path = "pki_int" type = "pki" description = "This is an example intermediate PKI mount" default_lease_ttl_seconds = 86400 max_lease_ttl_seconds = 157680000 }
This
vault_pki_secret_backend_intermediate_cert_request
block generates an intermediate and then uses thelocal_file
block to save the CSR aspki_intermediate.csr
.resource "vault_pki_secret_backend_intermediate_cert_request" "csr-request" { backend = vault_mount.pki_int.path type = "internal" common_name = "example.com Intermediate Authority" }
The intermediate certificate is signed with the root CA private key, and the save the generated certificate as
intermediate.cert.pem
.resource "vault_pki_secret_backend_root_sign_intermediate" "intermediate" { backend = vault_mount.pki.path common_name = "new_intermediate" csr = vault_pki_secret_backend_intermediate_cert_request.csr-request.csr format = "pem_bundle" ttl = 15480000 issuer_ref = vault_pki_secret_backend_root_cert.root_2023.issuer_id }
Import the certificate back into Vault.
resource "vault_pki_secret_backend_intermediate_set_signed" "intermediate" { backend = vault_mount.pki_int.path certificate = vault_pki_secret_backend_root_sign_intermediate.intermediate.certificate }
Step 3: create a role
A role is a logical name that maps to a policy used to generate those credentials. It allows configuration parameters to control certificate common names, alternate names, the key uses that they are valid for, and more.
Here are a few noteworthy parameters:
Param | Description |
---|---|
allowed_domains | Specifies the domains of the role (used with allow_bare_domains and allow-subdomains options) |
allow_bare_domains | Specifies if clients can request certificates matching the value of the actual domains themselves |
allow_subdomains | Specifies if clients can request certificates with CNs that are subdomains of the CNs allowed by the other role options (NOTE: This includes wildcard subdomains.) |
allow_glob_domains | Allows names specified in allowed_domains to contain glob patterns (e.g. ftp*.example.com) |
In this step, you are going to create a role named example-dot-com
.
Create a role named example-dot-com
which allows subdomains, and specify the default issuer ref ID as the value of issuer_ref
.
$ vault write pki_int/roles/example-dot-com \
issuer_ref="$(vault read -field=default pki_int/config/issuers)" \
allowed_domains="example.com" \
allow_subdomains=true \
max_ttl="720h"
Example output:
Key Value
--- -----
allow_any_name false
allow_bare_domains false
allow_glob_domains false
allow_ip_sans true
allow_localhost true
allow_subdomains true
allow_token_displayname false
allow_wildcard_certificates true
allowed_domains [example.com]
allowed_domains_template false
allowed_other_sans []
allowed_serial_numbers []
allowed_uri_sans []
allowed_uri_sans_template false
allowed_user_ids []
basic_constraints_valid_for_non_ca false
client_flag true
cn_validations [email hostname]
code_signing_flag false
country []
email_protection_flag false
enforce_hostnames true
ext_key_usage []
ext_key_usage_oids []
generate_lease false
issuer_ref b96a564f-6031-83e2-4d43-fd2b4bede424
key_bits 2048
key_type rsa
key_usage [DigitalSignature KeyAgreement KeyEncipherment]
locality []
max_ttl 720h
no_store false
not_after n/a
not_before_duration 30s
organization []
ou []
policy_identifiers []
postal_code []
province []
require_cn true
server_flag true
signature_bits 256
street_address []
ttl 0s
use_csr_common_name true
use_csr_sans true
use_pss false
Create an API request payload containing domain information to set.
$ tee payload-role.json <<EOF { "allowed_domains": "example.com", "allow_subdomains": true, "issuer_ref": "$(vault read -field=default pki_int/config/issuers)", "max_ttl": "720h" } EOF
Create a role named
example-dot-com
which allows subdomains.$ curl --header "X-Vault-Token: $VAULT_TOKEN" \ --header "X-Vault-Namespace: $VAULT_NAMESPACE" \ --request POST \ --data @payload-role.json \ $VAULT_ADDR/v1/pki_int/roles/example-dot-com
Create a role named example-dot-com
which allows subdomains.
Click the terminal icon.
In terminal, use the following command to list issuers:
$ read -field=default pki_int/config/issuers
Copy the value from the preceding command.
Click secrets.
Click pki_int and then select Roles.
Click Create role.
Enter
example-dot-com
in the Role name field.Toggle Use default issuer.
Select the issuer from the drop down. It has a unique ID, not the literal value
root-2023
.Under Not valid after, set the TTL to
43800 hours
(5
years).Expand Domain Handling and then select the Allow subdomains check-box. Enter
example.com
in the Allowed domains field.Click Create.
Create a role named
example-dot-com
using thevault_pki_secret_backend_role
resource block which allows subdomains, and specify the default issuer ref ID as the value ofissuer_ref
.resource "vault_pki_secret_backend_role" "intermediate_role" { backend = vault_mount.pki_int.path issuer_ref = vault_pki_secret_backend_issuer.intermediate.issuer_ref name = "example-dot-com" ttl = 86400 max_ttl = 2592000 allow_ip_sans = true key_type = "rsa" key_bits = 4096 allowed_domains = ["example.com"] allow_subdomains = true }
Examine the role
example-dot-com
.$ vault read pki_int/roles/example-dot-com Key Value --- ----- allow_any_name false allow_bare_domains false allow_glob_domains false allow_ip_sans true allow_localhost true allow_subdomains true allow_token_displayname false allow_wildcard_certificates true allowed_domains [example.com] allowed_domains_template false allowed_other_sans [] allowed_serial_numbers [] allowed_uri_sans [] allowed_uri_sans_template false allowed_user_ids [] basic_constraints_valid_for_non_ca false client_flag true cn_validations [email hostname] code_signing_flag false country [] email_protection_flag false enforce_hostnames true ext_key_usage [] ext_key_usage_oids [] generate_lease false issuer_ref 40ba9192-d609-235f-4454-ea3ec33aeb4e key_bits 4096 key_type rsa key_usage [DigitalSignature KeyAgreement KeyEncipherment] locality [] max_ttl 720h no_store false not_after n/a not_before_duration 30s organization [] ou [] policy_identifiers [] postal_code [] province [] require_cn true server_flag true signature_bits 256 street_address [] ttl 24h use_csr_common_name true use_csr_sans true use_pss false
Step 4: request certificates
Keep certificate lifetimes short to align with Vault's philosophy of short-lived secrets.
Execute the following command to request a new certificate for the
test.example.com
domain based on the example-dot-com
role.
$ vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
Example output:
Key Value
--- -----
certificate -----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIUTQABMCAsXjG6ExFTX8201xKVH4IwDQYJKoZIhvcNAQEL
BQAwGjEYMBYGA1UEAxMPd3d3LmV4YW1wbGUuY29tMB4XDTE4MDcyNDIxMTMxOVoX
...
-----END CERTIFICATE-----
issuing_ca -----BEGIN CERTIFICATE-----
MIIDQTCCAimgAwIBAgIUbMYp39mdj7dKX033ZjK18rx05x8wDQYJKoZIhvcNAQEL
...
-----END CERTIFICATE-----
private_key -----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAte1fqy2Ekj+EFqKV6N5QJlBgMo/U4IIxwLZI6a87yAC/rDhm
W58liadXrwjzRgWeqVOoCRr/B5JnRLbyIKBVp6MMFwZVkynEPzDmy0ynuomSfJkM
...
-----END RSA PRIVATE KEY-----
private_key_type rsa
serial_number 4d:00:01:30:20:2c:5e:31:ba:13:11:53:5f:cd:b4:d7:12:95:1f:82
The response has the PEM-encoded private key, key type and certificate serial number.
Invoke the /pki_int/issue/<role_name>
endpoint to request a new certificate.
Request a certificate for the test.example.com
domain based on the
example-dot-com
role.
$ curl --header "X-Vault-Token: $VAULT_TOKEN" \
--header "X-Vault-Namespace: $VAULT_NAMESPACE" \
--request POST \
--data '{"common_name": "test.example.com", "ttl": "24h"}' \
$VAULT_ADDR/v1/pki_int/issue/example-dot-com | jq
{
"request_id": "7544fb94-49aa-8a11-1396-67938b69c3f2",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"ca_chain": [
"-----BEGIN CERTIFICATE-----\nMIIDqDCCApCgAwIBAgIUfg7l07KVEnht4ETStjHLAA3xvp4wDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjEwNDE5MTYzNTM1WhcNMjYw\nNDE4MTYzNjA1WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg\nQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApranvA5f\ncP+OySUE+uKNxzR3IkWI59Z0mMYlHYBtAY47V8avXcmwsaFDJiGSq6OXJs8SXKiF\nrXONaQSudWxTO28U8AovcwoV3/C5KsldqbhLMOyor+XDWYQP7GVXGuKYhn9C+kaF\nbiyaPE0SsAb6Yl3emVl1UCq9kBbQm1XmFyih7rdREB4XJZW2Mtp5KdzlHf3zGLfB\nEcK/BF0CsC1kI5UkkJVWxLB3hKx2pr3Bl3olufQDDdweKqlg7YtiPPnmxLvuEq5n\nG5h18++mbxywIvU7iehGYDj+EBFV1zrsOmZcqfSC34xRbJLVg8wFhuQ59Zy/k+mh\nEUo63NOrT1lzTQIDAQABo4HWMIHTMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\nBTADAQH/MB0GA1UdDgQWBBQoQnqjxjJ8ExU3SDHRG9M67KNlLDAfBgNVHSMEGDAW\ngBSnG76ILbfk2VdLrRd6m6bVYK3qRjA8BggrBgEFBQcBAQQwMC4wLAYIKwYBBQUH\nMAKGIGh0dHBzOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpL2NhMDIGA1UdHwQrMCkw\nJ6AloCOGIWh0dHBzOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpL2NybDANBgkqhkiG\n9w0BAQsFAAOCAQEAegVUIb5SFR/bXo33xqjuocINJLfec22dgjgBO0lMOGp32GFB\nIp4clgfNU1Dw8yGQEoRQHEfWDjZeGdPuCYbgu/ucLW1pLrBiQqO/XgVeFQhwnJp6\nza0NLGnmaDd5k8z1+VJ79tJa0Wb2WRxWxYqu6lsqtYPAj9jD4DM73tfGR76/rCiK\nRB9DRwlr9UTm9h0cw84UpPwaX33kfiIKBeyY9/hUe3pBzGuNGuDgjOiaMph0v+9z\nwiyNCzcR0AN0y1qDVvsqABH9GJ0BpaZ48GbZTZm91Q/kg+HdNlPkgwqpY8+V8aMa\n1CyvvvJeega8q9Evg1v68MajkFFT37YI5XESCw==\n-----END CERTIFICATE-----"
],
"certificate": "-----BEGIN CERTIFICATE-----\nMIIDZjCCAk6gAwIBAgIUPwOpEOAo39zYqPlQe83UjAH1R14wDQYJKoZIhvcNAQEL\nBQAwLTErMCkGA1UEAxMiZXhhbXBsZS5jb20gSW50ZXJtZWRpYXRlIEF1dGhvcml0\neTAeFw0yMTA0MTkxNjM5MjVaFw0yMTA0MjAxNjM5NTVaMBsxGTAXBgNVBAMTEHRl\nc3QuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP\nfe4nVV+AFnRUgW33qXyCOC4y7ruzSm9P6SiEirTFT+MmNtHEGPBGImGFBLYrSqMo\nePiO3GolpHWezY73cvRzFWM/ZLTmfjqsFNsQmP4tS1bbKa7Bn2ZcypbJNENr+/d1\nBkv9mIJuJwI1sQLI2yQe8KZT3HLrtr4tbEGXnY/tZD3rm+kgZNvCq0GpTARATCkZ\n3qUgOT10O5dMYe8Y2GGCZisiviKeGP1S42EL15QsoLhvIui2sZws7sa6LLeWMNYM\nIsWRAYsTkDffFRWEx/yK1axr0Heo9TrRVBuNfRkU8X/p1Ls3kXUB/HMF9RsW53uZ\nDPJbXOHDSJVhkubPoDxXAgMBAAGjgY8wgYwwDgYDVR0PAQH/BAQDAgOoMB0GA1Ud\nJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQU0UKGmwpRpQsi+wTo\nviMS6w6iEkowHwYDVR0jBBgwFoAUKEJ6o8YyfBMVN0gx0RvTOuyjZSwwGwYDVR0R\nBBQwEoIQdGVzdC5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAhRcjJf6T\nve9hrVsYSVZLGEOmfYoVJ9J2u7fxOfIMnBYJ0EEm047my6Qj5wmu00HSefhheBK1\n46XK5iBlpdSVrV28IjE8PDnWotWVyYhy9vq+LdVbrhHV3JWbRtM21AXkL/73dB3h\nBC3o+lRjmw+ySSEFlfugP+a9ULqwWtuQH+uFWq86F2Ar4y5Sl4TnIiZ9i22Yx+i4\nKcwPRPFsEyCvwRh1Ih/r/UTsrF0lqE7iQ77Yo9I9MuNKMc6y3+fI3vZWTED0QIfz\nVlb+A5Q9AfGhfaygc2fzJ9w/gnZ+D6wp/mQWQz2xNxs3iaDQf8xHpXywYNGbkVXX\ny/cTj/D8a8iAJA==\n-----END CERTIFICATE-----",
"expiration": 1618936795,
"issuing_ca": "-----BEGIN CERTIFICATE-----\nMIIDqDCCApCgAwIBAgIUfg7l07KVEnht4ETStjHLAA3xvp4wDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjEwNDE5MTYzNTM1WhcNMjYw\nNDE4MTYzNjA1WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg\nQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApranvA5f\ncP+OySUE+uKNxzR3IkWI59Z0mMYlHYBtAY47V8avXcmwsaFDJiGSq6OXJs8SXKiF\nrXONaQSudWxTO28U8AovcwoV3/C5KsldqbhLMOyor+XDWYQP7GVXGuKYhn9C+kaF\nbiyaPE0SsAb6Yl3emVl1UCq9kBbQm1XmFyih7rdREB4XJZW2Mtp5KdzlHf3zGLfB\nEcK/BF0CsC1kI5UkkJVWxLB3hKx2pr3Bl3olufQDDdweKqlg7YtiPPnmxLvuEq5n\nG5h18++mbxywIvU7iehGYDj+EBFV1zrsOmZcqfSC34xRbJLVg8wFhuQ59Zy/k+mh\nEUo63NOrT1lzTQIDAQABo4HWMIHTMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\nBTADAQH/MB0GA1UdDgQWBBQoQnqjxjJ8ExU3SDHRG9M67KNlLDAfBgNVHSMEGDAW\ngBSnG76ILbfk2VdLrRd6m6bVYK3qRjA8BggrBgEFBQcBAQQwMC4wLAYIKwYBBQUH\nMAKGIGh0dHBzOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpL2NhMDIGA1UdHwQrMCkw\nJ6AloCOGIWh0dHBzOi8vMTI3LjAuMC4xOjgyMDAvdjEvcGtpL2NybDANBgkqhkiG\n9w0BAQsFAAOCAQEAegVUIb5SFR/bXo33xqjuocINJLfec22dgjgBO0lMOGp32GFB\nIp4clgfNU1Dw8yGQEoRQHEfWDjZeGdPuCYbgu/ucLW1pLrBiQqO/XgVeFQhwnJp6\nza0NLGnmaDd5k8z1+VJ79tJa0Wb2WRxWxYqu6lsqtYPAj9jD4DM73tfGR76/rCiK\nRB9DRwlr9UTm9h0cw84UpPwaX33kfiIKBeyY9/hUe3pBzGuNGuDgjOiaMph0v+9z\nwiyNCzcR0AN0y1qDVvsqABH9GJ0BpaZ48GbZTZm91Q/kg+HdNlPkgwqpY8+V8aMa\n1CyvvvJeega8q9Evg1v68MajkFFT37YI5XESCw==\n-----END CERTIFICATE-----",
"private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAz33uJ1VfgBZ0VIFt96l8gjguMu67s0pvT+kohIq0xU/jJjbR\nxBjwRiJhhQS2K0qjKHj4jtxqJaR1ns2O93L0cxVjP2S05n46rBTbEJj+LUtW2ymu\nwZ9mXMqWyTRDa/v3dQZL/ZiCbicCNbECyNskHvCmU9xy67a+LWxBl52P7WQ965vp\nIGTbwqtBqUwEQEwpGd6lIDk9dDuXTGHvGNhhgmYrIr4inhj9UuNhC9eULKC4byLo\ntrGcLO7Guiy3ljDWDCLFkQGLE5A33xUVhMf8itWsa9B3qPU60VQbjX0ZFPF/6dS7\nN5F1AfxzBfUbFud7mQzyW1zhw0iVYZLmz6A8VwIDAQABAoIBADMwNhiuDyliYMCY\nTbDTt0vI4FzgWJ4atutX8g8AySgEVV2QGJ/wJxamVLikOOzlNOs/LNLRvb4bnIjY\n3XRef8AEfr+c8KQMcB0T6BdoJwy1kW/wEJTj5jTuJdTtd9SkDKBqNUUS4tqZ9QmZ\n6b3zki2v4Ni/gfp00uYR1vy4elFt/yjTmA0lhY4onPNPqXNWCF1LY38xNegBQZg4\nrYkSFvIOr7bl7Rk2JljWCxZZef5kEIFGdoqus5KekzW8Li9s2XVVXWEfoY/r/NI1\nHgAwHugwe3+8ve3YKLIIKJqGvYk+8Lp8rXPsf9GRZxpxx/yER4NX8+83DndBnfQO\n7G/BOwECgYEA/0ZAU6h+1cW+PxQ+CuSdbClXhKgqTN4pRwCbrGUQ3y2KGsGUzZW/\nUuO/XIN/hkx0SkKz10TvUV5o4SXh5nY7s+3OCsnc7fBx3Gv4vHCWQ4t9JNxeH6vP\nzNKjQeceVzlniyxKlW9OvVDXZU6nv27LBRH1fIYp9QRbc7yHzUQXLCcCgYEA0BTp\nD3CXELJYoV0/LFvRQqZ+P1Xo26qa9xjRMI+Owr2nNuzqcMl4dBvZWNNCpDwqrKn6\n6iiWXYhwxvvKkJe9QgE3LCvkmluGiBDpK4U2rVbB/LoBAtNgGdnxaP9t9OfhtQC3\nPjhVJvBVbk3mFWG950Ua5E1cgl6n4xtGoHgxHFECgYBaXOTieE/FnoUU0TaRJpIv\nOoc3d0vZ//5+mtGAeho51mX/yKzDBZI/Zk1UE1xuDtxPeUMuHcHVfOUFZiKMMSg7\nLh/0o7ZoJ+g2TaY0FmqqqFL5XGSZM3mQmLOf3Y9Y8wIbOud/9HHcBCTrQKeS1UZa\nmhvbI6bwi8VPt9oeqE7HmwKBgHqOVlbJsbAb2yfvi+3MhowDFAipyOTYrz0qWMuJ\nQkRg/8PR9qNHhrKcVH+ErpOc/GWGGEsibK3aVtJcKwrO1KGzpZNWpuZjUfGCRFNl\nuraNiuQXidDoPon7W7zD9Tdx+/Zn3YXAGCc/FpJJP2MIlplIknY1Om9u4ONahVau\nc/6BAoGBAIfW6PPFjXUUn3LvcswvQAahnclYIW+Ml6peerfMr5kq0TTWykrMN4nc\nLl9oGG2ifigSUo/mz2cfemExO+hgXJP+UOOKi2LkUUpV9x1fzJFHYutiIyV4mBPd\nTFPCpLJjz3T4Nn18nzGmrQiQT6ymgt4KDx6cW+xbOLTuNsNg+0ib\n-----END RSA PRIVATE KEY-----",
"private_key_type": "rsa",
"serial_number": "3f:03:a9:10:e0:28:df:dc:d8:a8:f9:50:7b:cd:d4:8c:01:f5:47:5e"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
- Select secrets.
- Select pki_int from the Secrets Engines list.
- Select example-dot-com under Roles.
- Click Generate certificate.
- Enter
test.example.com
in the Common name field. - Under Not valid after, set the TTL to
24 hours
. - Click Generate. The response contains the PEM-encoded private key, key type and certificate serial number. You can click on the sensitive information toggle to show or hide the generated values.
- Click Download and save it to a file.
The vault_pki_secret_backend_cert
resource creates a new certificate for the test.example.com
domain based on example-dot-com
role.
resource "vault_pki_secret_backend_cert" "example-dot-com" {
issuer_ref = vault_pki_secret_backend_issuer.intermediate.issuer_ref
backend = vault_pki_secret_backend_role.intermediate_role.backend
name = vault_pki_secret_backend_role.intermediate_role.name
common_name = "test.example.com"
ttl = 3600
revoke = true
}
Tip
A certificate can be renewed at any time by issuing a new certificate with the same CN. The prior certificate remains valid through its time-to-live value unless explicitly revoked.
Step 5: revoke certificates
If a certificate must be revoked, you can perform the revocation action that causes the CRL to be regenerated. When you regenerate the CRL, Vault removes any expired certificates from it.
Note
As of Vault version 1.12.0, the PKI Secret Engine's Bring-Your-Own-Cert (BYOC) capability allows revocation of certificates not stored before (e.g., issued via a role with no_store=true
). This means that setting no_store=true
is now safe to be used globally, regardless of importance of issued certificates, and their likelihood for revocation.
In certain circumstances, you may wish to revoke an issued certificate.
To revoke a certificate, execute the following command, replacing the <serial_number>
placeholder with the actual serial number of the certificate you want to revoke.
$ vault write pki_int/revoke serial_number=<serial_number>
Example:
$ vault write pki_int/revoke \
serial_number="3f:03:a9:10:e0:28:df:dc:d8:a8:f9:50:7b:cd:d4:8c:01:f5:47:5e"
Key Value
--- -----
revocation_time 1692733613
revocation_time_rfc3339 2023-08-22T19:46:53.117254Z
state revoked
Invoke the /pki_int/revoke
endpoint to invoke a certificate using its
serial number.
$ curl --header "X-Vault-Token: $VAULT_TOKEN" \
--header "X-Vault-Namespace: $VAULT_NAMESPACE" \
--request POST \
--data '{"serial_number": "<serial_number>"}' \
$VAULT_ADDR/v1/pki_int/revoke | jq
- Select Secrets.
- Select pki_int from the Secrets Engines list.
- Select the Certificates tab.
- Select the serial number for the certificate you wish to revoke.
- Click Revoke. At the confirmation, click Revoke again.
To revoke a certificate in Terraform, the
revoke
argument invault_pki_secret_backend_cert
should to set totrue
.resource "vault_pki_secret_backend_cert" "example-dot-com" { issuer_ref = vault_pki_secret_backend_issuer.intermediate.issuer_ref backend = vault_pki_secret_backend_role.intermediate_role.backend name = vault_pki_secret_backend_role.intermediate_role.name common_name = "test.example.com" ttl = 3600 revoke = true }
Look at the output of the
terraform apply
ran at the beginning of the tutorial. There is a field namedvault_pki_secret_backend_cert_example-dot-com_serial_number
. In your terminal copy the value displayed.Outputs: ... vault_pki_secret_backend_cert_example-dot-com_serial_number = "5f:83:38:d0:f6:25:6a:a6:85:9a:76:5d:50:8c:ee:5f:3d:f4:8c:26" ...
Use the Vault CLI to look at the expiration of the certificate. The
revocation_time
is zero, and therefore not set.$ vault read pki_int/cert/<YOUR_SERIAL_NUMBER_HERE> Key Value --- ----- certificate -----BEGIN CERTIFICATE----- MIIEVDCCAzygAwIBAgIUX4M40PYlaqaFmnZdUIzuXz30jCYwDQYJKoZIhvcNAQEL ... -----END CERTIFICATE----- revocation_time 0 revocation_time_rfc3339 n/a
To revoke the certificated generated by the
vault_pki_secret_backend_cert.example-dot-com
block, use the-target
flag with-destroy
.$ terraform apply -destroy --target vault_pki_secret_backend_cert.example-dot-com --auto-approve ... Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
Using the Vault CLI check the expiration of the certificate. The
revocation_time
is now set.$ vault read pki_int/cert/<YOUR_SERIAL_NUMBER_HERE> Key Value --- ----- certificate -----BEGIN CERTIFICATE----- MIIEVDCCAzygAwIBAgIUX4M40PYlaqaFmnZdUIzuXz30jCYwDQYJKoZIhvcNAQEL ... -----END CERTIFICATE----- issuer_id 16943d6c-0745-8d7a-fdb9-8c723a67aeee revocation_time 1692979749 revocation_time_rfc3339 2023-08-25T16:09:09.327564Z
Step 6: remove expired certificates
Keep the storage backend and CRL by periodically removing certificates that have expired and are past a certain buffer period beyond their expiration time.
To remove revoked certificate and clean the CRL.
$ vault write pki_int/tidy tidy_cert_store=true tidy_revoked_certs=true
WARNING! The following warnings were returned from Vault:
* Tidy operation successfully started. Any information from the operation
will be printed to Vault's server logs.
Note
You can expect the warning message on every invocation of the tidy command. It serves to alert the operator that they can observe progress from the tidy operation in the Vault operational log.
Invoke the /pki_int/tidy
endpoint to remove revoked certificate and clean
the CRL.
$ curl --header "X-Vault-Token: $VAULT_TOKEN" \
--header "X-Vault-Namespace: $VAULT_NAMESPACE" \
--request POST \
--data '{"tidy_cert_store": true, "tidy_revoked_certs": true}' \
$VAULT_ADDR/v1/pki_int/tidy | jq
- Select Secrets.
- Select pki_int from the Secrets Engines list.
- Select the Tidy tab.
- Select the check-box for Tidy the Certificate Store and Tidy the Revocation List (CRL).
- Click Save.
You are now finished with the steps in the main scenario, and can move to the Cleanup section from here.
The following steps 7-10 are optional and for learning about Root CA rotation. These steps also require that you are using Vault version 1.11.0 or greater.
Step 7: rotate root CA
Before version 1.11.0, you could not enable more than one root CA in the same PKI secrets engine mount. This makes rotating the root CA challenging when coordinating a number of PKI secrets engine mounts, and manually managing two root CAs while simultaneously deploying the new root CA to clients.
Vault version 1.11.0 introduces a set of new features in the PKI secrets engine, including multi-issuer capabilities, which include more than one root CA in the same secrets engine mount. This enables less burdensome root CA rotation. You can also use CA migration helpers to ease the operational burden of migrating a root CA controlled by Vault.
To begin learning about this capability, enable another root CA in the existing PKI secrets engine mount using the new rotate feature.
$ vault write pki/root/rotate/internal \ common_name="example.com" \ issuer_name="root-2024"
Key Value --- ----- certificate -----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUT2afmPzCe/DQFO4c/Re0H7nC0jMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTQzMTA0WhcNMjIw NzIyMTQzMTM0WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMAT2ZvEAP0hzJlkVY7ZNY2sbiGpor9CAzL2Uaqd 9PtmL761XzboQHFziwDnffWdUKttZ5Gdc6KLFSPG8Bpl50PgKxwsUqr/2skLm1O/ nVPVlodr3AUkbQZ4Qc6/Fs/lhGbPglFFMXl7NVdkeOnyFUZtouXLq11A38rc6280 FzxMw16FEmwsLU/tWOwgAad7Q/Xp9l5HrlKN//+LUnaDcFmYXmNIK2CVTloUOqL3 9WPMR+CeXqGR1RkxTuJ0oy0GdLo2JMh9ndz7JQkMcU8VigJ7oqhvhxczoM/Lx0hB 8Y+wUMynn/VvbUO0uBRE1RpOpk5Zfxbb8TiYysN9PpzhVYMCAwEAAaOB7DCB6TAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoWSv9g7d oyvm9Xqspi9jH6kfhq8wHwYDVR0jBBgwFoAUoWSv9g7doyvm9Xqspi9jH6kfhq8w OwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy MDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw JqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3 DQEBCwUAA4IBAQByO9DyRVJYjK9xrnfNnD1pbG0WXWRmsk7dxtXY336ollMl0eqO uEEHNGy0a4c1UKOjFg0LSg3c/DzUoPluUB4Kk9gs4ifn+1cm2k6G9gjT/6Wi0xIW 0t7+a7XX8ttPO7T4ZFDJT2KDINz42Oeht5u82TuKgBfdQsXm9XnJLqckQbaCZLPs uvyAT8MrgBABOvCVF1yurHNrmu+js6fZxu4wAO7ANMBDzNPYYx0IphYRGCnBte1N j4vSiTtlFOdf8+gdM8lSlv4XV56e10MepNSvvFDd1De6l+ErLciZJQydDklX2e3R 1GlQmm3d5Sxn5Wob3FA4nkyqLq21RlH2RRgo -----END CERTIFICATE----- expiration 1658500294 issuer_id f4baa074-10d7-7fc3-1623-f6f906ceed98 issuer_name root-2024 issuing_ca -----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUT2afmPzCe/DQFO4c/Re0H7nC0jMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTQzMTA0WhcNMjIw NzIyMTQzMTM0WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMAT2ZvEAP0hzJlkVY7ZNY2sbiGpor9CAzL2Uaqd 9PtmL761XzboQHFziwDnffWdUKttZ5Gdc6KLFSPG8Bpl50PgKxwsUqr/2skLm1O/ nVPVlodr3AUkbQZ4Qc6/Fs/lhGbPglFFMXl7NVdkeOnyFUZtouXLq11A38rc6280 FzxMw16FEmwsLU/tWOwgAad7Q/Xp9l5HrlKN//+LUnaDcFmYXmNIK2CVTloUOqL3 9WPMR+CeXqGR1RkxTuJ0oy0GdLo2JMh9ndz7JQkMcU8VigJ7oqhvhxczoM/Lx0hB 8Y+wUMynn/VvbUO0uBRE1RpOpk5Zfxbb8TiYysN9PpzhVYMCAwEAAaOB7DCB6TAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoWSv9g7d oyvm9Xqspi9jH6kfhq8wHwYDVR0jBBgwFoAUoWSv9g7doyvm9Xqspi9jH6kfhq8w OwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy MDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw JqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3 DQEBCwUAA4IBAQByO9DyRVJYjK9xrnfNnD1pbG0WXWRmsk7dxtXY336ollMl0eqO uEEHNGy0a4c1UKOjFg0LSg3c/DzUoPluUB4Kk9gs4ifn+1cm2k6G9gjT/6Wi0xIW 0t7+a7XX8ttPO7T4ZFDJT2KDINz42Oeht5u82TuKgBfdQsXm9XnJLqckQbaCZLPs uvyAT8MrgBABOvCVF1yurHNrmu+js6fZxu4wAO7ANMBDzNPYYx0IphYRGCnBte1N j4vSiTtlFOdf8+gdM8lSlv4XV56e10MepNSvvFDd1De6l+ErLciZJQydDklX2e3R 1GlQmm3d5Sxn5Wob3FA4nkyqLq21RlH2RRgo -----END CERTIFICATE----- key_id 775fb40a-4568-f613-2c6d-0f6802e859e1 key_name n/a serial_number 4f:66:9f:98:fc:c2:7b:f0:d0:14:ee:1c:fd:17:b4:1f:b9:c2:d2:33
Note that the certificate information has multi-issuer information, such as the
issuer_ref
andissuer_name
values.You can also list the issuers to confirm the addition of the new Root CA.
$ vault list pki/issuers Keys ---- 09c2c9a0-a874-36d2-de85-d79a7a51e373 f4baa074-10d7-7fc3-1623-f6f906ceed98
Both root CA issuers are now enabled in the same PKI secrets engine mount. While the new root CA is enabled, it's not yet actively used, and all later issuance from the mount uses the original root CA issuer,
root-2023
.Tip
At this point, the new root CA certificate can be deployed to all devices which require it as part of rotating out the old root CA.
Create a role for the new example.com root CA; creating this role allows for specifying an issuer when necessary. This also provides a simple way to transition from one issuer to another by referring to it by name.
$ vault write pki/roles/2024-servers allow_any_name=true Success! Data written to: pki/roles/2024-servers
Key Value --- ----- allow_any_name true allow_bare_domains false allow_glob_domains false allow_ip_sans true allow_localhost true allow_subdomains false allow_token_displayname false allow_wildcard_certificates true allowed_domains [] allowed_domains_template false allowed_other_sans [] allowed_serial_numbers [] allowed_uri_sans [] allowed_uri_sans_template false allowed_user_ids [] basic_constraints_valid_for_non_ca false client_flag true cn_validations [email hostname] code_signing_flag false country [] email_protection_flag false enforce_hostnames true ext_key_usage [] ext_key_usage_oids [] generate_lease false issuer_ref default key_bits 2048 key_type rsa key_usage [DigitalSignature KeyAgreement KeyEncipherment] locality [] max_ttl 0s no_store false not_after n/a not_before_duration 30s organization [] ou [] policy_identifiers [] postal_code [] province [] require_cn true server_flag true signature_bits 256 street_address [] ttl 0s use_csr_common_name true use_csr_sans true use_pss false
Note
This role is overly permissive in the names that it allows just for the purposes of simplification in this tutorial.
To begin learning about the multi-issuer capabilities, enable another root CA in the existing PKI secrets engine mount using the new rotate feature.
Generate the new Root CA using the
/pki/root/rotate/internal
endpoint.$ curl \ --silent \ --request PUT \ --header "X-Vault-Request: true" \ --header "X-Vault-Token: $(vault print token)" \ --data '{"common_name":"example.com","issuer_name":"root-2024"}' \ $VAULT_ADDR/v1/pki/root/rotate/internal \ | jq
{ "request_id": "ddab7cc7-fc1e-9d61-853f-fbf18f064f83", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "certificate": "-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUVoV/wuZMv05MacS2Pry3VrLgGhQwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTkwMTE4WhcNMjIw\nNzIyMTkwMTQ3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANnVXc6+Ytvov3k68vo3HEzOKJyjdPhvGHtsk1Rh\nsIfOoMjYtCTtxOmouP2SWVE819yQP7MejfOxXjROhfX/gUvMk//ETqX6Rq+DKYjp\nq2nfAHV3KQK/hQ1VWJx37uwazwGpSfd7so8TvnxqljKZny7zC/ZKaRLbt8OqoMfn\n6xOH8IEPw0798wmg5/UTByFrf/uzN1d4tX90Batnp0fDhexK9B7YDq5CL3NaxQnI\ngxd9ry2160ng0C5w9FfqbBVSBhbGPbvweEIBvaP08S4SIoAmG6gW2gFrU/A4zAdg\nnWU1qJ+eS8D0zFZNLbCjiHyqyg3Q0lH274v/VvHW7mLBvRsCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOO/cNV1d\nogLUPuiXvryXlohDm4owHwYDVR0jBBgwFoAUOO/cNV1dogLUPuiXvryXlohDm4ow\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQAkvCg+Q2jLEtUE3zlEvVN8Qtb2+zz5MlHsDwKkqjZykpRjrIC7\nxTeLxHod/GbzE+7DQKWUCn8AqoFjx6X7uOKCliPZ7ndVUHtGddOfXN5/xxRi1EE3\nf4bPyxbq/4gdZFfbUuHw9ugVWIbpeWrgBJfUos47suAUyFxOkIfqKxdK5lHCO6cD\n/FExWtqlyLIyjw0v7vaoPi88SHzrEWuosSkJzOc3NcT3tLSyQg+t+sqIxfLdddqF\n6Orp6M9k1cC54h/8O8G24dq47SiqlhwGft+IZgD9NwsWfgV5r3PT96LhITNf6bLy\nXnUSjLh1FBUyuSyHTmkVTeOuxVCNPzr2BXEA\n-----END CERTIFICATE-----", "expiration": 1658516507, "issuer_id": "8247c31f-ad1e-001c-6ad9-0aacc0aebea2", "issuer_name": "root-2024", "issuing_ca": "-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUVoV/wuZMv05MacS2Pry3VrLgGhQwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTkwMTE4WhcNMjIw\nNzIyMTkwMTQ3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANnVXc6+Ytvov3k68vo3HEzOKJyjdPhvGHtsk1Rh\nsIfOoMjYtCTtxOmouP2SWVE819yQP7MejfOxXjROhfX/gUvMk//ETqX6Rq+DKYjp\nq2nfAHV3KQK/hQ1VWJx37uwazwGpSfd7so8TvnxqljKZny7zC/ZKaRLbt8OqoMfn\n6xOH8IEPw0798wmg5/UTByFrf/uzN1d4tX90Batnp0fDhexK9B7YDq5CL3NaxQnI\ngxd9ry2160ng0C5w9FfqbBVSBhbGPbvweEIBvaP08S4SIoAmG6gW2gFrU/A4zAdg\nnWU1qJ+eS8D0zFZNLbCjiHyqyg3Q0lH274v/VvHW7mLBvRsCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOO/cNV1d\nogLUPuiXvryXlohDm4owHwYDVR0jBBgwFoAUOO/cNV1dogLUPuiXvryXlohDm4ow\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQAkvCg+Q2jLEtUE3zlEvVN8Qtb2+zz5MlHsDwKkqjZykpRjrIC7\nxTeLxHod/GbzE+7DQKWUCn8AqoFjx6X7uOKCliPZ7ndVUHtGddOfXN5/xxRi1EE3\nf4bPyxbq/4gdZFfbUuHw9ugVWIbpeWrgBJfUos47suAUyFxOkIfqKxdK5lHCO6cD\n/FExWtqlyLIyjw0v7vaoPi88SHzrEWuosSkJzOc3NcT3tLSyQg+t+sqIxfLdddqF\n6Orp6M9k1cC54h/8O8G24dq47SiqlhwGft+IZgD9NwsWfgV5r3PT96LhITNf6bLy\nXnUSjLh1FBUyuSyHTmkVTeOuxVCNPzr2BXEA\n-----END CERTIFICATE-----", "key_id": "3e46e483-a692-3a8d-6f33-2f9173c2f110", "key_name": "", "serial_number": "56:85:7f:c2:e6:4c:bf:4e:4c:69:c4:b6:3e:bc:b7:56:b2:e0:1a:14" }, "wrap_info": null, "warnings": null, "auth": null }
The Vault provider does not include a way to rotate certificates, but the Generic Endpoint resource could be used to rotate the certificates, but this is not recommended due to security concerns.
The /root/rotate/
call in the API is convention to make it easier for users, so simply generating a new root is sufficient. Refer to the Generate root API documentation for more details.
Enable a new root CA named
root_2024
.resource "vault_pki_secret_backend_root_cert" "root_2024" { backend = vault_mount.pki.path type = "internal" common_name = "example.com" ttl = "315360000" issuer_name = "root-2024" key_name = "root_2024" }
A role is created for the
root_2024
certificate. Creating this role allows for specifying an issuer when necessary. This provides a simple way to transition from one issuer to another by referring to it by name.resource "vault_pki_secret_backend_role" "role_2024" { backend = vault_mount.pki.path name = "2024-servers" allow_any_name = true }
Note
This role is overly permissive in the names that it allows just for the purposes of simplification in this tutorial.
Step 8: create a root bridge CA
A significant step in rotating an older root CA involves handling devices which might be offline for long periods of time, but which need to eventually come online and fetch the new root certificate.
If you want to effectively handle this situation, you can do so with the use of a root bridge CA between the older root to the new root. This helps clients of the older root trust certificates which the new root signs, and provides a predictable way to rotate the root with a clear time-frame (the pki/intermediate TTL).
The new cross-sign command provides this capability to the Vault operator in a familiar way, resembling the command syntax for creating a new intermediate CA with an existing key.
Two approaches to root CA rotation
You should choose to either create a root bridge CA as described in this section or create a cross-signed intermediate as described in the following section for your migration strategy, but do not perform the tasks for both steps.
Create the cross-signed Intermediate CSR.
$ vault write -format=json pki/intermediate/cross-sign \ common_name="example.com" \ key_ref="$(vault read pki/issuer/root-2024 \ | grep -i key_id | awk '{print $2}')" \ | jq -r '.data.csr' \ | tee cross-signed-intermediate.csr
Example output:
-----BEGIN CERTIFICATE REQUEST----- MIIChDCCAWwCAQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQDAE9mbxAD9IcyZZFWO2TWNrG4hqaK/QgMy9lGq nfT7Zi++tV826EBxc4sA5331nVCrbWeRnXOiixUjxvAaZedD4CscLFKq/9rJC5tT v51T1ZaHa9wFJG0GeEHOvxbP5YRmz4JRRTF5ezVXZHjp8hVGbaLly6tdQN/K3Otv NBc8TMNehRJsLC1P7VjsIAGne0P16fZeR65Sjf//i1J2g3BZmF5jSCtglU5aFDqi 9/VjzEfgnl6hkdUZMU7idKMtBnS6NiTIfZ3c+yUJDHFPFYoCe6Kob4cXM6DPy8dI QfGPsFDMp5/1b21DtLgURNUaTqZOWX8W2/E4mMrDfT6c4VWDAgMBAAGgKTAnBgkq hkiG9w0BCQ4xGjAYMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEB CwUAA4IBAQAeUqJ4t+IXNgGCMl2xCZ0a9Fypj873Ork+OgVz2zoASH14zia2MY3g GQe7NR9MFgebZMNG2hR3hcU22bmoUrsB+RITsgiQa1hYqZk5/MB059QvEUBYrBsn dSN6bFkembWYBLqae7I8JDW4UY+06LSI4eGeV2n5VyMJ1WwxerbYOCvFpRK+7qYf O4r6roMAuM5uTpKJMMqMipwG0dLJEXC/9E/j3V0qiN9yIy74PdNWJahg6X5biytL WGNfsVafnjH5zkJfb0iEDECJz8tuayEW98JLzEGfeH6dz0S1hAMhoWiqiL1ZQeOe 7zSKnUooThRs1904nojr6If74Pj058p4 -----END CERTIFICATE REQUEST-----
Sign the resulting CSR with the older root CA.
$ vault write -format=json pki/issuer/root-2023/sign-intermediate \ common_name="example.com" \ csr=@cross-signed-intermediate.csr \ | jq -r '.data.certificate' | tee cross-signed-intermediate.crt
Example output:
-----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUOjMm1b+ICXC682TSMH7wsFRdYMkwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTUwODA3WhcNMjIw NzIyMTUwODM3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMAT2ZvEAP0hzJlkVY7ZNY2sbiGpor9CAzL2Uaqd 9PtmL761XzboQHFziwDnffWdUKttZ5Gdc6KLFSPG8Bpl50PgKxwsUqr/2skLm1O/ nVPVlodr3AUkbQZ4Qc6/Fs/lhGbPglFFMXl7NVdkeOnyFUZtouXLq11A38rc6280 FzxMw16FEmwsLU/tWOwgAad7Q/Xp9l5HrlKN//+LUnaDcFmYXmNIK2CVTloUOqL3 9WPMR+CeXqGR1RkxTuJ0oy0GdLo2JMh9ndz7JQkMcU8VigJ7oqhvhxczoM/Lx0hB 8Y+wUMynn/VvbUO0uBRE1RpOpk5Zfxbb8TiYysN9PpzhVYMCAwEAAaOB7DCB6TAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoWSv9g7d oyvm9Xqspi9jH6kfhq8wHwYDVR0jBBgwFoAU+RF2dgdU6BZrOKHck1QpEmqPemUw OwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy MDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw JqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3 DQEBCwUAA4IBAQARw49qG9j/N46En+ME8u3uK0DmVxo/Fuj/RCNOBQ3EnIJ8zU+V hujyckHcQNQ0f8nfrdE1JHnn+bnhOIzO1qaaooSlmhWg1HuEolOn/g1Aq1xJvmOd nWKr4G1iwo3xmID6AYAI80LaVAeRcrk+S6dmhDuo1KT4CZmGNo+sz0rguute7aTh N568OGJF7qHoLFSn3/pe9zvpjuBiH9jiB1HmWbL/JqntTherr5JWK6W7EukGFNOd HEn/n/QfBuKX32QU8SjVb0ZRWd7CbJwL7nsNY6jnqlwLnCOyncL8hhnW6U8QbHqd dm1l9PpE7BssfVTk/exS/Icemf6XFzDeIrqT -----END CERTIFICATE-----
The result is a cross-signed certificate, but no issuer gets created yet. Import the signed certificate to create the new issuer.
Import the cross-signed certificate.
$ vault write pki/intermediate/set-signed \ certificate=@cross-signed-intermediate.crt
Example output:
Key Value --- ----- imported_issuers [3f5a71f1-6558-0c82-2f8c-c3feb27eee8a] imported_keys <nil> mapping map[3f5a71f1-6558-0c82-2f8c-c3feb27eee8a:775fb40a-4568-f613-2c6d-0f6802e859e1]
You now have 2 root CAs with a cross-signed intermediate CA providing a trust chain between them. You are ready to handle migration of devices which were not initially possible to migrate to the new root CA.
Read the issuer for the new root CA. You'll notice that there are now more certificates in the
ca_chain
field.$ vault read pki/issuer/root-2024
Key Value --- ----- ca_chain [-----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUT2afmPzCe/DQFO4c/Re0H7nC0jMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTQzMTA0WhcNMjIw NzIyMTQzMTM0WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMAT2ZvEAP0hzJlkVY7ZNY2sbiGpor9CAzL2Uaqd 9PtmL761XzboQHFziwDnffWdUKttZ5Gdc6KLFSPG8Bpl50PgKxwsUqr/2skLm1O/ nVPVlodr3AUkbQZ4Qc6/Fs/lhGbPglFFMXl7NVdkeOnyFUZtouXLq11A38rc6280 FzxMw16FEmwsLU/tWOwgAad7Q/Xp9l5HrlKN//+LUnaDcFmYXmNIK2CVTloUOqL3 9WPMR+CeXqGR1RkxTuJ0oy0GdLo2JMh9ndz7JQkMcU8VigJ7oqhvhxczoM/Lx0hB 8Y+wUMynn/VvbUO0uBRE1RpOpk5Zfxbb8TiYysN9PpzhVYMCAwEAAaOB7DCB6TAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoWSv9g7d oyvm9Xqspi9jH6kfhq8wHwYDVR0jBBgwFoAUoWSv9g7doyvm9Xqspi9jH6kfhq8w OwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy MDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw JqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3 DQEBCwUAA4IBAQByO9DyRVJYjK9xrnfNnD1pbG0WXWRmsk7dxtXY336ollMl0eqO uEEHNGy0a4c1UKOjFg0LSg3c/DzUoPluUB4Kk9gs4ifn+1cm2k6G9gjT/6Wi0xIW 0t7+a7XX8ttPO7T4ZFDJT2KDINz42Oeht5u82TuKgBfdQsXm9XnJLqckQbaCZLPs uvyAT8MrgBABOvCVF1yurHNrmu+js6fZxu4wAO7ANMBDzNPYYx0IphYRGCnBte1N j4vSiTtlFOdf8+gdM8lSlv4XV56e10MepNSvvFDd1De6l+ErLciZJQydDklX2e3R 1GlQmm3d5Sxn5Wob3FA4nkyqLq21RlH2RRgo -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUOjMm1b+ICXC682TSMH7wsFRdYMkwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTUwODA3WhcNMjIw NzIyMTUwODM3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMAT2ZvEAP0hzJlkVY7ZNY2sbiGpor9CAzL2Uaqd 9PtmL761XzboQHFziwDnffWdUKttZ5Gdc6KLFSPG8Bpl50PgKxwsUqr/2skLm1O/ nVPVlodr3AUkbQZ4Qc6/Fs/lhGbPglFFMXl7NVdkeOnyFUZtouXLq11A38rc6280 FzxMw16FEmwsLU/tWOwgAad7Q/Xp9l5HrlKN//+LUnaDcFmYXmNIK2CVTloUOqL3 9WPMR+CeXqGR1RkxTuJ0oy0GdLo2JMh9ndz7JQkMcU8VigJ7oqhvhxczoM/Lx0hB 8Y+wUMynn/VvbUO0uBRE1RpOpk5Zfxbb8TiYysN9PpzhVYMCAwEAAaOB7DCB6TAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoWSv9g7d oyvm9Xqspi9jH6kfhq8wHwYDVR0jBBgwFoAU+RF2dgdU6BZrOKHck1QpEmqPemUw OwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy MDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw JqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3 DQEBCwUAA4IBAQARw49qG9j/N46En+ME8u3uK0DmVxo/Fuj/RCNOBQ3EnIJ8zU+V hujyckHcQNQ0f8nfrdE1JHnn+bnhOIzO1qaaooSlmhWg1HuEolOn/g1Aq1xJvmOd nWKr4G1iwo3xmID6AYAI80LaVAeRcrk+S6dmhDuo1KT4CZmGNo+sz0rguute7aTh N568OGJF7qHoLFSn3/pe9zvpjuBiH9jiB1HmWbL/JqntTherr5JWK6W7EukGFNOd HEn/n/QfBuKX32QU8SjVb0ZRWd7CbJwL7nsNY6jnqlwLnCOyncL8hhnW6U8QbHqd dm1l9PpE7BssfVTk/exS/Icemf6XFzDeIrqT -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDNTCCAh2gAwIBAgIUfDvqkDZG/fr7np0RZ50wHl2VO+QwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTQyMjU1WhcNMzIw NjE3MTQyMzI1WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAJkM2EYB3+5S70a+aZpJeyvN8wY1wNnRIStPnJCq uIlgMGu3u+c/fVUwpsxh+KSGMfLnA8vdVmZhkjd+Si97RlqV4yxA6OfJYlSyrOBH ODA3E+J302gekWPGd851EpbAx+1MDekWoGprCEaiJCVIulNAgDWSHQHqfHNKJMox GMxvCKhse+NcVMZoRUxBPG9pgKeb7gXIXlnPVOKeZAZYzzUCrU0x/OHCD6xKOVNN t8mmncKmUlQach8D1r0LA1IvNCYjhNF4+RGIYBAuuym2nKXI4aj3mtYrlDSnnBwZ T3/8CdMwf0qy10KCExDHc+Yyge6EcbJBLnfYkIjOj/PzT8UCAwEAAaN7MHkwDgYD VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPkRdnYHVOgW azih3JNUKRJqj3plMB8GA1UdIwQYMBaAFPkRdnYHVOgWazih3JNUKRJqj3plMBYG A1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA+fKtmp+8j J6KcS9aeiNgyVc2RuZQBVMkn0l9z6c6+zZwn7LbQ9LmoTfcbE8aXj3gKJlYGD+p6 6fCClmQjq8epWdT/HuO/5efFiiRsCan1eaKoK7dah9z39kfYxaXrOmG6nXjFDn0m u+ZxBtuW3kkib0MYnVvJDe/xJ8pg2J8Mt1tAqcbDbJZWW+yEH4XFJdjDt9LxeNm7 0y2BzDfr1Ckzh/rHowHEBDX/1a2rGgdU7n5j5mgMYKCcP5G+dlWhm3kztFo2aRni RE939WmlzLob9vgKBEkO7hfcs4kMO6TwDJx0/BZ3OpM54mgHf/DXkiY/AIsRYBp9 8iJESkjzeDAA -----END CERTIFICATE----- ] certificate -----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUT2afmPzCe/DQFO4c/Re0H7nC0jMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTQzMTA0WhcNMjIw NzIyMTQzMTM0WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMAT2ZvEAP0hzJlkVY7ZNY2sbiGpor9CAzL2Uaqd 9PtmL761XzboQHFziwDnffWdUKttZ5Gdc6KLFSPG8Bpl50PgKxwsUqr/2skLm1O/ nVPVlodr3AUkbQZ4Qc6/Fs/lhGbPglFFMXl7NVdkeOnyFUZtouXLq11A38rc6280 FzxMw16FEmwsLU/tWOwgAad7Q/Xp9l5HrlKN//+LUnaDcFmYXmNIK2CVTloUOqL3 9WPMR+CeXqGR1RkxTuJ0oy0GdLo2JMh9ndz7JQkMcU8VigJ7oqhvhxczoM/Lx0hB 8Y+wUMynn/VvbUO0uBRE1RpOpk5Zfxbb8TiYysN9PpzhVYMCAwEAAaOB7DCB6TAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoWSv9g7d oyvm9Xqspi9jH6kfhq8wHwYDVR0jBBgwFoAUoWSv9g7doyvm9Xqspi9jH6kfhq8w OwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy MDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw JqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3 DQEBCwUAA4IBAQByO9DyRVJYjK9xrnfNnD1pbG0WXWRmsk7dxtXY336ollMl0eqO uEEHNGy0a4c1UKOjFg0LSg3c/DzUoPluUB4Kk9gs4ifn+1cm2k6G9gjT/6Wi0xIW 0t7+a7XX8ttPO7T4ZFDJT2KDINz42Oeht5u82TuKgBfdQsXm9XnJLqckQbaCZLPs uvyAT8MrgBABOvCVF1yurHNrmu+js6fZxu4wAO7ANMBDzNPYYx0IphYRGCnBte1N j4vSiTtlFOdf8+gdM8lSlv4XV56e10MepNSvvFDd1De6l+ErLciZJQydDklX2e3R 1GlQmm3d5Sxn5Wob3FA4nkyqLq21RlH2RRgo -----END CERTIFICATE----- issuer_id f4baa074-10d7-7fc3-1623-f6f906ceed98 issuer_name root-2024 key_id 775fb40a-4568-f613-2c6d-0f6802e859e1 leaf_not_after_behavior err manual_chain <nil> usage read-only,issuing-certificates,crl-signing
Create the cross-signed Intermediate CSR.
$ curl \ --silent \ --request PUT \ --header "X-Vault-Request: true" \ --header "X-Vault-Token: $(vault print token)" \ --data '{"common_name":"example.com","key_ref":"'$(vault read pki/issuer/root-2024 | grep -i key_id | awk '{print $2}')'"}' \ "$VAULT_ADDR"/v1/pki/intermediate/cross-sign \ | jq '.data.csr' \ | tee cross-signed-intermediate.csr
Example output:
"-----BEGIN CERTIFICATE REQUEST-----\nMIIChDCCAWwCAQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3\nDQEBAQUAA4IBDwAwggEKAoIBAQDZ1V3OvmLb6L95OvL6NxxMziico3T4bxh7bJNU\nYbCHzqDI2LQk7cTpqLj9kllRPNfckD+zHo3zsV40ToX1/4FLzJP/xE6l+kavgymI\n6atp3wB1dykCv4UNVVicd+7sGs8BqUn3e7KPE758apYymZ8u8wv2SmkS27fDqqDH\n5+sTh/CBD8NO/fMJoOf1Ewcha3/7szdXeLV/dAWrZ6dHw4XsSvQe2A6uQi9zWsUJ\nyIMXfa8ttetJ4NAucPRX6mwVUgYWxj278HhCAb2j9PEuEiKAJhuoFtoBa1PwOMwH\nYJ1lNaifnkvA9MxWTS2wo4h8qsoN0NJR9u+L/1bx1u5iwb0bAgMBAAGgKTAnBgkq\nhkiG9w0BCQ4xGjAYMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEB\nCwUAA4IBAQAbWckGlLZGCYOWbbXNESvRGSbFc5+Tk6dcoOcb33r04GsfQsWTSvNh\nMmqm8ovBB2fSYxRZvZ8ciQ232XqnrKfySZ5NdTxitc52U2gWqsT64I1rwmSfvKFT\nxU23ILsAf0YSPnB1+jZ6u1Oqzj5jVsXJPB4Goyu2vCyxmwNKd8Op7FGosFoA5vl6\nji4S70L0ClR8B8Pf4i0iNbtCqPX+diMEezfgOiPv9EfCkqM+Sbh09sWHe7VTucWO\nCWiYnKYs2OKWM9S3B2shlH1rluNQe2R6UaovxAt6jlDwQYbqPmqJb3buicSdCoKI\nOZZ0VTha98YlwHrdEQwPH2brOL0ZAMi9\n-----END CERTIFICATE REQUEST-----"
Create a payload file for the intermediate information.
$ tee > payload-intermediate-csr.json << EOF { "common_name": "example.com", "csr": $(cat cross-signed-intermediate.csr) } EOF
Sign the resulting CSR with the older root CA.
$ curl \ --silent \ --request PUT \ --header "X-Vault-Request: true" \ --header "X-Vault-Token: $(vault print token)" \ --data @payload-intermediate-csr.json \ "$VAULT_ADDR"/v1/pki/issuer/root-2023/sign-intermediate \ | jq '.data.certificate' | tee cross-signed-intermediate.crt
Example output:
"-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUG4zofnl6wpYyuzikKXh11Oe6JHgwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTk0MTMzWhcNMjIw\nNzIyMTk0MjAzWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANnVXc6+Ytvov3k68vo3HEzOKJyjdPhvGHtsk1Rh\nsIfOoMjYtCTtxOmouP2SWVE819yQP7MejfOxXjROhfX/gUvMk//ETqX6Rq+DKYjp\nq2nfAHV3KQK/hQ1VWJx37uwazwGpSfd7so8TvnxqljKZny7zC/ZKaRLbt8OqoMfn\n6xOH8IEPw0798wmg5/UTByFrf/uzN1d4tX90Batnp0fDhexK9B7YDq5CL3NaxQnI\ngxd9ry2160ng0C5w9FfqbBVSBhbGPbvweEIBvaP08S4SIoAmG6gW2gFrU/A4zAdg\nnWU1qJ+eS8D0zFZNLbCjiHyqyg3Q0lH274v/VvHW7mLBvRsCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOO/cNV1d\nogLUPuiXvryXlohDm4owHwYDVR0jBBgwFoAUawEF1rPbTI4vc2/ORwjlRTR4dasw\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQC1peeI9oQGJUOrdknIiqg/uhQqe7yeuKwZS/Xg/ffN0Hn9Qc70\n5tgg6p3WXuGntG1EyE2l8uQ82kqzG4M9QqBMaNV3naXINdzp8ipFQfDnR0C6ekBn\nduxNgUkrVMqbUrsp7lpB1Ot+MbWXKbAY5fUVjEuXyKWBgPTEmAE/I13eZfPMLZNl\nyTjEyO3RdLZDN2SXGfvKykfN/DIazSMfV6/OhlLeiTh9mZB0ANbpmmmDu/tHVKGo\nRtjZ5J6nXxg8OJt2F0agCjz299GYWJ0Ng6sfcIqYtAmVegZc2ueVop2Sx6rIkAAa\nAmF4jUyMCxex6lnpTjIldU73BnVRVnOl2KGE\n-----END CERTIFICATE-----"
The result is a cross-signed certificate, but no issuer gets created yet. Import the signed certificate to create the new issuer.
Create a payload file.
$ cat > payload-cross-signed-cert.json << EOF { "certificate": $(cat cross-signed-intermediate.crt) } EOF
Import the cross-signed certificate.
$ curl \ --silent \ --request PUT \ --header "X-Vault-Request: true" \ --header "X-Vault-Token: $(vault print token)" \ --data @payload-cross-signed-cert.json \ "$VAULT_ADDR"/v1/pki/intermediate/set-signed \ | jq
Example output:
{ "request_id": "ef40177c-21d0-07aa-324e-6aef145f2185", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "imported_issuers": null, "imported_keys": null, "mapping": { "e81a37b1-dfb7-33a3-294b-6079ea4f9b2e": "3e46e483-a692-3a8d-6f33-2f9173c2f110" } }, "wrap_info": null, "warnings": null, "auth": null }
You now have 2 root CAs with a cross-signed intermediate CA providing a trust chain between them. You are ready to handle migration of devices which were not initially possible to migrate to the new root CA.
Read the issuer for the new root CA, and you'll notice that there are now more certificates in the
ca_chain
field.$ curl \ --silent \ --header "X-Vault-Request: true" \ --header "X-Vault-Token: $(vault print token)" \ "$VAULT_ADDR"/v1/pki/issuer/root-2024 \ | jq
{ "request_id": "1101f28b-4562-6f17-5117-e7030522e2de", "lease_id": "", "renewable": false, "lease_duration": 0, "data": { "ca_chain": [ "-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUVoV/wuZMv05MacS2Pry3VrLgGhQwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTkwMTE4WhcNMjIw\nNzIyMTkwMTQ3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANnVXc6+Ytvov3k68vo3HEzOKJyjdPhvGHtsk1Rh\nsIfOoMjYtCTtxOmouP2SWVE819yQP7MejfOxXjROhfX/gUvMk//ETqX6Rq+DKYjp\nq2nfAHV3KQK/hQ1VWJx37uwazwGpSfd7so8TvnxqljKZny7zC/ZKaRLbt8OqoMfn\n6xOH8IEPw0798wmg5/UTByFrf/uzN1d4tX90Batnp0fDhexK9B7YDq5CL3NaxQnI\ngxd9ry2160ng0C5w9FfqbBVSBhbGPbvweEIBvaP08S4SIoAmG6gW2gFrU/A4zAdg\nnWU1qJ+eS8D0zFZNLbCjiHyqyg3Q0lH274v/VvHW7mLBvRsCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOO/cNV1d\nogLUPuiXvryXlohDm4owHwYDVR0jBBgwFoAUOO/cNV1dogLUPuiXvryXlohDm4ow\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQAkvCg+Q2jLEtUE3zlEvVN8Qtb2+zz5MlHsDwKkqjZykpRjrIC7\nxTeLxHod/GbzE+7DQKWUCn8AqoFjx6X7uOKCliPZ7ndVUHtGddOfXN5/xxRi1EE3\nf4bPyxbq/4gdZFfbUuHw9ugVWIbpeWrgBJfUos47suAUyFxOkIfqKxdK5lHCO6cD\n/FExWtqlyLIyjw0v7vaoPi88SHzrEWuosSkJzOc3NcT3tLSyQg+t+sqIxfLdddqF\n6Orp6M9k1cC54h/8O8G24dq47SiqlhwGft+IZgD9NwsWfgV5r3PT96LhITNf6bLy\nXnUSjLh1FBUyuSyHTmkVTeOuxVCNPzr2BXEA\n-----END CERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUG4zofnl6wpYyuzikKXh11Oe6JHgwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTk0MTMzWhcNMjIw\nNzIyMTk0MjAzWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANnVXc6+Ytvov3k68vo3HEzOKJyjdPhvGHtsk1Rh\nsIfOoMjYtCTtxOmouP2SWVE819yQP7MejfOxXjROhfX/gUvMk//ETqX6Rq+DKYjp\nq2nfAHV3KQK/hQ1VWJx37uwazwGpSfd7so8TvnxqljKZny7zC/ZKaRLbt8OqoMfn\n6xOH8IEPw0798wmg5/UTByFrf/uzN1d4tX90Batnp0fDhexK9B7YDq5CL3NaxQnI\ngxd9ry2160ng0C5w9FfqbBVSBhbGPbvweEIBvaP08S4SIoAmG6gW2gFrU/A4zAdg\nnWU1qJ+eS8D0zFZNLbCjiHyqyg3Q0lH274v/VvHW7mLBvRsCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOO/cNV1d\nogLUPuiXvryXlohDm4owHwYDVR0jBBgwFoAUawEF1rPbTI4vc2/ORwjlRTR4dasw\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQC1peeI9oQGJUOrdknIiqg/uhQqe7yeuKwZS/Xg/ffN0Hn9Qc70\n5tgg6p3WXuGntG1EyE2l8uQ82kqzG4M9QqBMaNV3naXINdzp8ipFQfDnR0C6ekBn\nduxNgUkrVMqbUrsp7lpB1Ot+MbWXKbAY5fUVjEuXyKWBgPTEmAE/I13eZfPMLZNl\nyTjEyO3RdLZDN2SXGfvKykfN/DIazSMfV6/OhlLeiTh9mZB0ANbpmmmDu/tHVKGo\nRtjZ5J6nXxg8OJt2F0agCjz299GYWJ0Ng6sfcIqYtAmVegZc2ueVop2Sx6rIkAAa\nAmF4jUyMCxex6lnpTjIldU73BnVRVnOl2KGE\n-----END CERTIFICATE-----\n", "-----BEGIN CERTIFICATE-----\nMIIDNTCCAh2gAwIBAgIUa1fnttKmxiB1kV8EaWHAVqe1mrEwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTgyMDIzWhcNMzIw\nNjE3MTgyMDUyWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBALhtn4dJrslD2s00tt9yilRCmtS3/kJWHu/wjkBF\nkCUQ4eba/PhXKHbSntD+XVV+FYZsLLOpvSxJ3htR1ebaXKFbS6VWZRmEtbfmnYoh\nyZZFC0eK+82xa/DDuJsdGa6pMaeFA7RV50LuICZoHDBzlj416HUih8v3wEqdcD16\nb7Pu1Oro3qcj/xBQ9HOQ8qTT/FyWrUgWnk/N6y7BX2fpWYH91aiNjov2QzuFTOer\n8NBJTjqbfIJK5xb9lmuKJZ2P0715UJYhAtdgJaEcOd5pzQw6Wutnh/0Z93ryQozJ\n9Q67+XgRNCDsG6CpfVIc40x7nvC0s0EUCM/hwlgf1tdEBWcCAwEAAaN7MHkwDgYD\nVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGsBBdaz20yO\nL3NvzkcI5UU0eHWrMB8GA1UdIwQYMBaAFGsBBdaz20yOL3NvzkcI5UU0eHWrMBYG\nA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCztaw07tp3\nN7oRV2eiHYnvS0r3667IWqYz0T7+epnN5YuOXSNzjgB8Zbytk2mMKDCLg5mczITi\n/naybO9dczbx2SD9FhQwR3vjy+ykcI5NLjgjQ7EZZO6tyS8ZOjK9igrcp+5XG9fw\nkmp9q64em5ZKYwhR6OP2VgUrgn6MR40BFvhRA5oXH6M+UmYebWNqBB8R2z0BTf+U\nOeO9jpsLDVuhQlis91TKJXTZS6MuZwJB5e8H5/foQXKrrprmKZf2nn95qUApwn7H\nd6y6jaa9zBu8w+HZsamZM9Qi93Vg/5XoGcuTm4XE9zcM2DKPtn79TlrElpdXEH+/\nbs7CfJ5Phq5t\n-----END CERTIFICATE-----\n" ], "certificate": "-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUVoV/wuZMv05MacS2Pry3VrLgGhQwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTkwMTE4WhcNMjIw\nNzIyMTkwMTQ3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBANnVXc6+Ytvov3k68vo3HEzOKJyjdPhvGHtsk1Rh\nsIfOoMjYtCTtxOmouP2SWVE819yQP7MejfOxXjROhfX/gUvMk//ETqX6Rq+DKYjp\nq2nfAHV3KQK/hQ1VWJx37uwazwGpSfd7so8TvnxqljKZny7zC/ZKaRLbt8OqoMfn\n6xOH8IEPw0798wmg5/UTByFrf/uzN1d4tX90Batnp0fDhexK9B7YDq5CL3NaxQnI\ngxd9ry2160ng0C5w9FfqbBVSBhbGPbvweEIBvaP08S4SIoAmG6gW2gFrU/A4zAdg\nnWU1qJ+eS8D0zFZNLbCjiHyqyg3Q0lH274v/VvHW7mLBvRsCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOO/cNV1d\nogLUPuiXvryXlohDm4owHwYDVR0jBBgwFoAUOO/cNV1dogLUPuiXvryXlohDm4ow\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQAkvCg+Q2jLEtUE3zlEvVN8Qtb2+zz5MlHsDwKkqjZykpRjrIC7\nxTeLxHod/GbzE+7DQKWUCn8AqoFjx6X7uOKCliPZ7ndVUHtGddOfXN5/xxRi1EE3\nf4bPyxbq/4gdZFfbUuHw9ugVWIbpeWrgBJfUos47suAUyFxOkIfqKxdK5lHCO6cD\n/FExWtqlyLIyjw0v7vaoPi88SHzrEWuosSkJzOc3NcT3tLSyQg+t+sqIxfLdddqF\n6Orp6M9k1cC54h/8O8G24dq47SiqlhwGft+IZgD9NwsWfgV5r3PT96LhITNf6bLy\nXnUSjLh1FBUyuSyHTmkVTeOuxVCNPzr2BXEA\n-----END CERTIFICATE-----\n", "issuer_id": "8247c31f-ad1e-001c-6ad9-0aacc0aebea2", "issuer_name": "root-2024", "key_id": "3e46e483-a692-3a8d-6f33-2f9173c2f110", "leaf_not_after_behavior": "err", "manual_chain": null, "usage": "read-only,issuing-certificates,crl-signing" }, "wrap_info": null, "warnings": null, "auth": null }
Specifically, you'll notice 3 certificates in ca_chain
, as follows:
The new root CA certificate (root-2024).
The new cross-signed intermediate CA certificate.
The older root CA certificate (root-2023).
If you do not want the chain composed in this way, you can specify a custom chain with the manual_chain
parameter when creating the intermediate CA. More information is available in the
API documentation.
Step 9: create a cross-signed intermediate
The previous section describes creating a root bridge certificate that is useful for bridging between two separate roots of trust. In other words, if you have devices with root-2023 hard-coded and not replaceable, the root bridge CA allows you to validate against it.
You can also create a cross-signed intermediate CA to achieve this in an alternative way.
Two approaches to root CA rotation
You should choose to either create a cross-signed intermediate as described in this section, or create a root bridge CA as described in the previous section for your migration strategy, but do not perform the tasks for both steps.
Follow these steps to create the cross-signed intermediate CA.
Get a CSR to cross-sign the intermediate.
$ vault write -format=json pki_int/intermediate/cross-sign \ common_name="example.com Intermediate Authority" \ key_ref="$(vault read pki_int/issuer/$(vault read -field=default pki_int/config/issuers) \ | grep -i key_id | awk '{print $2}')" \ | jq -r '.data.csr' \ | tee cross-signed-intermediate.csr
Example output:
-----BEGIN CERTIFICATE REQUEST----- MIICcjCCAVoCAQAwLTErMCkGA1UEAxMiZXhhbXBsZS5jb20gSW50ZXJtZWRpYXRl IEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALRWH4ag H8Lh4eRTS9qVN4zsuovY+aHxmAf5Yt9Atwbd7obx/OdpiAmeHjH03hZlo0/GV6UZ 43QuR/WqYZgIHvcDMNZaqYMy4Bp6PYDDDHTEm43oOkDvA/ZslWI/nt/f1o6D/O4R 6JvQaE5GrzANyUhmq3BrvYpof8abvZpchCeCtAKyD7vmLrtsVAH2fGpYboTV02zL 5+HZY3CKxoELhrVab7hArnq6VLQvHY6nvC5y7XV6D/ze+BuVNfKRDhapZzDPr2CQ Y0RWlOGGopJ1QBUp2AMlY/0PwJhO7WKpPudZw9YZd7sncLuJiX5sVOUzBafEww9B kz/1pDsAesr89GkCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQA17CFuLX3MDfkb e4E+O1saZHnYgJ4lRurwg+E/CMPNHve8wkrtk/zv5cPJ6GSNusuEW/QPtSSpRsjn Qj32qtEYqzNQhGpMGHh0VEEgjXYNtHp1dnlUodZQe2+1+u/aPeniblpEM4e/joER 5AlxqlOZXUEz6rM8ExdMkPhN4pN3kNU/aaBVhEC8pxTig2kWR3uVMmF3DWSgkVNP SjJjbPFSEnKuIIHMfkwjv4QFnWhkgiNzwlKjmjK9WQD7JDkX9davkcr4AUAi23Js NxfOqlTNDke66lP1+GlUnStNy7XlcaMFuOSBQr30vtyn9G190XhA1m3dHtBqwehP zaC6UpJT -----END CERTIFICATE REQUEST-----
Sign the CSR under the new root.
$ vault write -format=json pki/issuer/root-2024/sign-intermediate \ common_name="example.com Intermediate Authority" \ csr=@cross-signed-intermediate.csr \ | jq -r '.data.certificate' | tee cross-signed-intermediate.crt
Example output:
-----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUV4yUTVdgE/E50pbw0u1eDOicToMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk1MjI2WhcNMjMw OTIzMTk1MjU2WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBR2Mdo2xl0gsSOc0mOwpx9Tx8AaeDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAAuJDED9c4gNf0eOJZbglDKUID1CdVSdwXqLKX4JycFxWvraLJkl y61ulx7bdEURhvMP4Df38sxYDkjNel+Ma5Pk5IGXzKezRdxHTf3uMQOUfLaH6AUq 0gJiAqNjBBR+iJiP6/dEkv2DUnevXd0CcMTwDGS1Hu0M82DQ8k52+b9qRuAks6hJ il952LYSe+rvcXlWsw1isppEMDIYlwsfzRv/enaIXOlTAfNF+n4RJLHlrNjdFsmc dxbfFjojiyfKVpgayA54rkB/iCrqVxNM4hGRpjfk6EGtZ0gAbisr9TDNbvynrUaV +bgMuk68oTgOwVgkZw+By5G8pVY1AJVKA6E= -----END CERTIFICATE-----
Import the cross-signed certificate into the new mount.
$ vault write pki_int/intermediate/set-signed \ certificate=@cross-signed-intermediate.crt
Example abbreviated output:
...snip... Key Value --- ----- existing_issuers <nil> existing_keys <nil> imported_issuers [58dabd1a-73ee-c483-5371-add26cb1c48b] imported_keys <nil> mapping map[58dabd1a-73ee-c483-5371-add26cb1c48b:80375808-3015-91fc-ab36-b88e5b91d645] ...snip...
Set the issuer name.
$ vault write pki_int/issuer/58dabd1a-73ee-c483-5371-add26cb1c48b \ issuer_name=xc-example-dot-com-intermediate
Be sure to use the correct
imported_issuers
UUID for the value of issuer instead of the example value.Key Value --- ----- ca_chain [-----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUV4yUTVdgE/E50pbw0u1eDOicToMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk1MjI2WhcNMjMw OTIzMTk1MjU2WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBR2Mdo2xl0gsSOc0mOwpx9Tx8AaeDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAAuJDED9c4gNf0eOJZbglDKUID1CdVSdwXqLKX4JycFxWvraLJkl y61ulx7bdEURhvMP4Df38sxYDkjNel+Ma5Pk5IGXzKezRdxHTf3uMQOUfLaH6AUq 0gJiAqNjBBR+iJiP6/dEkv2DUnevXd0CcMTwDGS1Hu0M82DQ8k52+b9qRuAks6hJ il952LYSe+rvcXlWsw1isppEMDIYlwsfzRv/enaIXOlTAfNF+n4RJLHlrNjdFsmc dxbfFjojiyfKVpgayA54rkB/iCrqVxNM4hGRpjfk6EGtZ0gAbisr9TDNbvynrUaV +bgMuk68oTgOwVgkZw+By5G8pVY1AJVKA6E= -----END CERTIFICATE----- ] certificate -----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUV4yUTVdgE/E50pbw0u1eDOicToMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk1MjI2WhcNMjMw OTIzMTk1MjU2WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBR2Mdo2xl0gsSOc0mOwpx9Tx8AaeDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAAuJDED9c4gNf0eOJZbglDKUID1CdVSdwXqLKX4JycFxWvraLJkl y61ulx7bdEURhvMP4Df38sxYDkjNel+Ma5Pk5IGXzKezRdxHTf3uMQOUfLaH6AUq 0gJiAqNjBBR+iJiP6/dEkv2DUnevXd0CcMTwDGS1Hu0M82DQ8k52+b9qRuAks6hJ il952LYSe+rvcXlWsw1isppEMDIYlwsfzRv/enaIXOlTAfNF+n4RJLHlrNjdFsmc dxbfFjojiyfKVpgayA54rkB/iCrqVxNM4hGRpjfk6EGtZ0gAbisr9TDNbvynrUaV +bgMuk68oTgOwVgkZw+By5G8pVY1AJVKA6E= -----END CERTIFICATE----- crl_distribution_points [] issuer_id 58dabd1a-73ee-c483-5371-add26cb1c48b issuer_name xc-example-dot-com-intermediate issuing_certificates [] key_id 80375808-3015-91fc-ab36-b88e5b91d645 leaf_not_after_behavior err manual_chain <nil> ocsp_servers [] revocation_signature_algorithm n/a revoked false usage crl-signing,issuing-certificates,ocsp-signing,read-only
When reading issuers, the CA chain does not change. Try reading the intermediate CA:
$ vault read -field=ca_chain pki_int/issuer/"$(vault read -field=default pki_int/config/issuers)"
Example output:
[-----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUP/K4tDfqvxdDokLmfVQ0SCcjBS8wDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk0MzQzWhcNMjgw ODIwMTk0NDEzWjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBTS5JNOBLW/rwXBFO2Ol5zpfO9wnzA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAGQUfS3m41bC0lpEq7/KpVxGeIFutct21jWQX1AmORXFiu1yK+cA JJnDd1bRUE8MhhGH1lmFPZ8dpVvC/wm4Q1TvQGo1K82z4sUh1IDFqTMJOhwYYsQk rKqLCPcdcWxtgRnYldUp1AHnObFvQvJlJndZVpMfxRamTaLQjDEon7ItldOR4ZLo SCg18ZvUlqQk/bzosPq7pvmmShGpnX5ln02J1+6BYMZTbxn/FBjWIb466Bquc1+l CWHuOkHvnzAQ00oFKWTvEXr0EobWWANLuFnWiite8ZTwZjpkYPoXpOnK9dmk61rV +AlTy7qovM4Y5Yjg2uOA6FZbg3SW11RLEUA= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDNTCCAh2gAwIBAgIUezeBwAzYGi0DTibs/fKPq0zpBS8wDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk0MjU3WhcNMzMw ODE5MTk0MzI3WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAL9LxaXPq/iBmifhy9GEQaHpdmPrvGvH2oCS3Ejd eE5Zjb2Fm8R1HTYcWQ4HtLygG4rKdzKv8cXaMSOAXCcyyIgMsQxIkYsXbq+agJhB /AiGiH0lhx1vy6YmHUGqo8oGmWLMp4pGaFruz/Ij2V3K1a4WMAXW3+/NjQu871bI HVe+RfoIK3ZxM8vNGSq8SJ/7gh3XxZ4Cq+x3uMlKknSNpZc/AWQIEuzp78gYGKtt Tm4XAlY63Vb972d4m2KMY3Ux2NJDP9otSKwDRRFBAyY0ktTJkrEFgioA6F5qo3wz NwW55x8iTI12m2OSdUGg7PoMZBuOWm9KF/NTgdpHlkL9pRUCAwEAAaN7MHkwDgYD VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNLkk04Etb+v BcEU7Y6XnOl873CfMB8GA1UdIwQYMBaAFNLkk04Etb+vBcEU7Y6XnOl873CfMBYG A1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAVy+u6KEO1 6pEetpLFDrZPVQi2+DC76lkcHf3KnevUzKtbzRW4VipiI88Uj/9B7YXQFQXmHs3Q HibrTNUZbhqF3S25PnVGaJutlqC4qWmxwAWthOEFhrZRyRuIiEQrjriSNj4liCnt TmAT+GCEdqdKoQ/MclSEfThLPpk9Gm4REv2EX1savDLOxt3AkQTpLkwqK79+H1Jj rcyR9Dsq5db7oxZdMCk47wxcwJvwv5TLub+/MVfiFFLEkpucTf0A/i0bY30IXUAI IUoULskw4l2rAWC88sxA/ve3bXsESTXKbafLWKfp7WmnW3kBiRlCBLSt4JnRb3u+ ZeJPV8vHwlyi -----END CERTIFICATE----- ]
Read the cross-signed intermediate CA.
$ vault read -field=ca_chain pki_int/issuer/xc-example-dot-com-intermediate
Example output:
[-----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUV4yUTVdgE/E50pbw0u1eDOicToMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk1MjI2WhcNMjMw OTIzMTk1MjU2WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBR2Mdo2xl0gsSOc0mOwpx9Tx8AaeDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAAuJDED9c4gNf0eOJZbglDKUID1CdVSdwXqLKX4JycFxWvraLJkl y61ulx7bdEURhvMP4Df38sxYDkjNel+Ma5Pk5IGXzKezRdxHTf3uMQOUfLaH6AUq 0gJiAqNjBBR+iJiP6/dEkv2DUnevXd0CcMTwDGS1Hu0M82DQ8k52+b9qRuAks6hJ il952LYSe+rvcXlWsw1isppEMDIYlwsfzRv/enaIXOlTAfNF+n4RJLHlrNjdFsmc dxbfFjojiyfKVpgayA54rkB/iCrqVxNM4hGRpjfk6EGtZ0gAbisr9TDNbvynrUaV +bgMuk68oTgOwVgkZw+By5G8pVY1AJVKA6E= -----END CERTIFICATE----- ]
(optional) If you prefer to update them to refer to each other on sign requests, you can use
manual_chain
since they are sibling CAs, not in a hierarchy, and automated chain building does not detect them.Set a
manual_chain
value on the intermediate CA.$ vault patch pki_int/issuer/"$(vault read -field=default pki_int/config/issuers)" \ manual_chain=self,xc-example-dot-com-intermediate
Key Value --- ----- ca_chain [-----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUP/K4tDfqvxdDokLmfVQ0SCcjBS8wDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk0MzQzWhcNMjgw ODIwMTk0NDEzWjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBTS5JNOBLW/rwXBFO2Ol5zpfO9wnzA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAGQUfS3m41bC0lpEq7/KpVxGeIFutct21jWQX1AmORXFiu1yK+cA JJnDd1bRUE8MhhGH1lmFPZ8dpVvC/wm4Q1TvQGo1K82z4sUh1IDFqTMJOhwYYsQk rKqLCPcdcWxtgRnYldUp1AHnObFvQvJlJndZVpMfxRamTaLQjDEon7ItldOR4ZLo SCg18ZvUlqQk/bzosPq7pvmmShGpnX5ln02J1+6BYMZTbxn/FBjWIb466Bquc1+l CWHuOkHvnzAQ00oFKWTvEXr0EobWWANLuFnWiite8ZTwZjpkYPoXpOnK9dmk61rV +AlTy7qovM4Y5Yjg2uOA6FZbg3SW11RLEUA= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUV4yUTVdgE/E50pbw0u1eDOicToMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk1MjI2WhcNMjMw OTIzMTk1MjU2WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBR2Mdo2xl0gsSOc0mOwpx9Tx8AaeDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAAuJDED9c4gNf0eOJZbglDKUID1CdVSdwXqLKX4JycFxWvraLJkl y61ulx7bdEURhvMP4Df38sxYDkjNel+Ma5Pk5IGXzKezRdxHTf3uMQOUfLaH6AUq 0gJiAqNjBBR+iJiP6/dEkv2DUnevXd0CcMTwDGS1Hu0M82DQ8k52+b9qRuAks6hJ il952LYSe+rvcXlWsw1isppEMDIYlwsfzRv/enaIXOlTAfNF+n4RJLHlrNjdFsmc dxbfFjojiyfKVpgayA54rkB/iCrqVxNM4hGRpjfk6EGtZ0gAbisr9TDNbvynrUaV +bgMuk68oTgOwVgkZw+By5G8pVY1AJVKA6E= -----END CERTIFICATE----- ] certificate -----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUP/K4tDfqvxdDokLmfVQ0SCcjBS8wDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk0MzQzWhcNMjgw ODIwMTk0NDEzWjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBTS5JNOBLW/rwXBFO2Ol5zpfO9wnzA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAGQUfS3m41bC0lpEq7/KpVxGeIFutct21jWQX1AmORXFiu1yK+cA JJnDd1bRUE8MhhGH1lmFPZ8dpVvC/wm4Q1TvQGo1K82z4sUh1IDFqTMJOhwYYsQk rKqLCPcdcWxtgRnYldUp1AHnObFvQvJlJndZVpMfxRamTaLQjDEon7ItldOR4ZLo SCg18ZvUlqQk/bzosPq7pvmmShGpnX5ln02J1+6BYMZTbxn/FBjWIb466Bquc1+l CWHuOkHvnzAQ00oFKWTvEXr0EobWWANLuFnWiite8ZTwZjpkYPoXpOnK9dmk61rV +AlTy7qovM4Y5Yjg2uOA6FZbg3SW11RLEUA= -----END CERTIFICATE----- crl_distribution_points [] issuer_id 4e2e04ff-de9b-10ff-59e1-ccaa8247e1ae issuer_name n/a issuing_certificates [] key_id 80375808-3015-91fc-ab36-b88e5b91d645 leaf_not_after_behavior err manual_chain [4e2e04ff-de9b-10ff-59e1-ccaa8247e1ae 58dabd1a-73ee-c483-5371-add26cb1c48b] ocsp_servers [] revocation_signature_algorithm n/a revoked false usage crl-signing,issuing-certificates,ocsp-signing,read-only
Set a
manual_chain
value on the cross-signed intermediate CA.$ vault patch pki_int/issuer/xc-example-dot-com-intermediate \ manual_chain=self,"$(vault read -field=default pki_int/config/issuers)"
--- ----- ca_chain [-----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUV4yUTVdgE/E50pbw0u1eDOicToMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk1MjI2WhcNMjMw OTIzMTk1MjU2WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBR2Mdo2xl0gsSOc0mOwpx9Tx8AaeDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAAuJDED9c4gNf0eOJZbglDKUID1CdVSdwXqLKX4JycFxWvraLJkl y61ulx7bdEURhvMP4Df38sxYDkjNel+Ma5Pk5IGXzKezRdxHTf3uMQOUfLaH6AUq 0gJiAqNjBBR+iJiP6/dEkv2DUnevXd0CcMTwDGS1Hu0M82DQ8k52+b9qRuAks6hJ il952LYSe+rvcXlWsw1isppEMDIYlwsfzRv/enaIXOlTAfNF+n4RJLHlrNjdFsmc dxbfFjojiyfKVpgayA54rkB/iCrqVxNM4hGRpjfk6EGtZ0gAbisr9TDNbvynrUaV +bgMuk68oTgOwVgkZw+By5G8pVY1AJVKA6E= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUP/K4tDfqvxdDokLmfVQ0SCcjBS8wDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk0MzQzWhcNMjgw ODIwMTk0NDEzWjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBTS5JNOBLW/rwXBFO2Ol5zpfO9wnzA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAGQUfS3m41bC0lpEq7/KpVxGeIFutct21jWQX1AmORXFiu1yK+cA JJnDd1bRUE8MhhGH1lmFPZ8dpVvC/wm4Q1TvQGo1K82z4sUh1IDFqTMJOhwYYsQk rKqLCPcdcWxtgRnYldUp1AHnObFvQvJlJndZVpMfxRamTaLQjDEon7ItldOR4ZLo SCg18ZvUlqQk/bzosPq7pvmmShGpnX5ln02J1+6BYMZTbxn/FBjWIb466Bquc1+l CWHuOkHvnzAQ00oFKWTvEXr0EobWWANLuFnWiite8ZTwZjpkYPoXpOnK9dmk61rV +AlTy7qovM4Y5Yjg2uOA6FZbg3SW11RLEUA= -----END CERTIFICATE----- ] certificate -----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIUV4yUTVdgE/E50pbw0u1eDOicToMwDQYJKoZIhvcNAQEL BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwODIyMTk1MjI2WhcNMjMw OTIzMTk1MjU2WjAtMSswKQYDVQQDEyJleGFtcGxlLmNvbSBJbnRlcm1lZGlhdGUg QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtFYfhqAf wuHh5FNL2pU3jOy6i9j5ofGYB/li30C3Bt3uhvH852mICZ4eMfTeFmWjT8ZXpRnj dC5H9aphmAge9wMw1lqpgzLgGno9gMMMdMSbjeg6QO8D9myVYj+e39/WjoP87hHo m9BoTkavMA3JSGarcGu9imh/xpu9mlyEJ4K0ArIPu+Yuu2xUAfZ8alhuhNXTbMvn 4dljcIrGgQuGtVpvuECuerpUtC8djqe8LnLtdXoP/N74G5U18pEOFqlnMM+vYJBj RFaU4YaiknVAFSnYAyVj/Q/AmE7tYqk+51nD1hl3uydwu4mJfmxU5TMFp8TDD0GT P/WkOwB6yvz0aQIDAQABo4HUMIHRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBSnjahgWrDluYwFlvSie5k5MaHzRDAfBgNVHSMEGDAW gBR2Mdo2xl0gsSOc0mOwpx9Tx8AaeDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH MAKGH2h0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwMQYDVR0fBCowKDAm oCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jcmwwDQYJKoZIhvcN AQELBQADggEBAAuJDED9c4gNf0eOJZbglDKUID1CdVSdwXqLKX4JycFxWvraLJkl y61ulx7bdEURhvMP4Df38sxYDkjNel+Ma5Pk5IGXzKezRdxHTf3uMQOUfLaH6AUq 0gJiAqNjBBR+iJiP6/dEkv2DUnevXd0CcMTwDGS1Hu0M82DQ8k52+b9qRuAks6hJ il952LYSe+rvcXlWsw1isppEMDIYlwsfzRv/enaIXOlTAfNF+n4RJLHlrNjdFsmc dxbfFjojiyfKVpgayA54rkB/iCrqVxNM4hGRpjfk6EGtZ0gAbisr9TDNbvynrUaV +bgMuk68oTgOwVgkZw+By5G8pVY1AJVKA6E= -----END CERTIFICATE----- crl_distribution_points [] issuer_id 58dabd1a-73ee-c483-5371-add26cb1c48b issuer_name xc-example-dot-com-intermediate issuing_certificates [] key_id 80375808-3015-91fc-ab36-b88e5b91d645 leaf_not_after_behavior err manual_chain [58dabd1a-73ee-c483-5371-add26cb1c48b 4e2e04ff-de9b-10ff-59e1-ccaa8247e1ae] ocsp_servers [] revocation_signature_algorithm n/a revoked false usage crl-signing,issuing-certificates,ocsp-signing,read-only
Create a CSR payload.
$ cat > payload-cross-sign-csr.json << EOF
{
"common_name": "example.com Intermediate Authority",
"key_ref": "$(curl --request GET --silent "$VAULT_ADDR"/v1/pki_int/issuer/example-dot-com-intermediate/json | jq -r '.data.issuer_id')"
}
EOF
This command is expected to produce no output.
Get a CSR to cross-sign the intermediate.
$ curl \
--header "X-Vault-Token: root" \
--silent \
--request POST \
--data @payload-cross-sign-csr.json \
$VAULT_ADDR/v1/pki/intermediate/generate/exported \
| jq '.data.csr' \
| tee cross-signed-intermediate.csr
Example output:
"-----BEGIN CERTIFICATE REQUEST-----\nMIICcjCCAVoCAQAwLTErMCkGA1UEAxMiZXhhbXBsZS5jb20gSW50ZXJtZWRpYXRl\nIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMoQyla1\nwVGlVQOg108m0WbyC6mhJUVlgj0lpi0kxcnLHAe1r7Y22CCPIhMHAJcW+0YCFLQH\nE2quVBUb7KjoqPWDTgGHDqLgnbHg5rF0DcjkglImYErM/yvJ53KTwe/a4Ejva/w5\nqTnswLqi3oLXH6OQ52+yOoQrgAK1Uny8/oVm7V6d8QzMHnvEdZkil4AsPayojDds\nTpxpwBWTeATku2mz9tC4m1kS3laMsJCj3JH8H1/fNHPgnXJL6b44pFAGv/02qGbg\nGVefrcfjm42CaW6lTQERLJ+G2Q35ajutRvXSZcVlM6o1wQ3Qk4dhu/kud+C/9EVL\nLOxAA2XMz0Y4kuUCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBxYwNG/diUJ3dD\n4UcfNcPQuG3Gyh5hu/xvMtttczDV6TkA4w1FV26YEYg4TOfBiAVDFs9HZ6s8+SxP\nptLOd7QPOCylhCnvWiVKQIanmJWL4DXOwrNCKA4QMMp2CptJMAtvoh1aHjm8wooB\n6rScu9O4em0qFw/GjJFTOkssyr/n+/tL/OTQj1xar0M+UJMixd5cIABtbJ2Rx1JE\nkWbdpFBa/NcH5O7TuytyA5Dp8mMsH2WXHqEsnUuUrarLnUG1gSmM4CAH+c0cknA2\n9Ou1cfO5xWl/J2BWErVHhMxQuDcbfjidNzWo4j8HU5fmfS1gllxoYvewXW+Hq2rY\nvDs8e989\n-----END CERTIFICATE REQUEST-----"
Create a payload for signing the CSR under the new root CA.
$ cat > payload-new-root-sign.json << EOF
{
"csr": $(cat cross-signed-intermediate.csr),
"common_name": "example.com"
}
EOF
Sign the CSR under the new root.
$ curl \
--header "X-Vault-Token: root" \
--silent \
--request POST \
--data @payload-new-root-sign.json \
$VAULT_ADDR/v1/pki/issuer/root-2024/sign-intermediate \
| jq '.data.certificate' | tee cross-signed-intermediate.crt
Example output:
"-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUe0CLNyVIn6vjoMZF3gyBf8iqOKowDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwOTE4MjAxMDE1WhcNMjMx\nMDIwMjAxMDQ1WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMoQyla1wVGlVQOg108m0WbyC6mhJUVlgj0lpi0k\nxcnLHAe1r7Y22CCPIhMHAJcW+0YCFLQHE2quVBUb7KjoqPWDTgGHDqLgnbHg5rF0\nDcjkglImYErM/yvJ53KTwe/a4Ejva/w5qTnswLqi3oLXH6OQ52+yOoQrgAK1Uny8\n/oVm7V6d8QzMHnvEdZkil4AsPayojDdsTpxpwBWTeATku2mz9tC4m1kS3laMsJCj\n3JH8H1/fNHPgnXJL6b44pFAGv/02qGbgGVefrcfjm42CaW6lTQERLJ+G2Q35ajut\nRvXSZcVlM6o1wQ3Qk4dhu/kud+C/9EVLLOxAA2XMz0Y4kuUCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUpUGAwNEo\nWg7ELmvnURmzI1McQawwHwYDVR0jBBgwFoAUjx9eosrP4fqvkbtzzTdM7G52Tqgw\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQC63ia6lyuoSmAZiFsf55+oEUtz6nAmjmPhmUsfw0PkEc5KeLJO\nX1kkaCkoYXNoRoDB+QAIMbpOaGVaaFA9lr8S6mG9Yn0s5gHdI7ILkM1XSs3V8eUQ\n0MwcZemQJvR3Uvwv6E9043HMisLK5xVQR3CUqwCVEvZtru+AFExlf+YzqDXKFIM1\n4JhlyxEygZO1nVGr2qNFGItxFDNbERzf/Al3vKTYwugIknaU5ZatZ16QEJWp/s7l\nqA89CQO6t59yDas8ygkdR8Uyzqi3NrkQpeq+qlXXIR6h8juEnLNlFX4CLc60csvS\nVkELDn4az1YP9VpjY9ryMyIzIJPymkHJLFQV\n-----END CERTIFICATE-----"
Create a payload for importing the cross-signed certificate.
$ cat > payload-certificate-import.json << EOF
{
"certificate": $(cat cross-signed-intermediate.crt)
}
EOF
Import the cross-signed certificate into the new mount.
$ curl \
--header "X-Vault-Token: root" \
--request POST \
--data @payload-certificate-import.json \
$VAULT_ADDR/v1/pki_int/intermediate/set-signed \
| jq
Example abbreviated output:
{
"request_id": "094be7a6-71c5-be41-1442-a2048e83c9ac",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"existing_issuers": null,
"existing_keys": null,
"imported_issuers": ["c260c1d7-efde-3212-14cc-3f3778c09e1a"],
"imported_keys": null,
"mapping": {
"c260c1d7-efde-3212-14cc-3f3778c09e1a": ""
}
},
"wrap_info": null,
"warnings": [
"This mount hasn't configured any authority information access (AIA) fields; this may make it harder for systems to find missing certificates in the chain or to validate revocation status of certificates. Consider updating /config/urls or the newly generated issuer with this information."
],
"auth": null
}
Note the value of the imported_issuers
field as you'll need it in the next step.
Set the issuer name.
$ curl \
--header "X-Vault-Token: root" \
--silent \
--request POST \
--data '{"issuer_name": "xc-example-dot-com-intermediate"}' \
$VAULT_ADDR/v1/pki_int/issuer/c260c1d7-efde-3212-14cc-3f3778c09e1a
Be sure to use the correct imported_issuers
UUID for the value of issuer instead of the example value.
{
"request_id": "ac7db3a8-875a-5543-c856-0c602b5f8770",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"ca_chain": [
"-----BEGIN CERTIFICATE-----\nMIIDpzCCAo +gAwIBAgIUe0CLNyVIn6vjoMZF3gyBf8iqOKowDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMj MwOTE4MjAxMDE1WhcNMjMx\nMDIwMjAxMDQ1WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQAD ggEPADCCAQoCggEBAMoQyla1wVGlVQOg108m0WbyC6mhJUVlgj0lpi0k\nxcnLHAe1r7Y22CCPIhMHAJcW +0YCFLQHE2quVBUb7KjoqPWDTgGHDqLgnbHg5rF0\nDcjkglImYErM/yvJ53KTwe/a4Ejva/w5qTnswLqi3oLXH6OQ52 +yOoQrgAK1Uny8\n/oVm7V6d8QzMHnvEdZkil4AsPayojDdsTpxpwBWTeATku2mz9tC4m1kS3laMsJCj\n3JH8H1/ fNHPgnXJL6b44pFAGv/02qGbgGVefrcfjm42CaW6lTQERLJ+G2Q35ajut\nRvXSZcVlM6o1wQ3Qk4dhu/kud+C/ 9EVLLOxAA2XMz0Y4kuUCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/ zAdBgNVHQ4EFgQUpUGAwNEo\nWg7ELmvnURmzI1McQawwHwYDVR0jBBgwFoAUjx9eosrP4fqvkbtzzTdM7G52Tqgw\nOwYIKwY BBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1 wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBC wUAA4IBAQC63ia6lyuoSmAZiFsf55+oEUtz6nAmjmPhmUsfw0PkEc5KeLJO\nX1kkaCkoYXNoRoDB +QAIMbpOaGVaaFA9lr8S6mG9Yn0s5gHdI7ILkM1XSs3V8eUQ\n0MwcZemQJvR3Uvwv6E9043HMisLK5xVQR3CUqwCVEvZtru +AFExlf+YzqDXKFIM1\n4JhlyxEygZO1nVGr2qNFGItxFDNbERzf/Al3vKTYwugIknaU5ZatZ16QEJWp/ s7l\nqA89CQO6t59yDas8ygkdR8Uyzqi3NrkQpeq +qlXXIR6h8juEnLNlFX4CLc60csvS\nVkELDn4az1YP9VpjY9ryMyIzIJPymkHJLFQV\n-----END CERTIFICATE-----\n"
],
"certificate": "-----BEGIN CERTIFICATE-----\nMIIDpzCCAo +gAwIBAgIUe0CLNyVIn6vjoMZF3gyBf8iqOKowDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwOT E4MjAxMDE1WhcNMjMx\nMDIwMjAxMDQ1WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCC AQoCggEBAMoQyla1wVGlVQOg108m0WbyC6mhJUVlgj0lpi0k\nxcnLHAe1r7Y22CCPIhMHAJcW +0YCFLQHE2quVBUb7KjoqPWDTgGHDqLgnbHg5rF0\nDcjkglImYErM/yvJ53KTwe/a4Ejva/w5qTnswLqi3oLXH6OQ52 +yOoQrgAK1Uny8\n/oVm7V6d8QzMHnvEdZkil4AsPayojDdsTpxpwBWTeATku2mz9tC4m1kS3laMsJCj\n3JH8H1/ fNHPgnXJL6b44pFAGv/02qGbgGVefrcfjm42CaW6lTQERLJ+G2Q35ajut\nRvXSZcVlM6o1wQ3Qk4dhu/kud+C/ 9EVLLOxAA2XMz0Y4kuUCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/ zAdBgNVHQ4EFgQUpUGAwNEo\nWg7ELmvnURmzI1McQawwHwYDVR0jBBgwFoAUjx9eosrP4fqvkbtzzTdM7G52Tqgw\nOwYIKwYBBQU HAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29 tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQC63 ia6lyuoSmAZiFsf55+oEUtz6nAmjmPhmUsfw0PkEc5KeLJO\nX1kkaCkoYXNoRoDB +QAIMbpOaGVaaFA9lr8S6mG9Yn0s5gHdI7ILkM1XSs3V8eUQ\n0MwcZemQJvR3Uvwv6E9043HMisLK5xVQR3CUqwCVEvZtru +AFExlf+YzqDXKFIM1\n4JhlyxEygZO1nVGr2qNFGItxFDNbERzf/Al3vKTYwugIknaU5ZatZ16QEJWp/ s7l\nqA89CQO6t59yDas8ygkdR8Uyzqi3NrkQpeq +qlXXIR6h8juEnLNlFX4CLc60csvS\nVkELDn4az1YP9VpjY9ryMyIzIJPymkHJLFQV\n-----END CERTIFICATE-----\n",
"crl_distribution_points": [],
"issuer_id": "c260c1d7-efde-3212-14cc-3f3778c09e1a",
"issuer_name": "xc-example-dot-com-intermediate",
"issuing_certificates": [],
"key_id": "",
"leaf_not_after_behavior": "err",
"manual_chain": null,
"ocsp_servers": [],
"revocation_signature_algorithm": "",
"revoked": false,
"usage": "crl-signing,issuing-certificates,ocsp-signing,read-only"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
When reading issuers, the CA chain does not change. Try reading the intermediate CA:
$ curl --request GET \
--silent \
"$VAULT_ADDR"/v1/pki_int/issuer/example-dot-com-intermediate/json \
| jq
{
"request_id": "ffdf044f-1f1c-37f2-5252-a24380fd498f",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"ca_chain": [
"-----BEGIN CERTIFICATE-----\nMIIE3jCCA8agAwIBAgIULhwwE8Q8dURFpkui7syTrfcTRCQwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwOTE4MTYwMjI2WhcNMjgw\nOTE2MTYwMjU2WjBBMRIwEAYDVQQLEwllZHVjYXRpb24xKzApBgNVBAMTImV4YW1w\nbGUuY29tIEludGVybWVkaWF0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQC6b+eGK2qH+2w7teYmwdd3hTVstVtjree7o7Zu0dFaEgzl\n1+Ulbi8mHKW6NEvDlOwa0yOrviiWnKuJdB+ZO1yZHgNrnO9FHFY2cZi6bnxxypZu\naA8E/Fy9+2msFp906urFJ6Z5WIfTg79LIu3gwGNp79AuBy5PEg/vUzzT+mMydG7L\nykgii/fs6lqXlZZ0Qvx5D+IvkSVUn8M8SS2oLbeVaOEu4TWMXCa7W1rbO/7QdVjG\n/YIkvid3mpi8MLFYygqrIHM7eSwEYdVBKLVLongNYk26YwFX27kicpujFRAbE0VP\nfenKAVNRI8bxpj3ia9wSeRpiqK9lB2WPeJ9alHXHPEREGXPGMls54Ga6rU4uD8sO\nUZpR93YV2+5N6y4Dz69jj+zo2siv7hGcPG5LU54PIwGI3skr1lOQEzhkVMvXg7F3\noBqBQcZohQwXJcaN4eYb4VH3QPLoJeDizBlUdfFloDhGRI0yq7mBqPNSQLSYRVl7\n5GkOhXfDeTzEwohTWkgvNjR2Xo74YwUZxewwjKFktFe2hScMB7z/vx2iW1pdebdN\nkwOgsnAog2jgxYsJl+0jSiQIgDho8zkMris39tF2twMaqMZBXodERB3T5qUDrO86\nFtKiAI/ECk+HFT0hcLLobLAhdi38gZpxXfwzP0yvuFie72AmlwN05WrWE52XNQID\nAQABo4H4MIH1MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\nDgQWBBR//r79ovKjHVoz+Tc08pPmr6C9NDAfBgNVHSMEGDAWgBQ536mF93m1UK/W\ntf1zk0FARcoazzA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUHMAKGH2h0dHA6Ly8x\nMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwIgYDVR0eAQH/BBgwFqAUMBKCEHRlc3Qu\nZXhhbXBsZS5jb20wMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4\nMjAwL3YxL3BraS9jcmwwDQYJKoZIhvcNAQELBQADggEBABnfP4dRWLXWXLz7Ocv5\np24RqNYxMvlWhu0kxd7OGwNzJkxPr1vm7aEsob5K2pu0jevrDZfJb7xBT3zRguZj\n0arP0p9mNGppUOniq0sMQBQFnRhZKprQxNJx+yzEkC1FwEsiSoAVDLXfzbmt3dyX\nSbRUrCgPNMrmHWRjmnfFozMd6S1m4164uKoESgsSiNouET9490FCFfk40NScKuRD\ntZHufBSxq708UROwr/75GBakX0sxWubakTcePBUCxSqPemwleuwWqeClsNK3SkhO\nU9NAg2Pvar3K7cxoPA+9p2ceaWX8gZTKAUwlpsO0doroGMYYMLjHmr3DR1FXKmfh\nI+Q=\n-----END CERTIFICATE-----\n",
"-----BEGIN CERTIFICATE-----\nMIIDNTCCAh2gAwIBAgIUX6IHrgzCIayMMteuHmF6S9FzVVswDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwOTE4MTYwMDE4WhcNMzMw\nOTE1MTYwMDQ4WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAOafEccpjynGTDVSnf9rX/VsEEP72gRFnfhEvWjC\nkQxAgPKWT9fb76CgKMtz3NxmuNjXg5a+4Q5sFAda8YXmgjtno0Q8+HbYTYRM41Tn\napM906BlkAOcVj+19CfYrPNJuldEu1K6QA8d5qhGNKX4e5oYxLK/2MNpRnlT5FNY\nLMfdK054m33ekagcimkPXquIT49Lcn6pBUatjsmiTnOZKZzzPtuOpmDuYPydjmpE\ngQKyOnN8M72ffiMEaCYlXaET1ma2zpJybvmTP5OE7GQLRmqHfYpGfOh87VLgUgzi\n5Z9OKGPlAiRwUIv4adVB3uiKNog5ujC7FMV4KBUUqXPNYUUCAwEAAaN7MHkwDgYD\nVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFDnfqYX3ebVQ\nr9a1/XOTQUBFyhrPMB8GA1UdIwQYMBaAFDnfqYX3ebVQr9a1/XOTQUBFyhrPMBYG\nA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQDFYjWatJWj\n+OLiqETfnufMuQOjNWAIKYdagc7Z3C9qZ2uWOZxXO2MYx8MpriIcBXDUmJtGhJKv\nNhksvh4iGcKuX/XDd++XhsO7Wj6Who79N2wKmDG4B6yV/AAHWWLHRJgdMvhWXV5j\nMm7N1phxuoQT+SVrccFxIdqbEWPCOg+cPeLZAjIkUaXuxe1R8xjO6WloKjxbTarI\nfrbCzyYkIk8LQhBz8TTtCRZGdd0FA5aE7KbgY6kQoq29oCywJQZseDCB5/u4rnZn\nhvGRtvgxcTfEJRkim8hm6xJBoEm8Zd2YYHJ4kAKP2ciBoeJUpwXhAq6ORGq6Mfgm\nFWN7AydWWpq7\n-----END CERTIFICATE-----\n"
],
"certificate": "-----BEGIN CERTIFICATE-----\nMIIE3jCCA8agAwIBAgIULhwwE8Q8dURFpkui7syTrfcTRCQwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwOTE4MTYwMjI2WhcNMjgw\nOTE2MTYwMjU2WjBBMRIwEAYDVQQLEwllZHVjYXRpb24xKzApBgNVBAMTImV4YW1w\nbGUuY29tIEludGVybWVkaWF0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQC6b+eGK2qH+2w7teYmwdd3hTVstVtjree7o7Zu0dFaEgzl\n1+Ulbi8mHKW6NEvDlOwa0yOrviiWnKuJdB+ZO1yZHgNrnO9FHFY2cZi6bnxxypZu\naA8E/Fy9+2msFp906urFJ6Z5WIfTg79LIu3gwGNp79AuBy5PEg/vUzzT+mMydG7L\nykgii/fs6lqXlZZ0Qvx5D+IvkSVUn8M8SS2oLbeVaOEu4TWMXCa7W1rbO/7QdVjG\n/YIkvid3mpi8MLFYygqrIHM7eSwEYdVBKLVLongNYk26YwFX27kicpujFRAbE0VP\nfenKAVNRI8bxpj3ia9wSeRpiqK9lB2WPeJ9alHXHPEREGXPGMls54Ga6rU4uD8sO\nUZpR93YV2+5N6y4Dz69jj+zo2siv7hGcPG5LU54PIwGI3skr1lOQEzhkVMvXg7F3\noBqBQcZohQwXJcaN4eYb4VH3QPLoJeDizBlUdfFloDhGRI0yq7mBqPNSQLSYRVl7\n5GkOhXfDeTzEwohTWkgvNjR2Xo74YwUZxewwjKFktFe2hScMB7z/vx2iW1pdebdN\nkwOgsnAog2jgxYsJl+0jSiQIgDho8zkMris39tF2twMaqMZBXodERB3T5qUDrO86\nFtKiAI/ECk+HFT0hcLLobLAhdi38gZpxXfwzP0yvuFie72AmlwN05WrWE52XNQID\nAQABo4H4MIH1MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\nDgQWBBR//r79ovKjHVoz+Tc08pPmr6C9NDAfBgNVHSMEGDAWgBQ536mF93m1UK/W\ntf1zk0FARcoazzA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUHMAKGH2h0dHA6Ly8x\nMjcuMC4wLjE6ODIwMC92MS9wa2kvY2EwIgYDVR0eAQH/BBgwFqAUMBKCEHRlc3Qu\nZXhhbXBsZS5jb20wMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4\nMjAwL3YxL3BraS9jcmwwDQYJKoZIhvcNAQELBQADggEBABnfP4dRWLXWXLz7Ocv5\np24RqNYxMvlWhu0kxd7OGwNzJkxPr1vm7aEsob5K2pu0jevrDZfJb7xBT3zRguZj\n0arP0p9mNGppUOniq0sMQBQFnRhZKprQxNJx+yzEkC1FwEsiSoAVDLXfzbmt3dyX\nSbRUrCgPNMrmHWRjmnfFozMd6S1m4164uKoESgsSiNouET9490FCFfk40NScKuRD\ntZHufBSxq708UROwr/75GBakX0sxWubakTcePBUCxSqPemwleuwWqeClsNK3SkhO\nU9NAg2Pvar3K7cxoPA+9p2ceaWX8gZTKAUwlpsO0doroGMYYMLjHmr3DR1FXKmfh\nI+Q=\n-----END CERTIFICATE-----\n",
"issuer_id": "4a08c03b-acc9-5242-4c01-fd48c1616cc1",
"issuer_name": "example-dot-com-intermediate"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
Read the cross-signed intermediate CA.
$ curl --request GET \
--silent \
"$VAULT_ADDR"/v1/pki_int/issuer/xc-example-dot-com-intermediate/json \
| jq
Example output:
{
"request_id": "06aa1228-c5e0-f3fd-156e-683726ce493d",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"ca_chain": [
"-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUe0CLNyVIn6vjoMZF3gyBf8iqOKowDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwOTE4MjAxMDE1WhcNMjMx\nMDIwMjAxMDQ1WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMoQyla1wVGlVQOg108m0WbyC6mhJUVlgj0lpi0k\nxcnLHAe1r7Y22CCPIhMHAJcW+0YCFLQHE2quVBUb7KjoqPWDTgGHDqLgnbHg5rF0\nDcjkglImYErM/yvJ53KTwe/a4Ejva/w5qTnswLqi3oLXH6OQ52+yOoQrgAK1Uny8\n/oVm7V6d8QzMHnvEdZkil4AsPayojDdsTpxpwBWTeATku2mz9tC4m1kS3laMsJCj\n3JH8H1/fNHPgnXJL6b44pFAGv/02qGbgGVefrcfjm42CaW6lTQERLJ+G2Q35ajut\nRvXSZcVlM6o1wQ3Qk4dhu/kud+C/9EVLLOxAA2XMz0Y4kuUCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUpUGAwNEo\nWg7ELmvnURmzI1McQawwHwYDVR0jBBgwFoAUjx9eosrP4fqvkbtzzTdM7G52Tqgw\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQC63ia6lyuoSmAZiFsf55+oEUtz6nAmjmPhmUsfw0PkEc5KeLJO\nX1kkaCkoYXNoRoDB+QAIMbpOaGVaaFA9lr8S6mG9Yn0s5gHdI7ILkM1XSs3V8eUQ\n0MwcZemQJvR3Uvwv6E9043HMisLK5xVQR3CUqwCVEvZtru+AFExlf+YzqDXKFIM1\n4JhlyxEygZO1nVGr2qNFGItxFDNbERzf/Al3vKTYwugIknaU5ZatZ16QEJWp/s7l\nqA89CQO6t59yDas8ygkdR8Uyzqi3NrkQpeq+qlXXIR6h8juEnLNlFX4CLc60csvS\nVkELDn4az1YP9VpjY9ryMyIzIJPymkHJLFQV\n-----END CERTIFICATE-----\n"
],
"certificate": "-----BEGIN CERTIFICATE-----\nMIIDpzCCAo+gAwIBAgIUe0CLNyVIn6vjoMZF3gyBf8iqOKowDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjMwOTE4MjAxMDE1WhcNMjMx\nMDIwMjAxMDQ1WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMoQyla1wVGlVQOg108m0WbyC6mhJUVlgj0lpi0k\nxcnLHAe1r7Y22CCPIhMHAJcW+0YCFLQHE2quVBUb7KjoqPWDTgGHDqLgnbHg5rF0\nDcjkglImYErM/yvJ53KTwe/a4Ejva/w5qTnswLqi3oLXH6OQ52+yOoQrgAK1Uny8\n/oVm7V6d8QzMHnvEdZkil4AsPayojDdsTpxpwBWTeATku2mz9tC4m1kS3laMsJCj\n3JH8H1/fNHPgnXJL6b44pFAGv/02qGbgGVefrcfjm42CaW6lTQERLJ+G2Q35ajut\nRvXSZcVlM6o1wQ3Qk4dhu/kud+C/9EVLLOxAA2XMz0Y4kuUCAwEAAaOB7DCB6TAO\nBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUpUGAwNEo\nWg7ELmvnURmzI1McQawwHwYDVR0jBBgwFoAUjx9eosrP4fqvkbtzzTdM7G52Tqgw\nOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4xOjgy\nMDAvdjEvcGtpL2NhMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMDEGA1UdHwQqMCgw\nJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3\nDQEBCwUAA4IBAQC63ia6lyuoSmAZiFsf55+oEUtz6nAmjmPhmUsfw0PkEc5KeLJO\nX1kkaCkoYXNoRoDB+QAIMbpOaGVaaFA9lr8S6mG9Yn0s5gHdI7ILkM1XSs3V8eUQ\n0MwcZemQJvR3Uvwv6E9043HMisLK5xVQR3CUqwCVEvZtru+AFExlf+YzqDXKFIM1\n4JhlyxEygZO1nVGr2qNFGItxFDNbERzf/Al3vKTYwugIknaU5ZatZ16QEJWp/s7l\nqA89CQO6t59yDas8ygkdR8Uyzqi3NrkQpeq+qlXXIR6h8juEnLNlFX4CLc60csvS\nVkELDn4az1YP9VpjY9ryMyIzIJPymkHJLFQV\n-----END CERTIFICATE-----\n",
"issuer_id": "c260c1d7-efde-3212-14cc-3f3778c09e1a",
"issuer_name": "xc-example-dot-com-intermediate"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
In this section, you create a new CSR and cross sign it with the original root to create a cross signed CSR.
The
vault_pki_secret_backend_intermediate_cert_request
resource creates a new cross-signed Intermediate CSR from the newroot_2024
.resource "vault_pki_secret_backend_intermediate_cert_request" "new_csr" { backend = vault_mount.pki.path type = "existing" common_name = "example.com" key_ref = vault_pki_secret_backend_root_cert.root_2024.key_name }
The
vault_pki_secret_backend_root_sign_intermediate
resource signs the CSR with olderroot_2023
CA.resource "vault_pki_secret_backend_root_sign_intermediate" "root_2024" { backend = vault_mount.pki.path csr = vault_pki_secret_backend_intermediate_cert_request.new_csr.csr common_name = "example.com" ttl = 43800 issuer_ref = vault_pki_secret_backend_root_cert.root_2023.issuer_id }
To import the cross signed certificate, use the
vault_pki_secret_backend_intermediate_set_signed
resource block.resource "vault_pki_secret_backend_intermediate_set_signed" "root_2024" { backend = vault_mount.pki.path certificate = vault_pki_secret_backend_root_sign_intermediate.root_2024.certificate }
Take a look at the new root CA named root-2024.
$ vault read pki/issuer/root-2024
Example output
Key Value --- ----- ca_chain [-----BEGIN CERTIFICATE----- MIIDNTCCAh2gAwIBAgIUGVy5lNcQ9TbydQEUWKXXJg1nDLwwDQYJKoZIhvcNAQEL ... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUHZOy6VSoKuLzdwBKjS6Oxr/emm8wDQYJKoZIhvcNAQEL ... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDpzCCAo+gAwIBAgIUSouOAPGZ+ctkjMJsFYQ6ivEAPPUwDQYJKoZIhvcNAQEL ... -----END CERTIFICATE----- ] certificate -----BEGIN CERTIFICATE----- MIIDNTCCAh2gAwIBAgIUGVy5lNcQ9TbydQEUWKXXJg1nDLwwDQYJKoZIhvcNAQEL ... -----END CERTIFICATE----- crl_distribution_points [] issuer_id 9e3ae643-f435-8e68-4e44-a531b33f871c issuer_name root-2024 issuing_certificates [] key_id 00a7e2d4-18cc-dae6-0075-95424530474b leaf_not_after_behavior err manual_chain <nil> ocsp_servers [] revocation_signature_algorithm SHA256WithRSA revoked false usage crl-signing,issuing-certificates,ocsp-signing,read-only
Step 10: set default issuer
After your defined cutoff point for the older root CA, and after you have distributed the new root CA to all devices, you can switch the default issuer to the new root CA.
Use the root replace command to do this, and specify the issuer name of the new root CA as the value to the default
parameter.
$ vault write pki/root/replace default=root-2024
Key Value
--- -----
default f4baa074-10d7-7fc3-1623-f6f906ceed98
Use the root replace command to do this, and specify the issuer name of the new root CA as the value to the default
parameter.
$ curl \
--silent \
--request PUT \
--header "X-Vault-Token: $(vault print token)" \
--header "X-Vault-Request: true" \
--data '{"default":"root-2024"}' \
"$VAULT_ADDR"/v1/pki/root/replace \
| jq
Example output:
{
"request_id": "dc7459a4-7cf3-bd0d-d632-08d1554cf555",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"default": "8247c31f-ad1e-001c-6ad9-0aacc0aebea2"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
Check the current default issuer for the PKI secrets engine.
$ vault read pki/issuer/default ... -----END CERTIFICATE----- crl_distribution_points [] issuer_id 71f06cbe-04ce-afc4-9941-98f189e04189 issuer_name root-2024 issuing_certificates [] ...
Find the
vault_pki_secret_backend_config_issuers
block and remove the comments for the"vault_pki_secret_backend_config_issuers" "config"
block.Apply the configuration.
$ terraform apply -auto-approve Terraform will perform the following actions: # vault_pki_secret_backend_config_issuers.config will be created + resource "vault_pki_secret_backend_config_issuers" "config" { + backend = "pki" + default = "71f06cbe-04ce-afc4-9941-98f189e04189" + default_follows_latest_issuer = true + id = (known after apply) } Plan: 1 to add, 0 to change, 0 to destroy. Plan: 1 to add, 0 to change, 0 to destroy. vault_pki_secret_backend_config_issuers.config: Creating... vault_pki_secret_backend_config_issuers.config: Creation complete after 0s [id=pki/config/issuers] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Check the change in default issuer.
$ vault read pki/issuer/default ... crl_distribution_points [] issuer_id 71f06cbe-04ce-afc4-9941-98f189e04189 issuer_name root-2024 issuing_certificates [] ...
From this point forward, Vault issues new certificates based on the new root CA (root-2024).
Step 11: sunset defunct root CA
The last step is to sunset the older root CA.
You can effectively sunset the older root CA by removing its ability to issue certificates, while preserving the ability to sign CRLs and revoke certificates.
Remove issuing-certificates
from the Root CA capabilities.
$ vault write pki/issuer/root-2023 \
issuer_name="root-2023" \
usage=read-only,crl-signing | tail -n 5
Example output:
issuer_name root-2023
key_id b785654a-6ab6-0904-be34-3f5b1c1e8d53
leaf_not_after_behavior err
manual_chain <nil>
usage crl-signing,read-only
You'll notice "issuing-certificates" removed from usage
, and now just "crl-signing", and "read-only" are available on the older root CA. This means it can't issue any further certificates going forward.
You can confirm this by attempting to issue a certificate directly from the older root CA.
$ vault write pki/issuer/root-2023/issue/2023-servers \
common_name="super.secret.internal.dev" \
ttl=10m
The request fails.
Error writing data to pki/issuer/root-2023/issue/2023-servers: Error making API request.
URL: PUT http://127.0.0.1:8200/v1/pki/issuer/root-2023/issue/2023-servers
Code: 500. Errors:
* 1 error occurred:
* error fetching CA certificate: error while attempting to use issuer 09c2c9a0-a874-36d2-de85-d79a7a51e373: requested usage issuing-certificates for issuer [id:09c2c9a0-a874-36d2-de85-d79a7a51e373 / name:root-2023] but only had usage read-only,crl-signing
Remove issuing-certificates
from the Root CA capabilities.
$ curl \
--silent \
--request PUT \
--header "X-Vault-Request: true" \
--header "X-Vault-Token: $(vault print token)" \
--data '{"issuer_name":"root-2023","usage":"read-only,crl-signing"}' \
"$VAULT_ADDR"/v1/pki/issuer/root-2023 \
| jq
Example output:
{
"request_id": "3533ed1a-b59c-6c3d-7e53-1a2a35e213c2",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"ca_chain": [
"-----BEGIN CERTIFICATE-----\nMIIDNTCCAh2gAwIBAgIUa1fnttKmxiB1kV8EaWHAVqe1mrEwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTgyMDIzWhcNMzIw\nNjE3MTgyMDUyWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBALhtn4dJrslD2s00tt9yilRCmtS3/kJWHu/wjkBF\nkCUQ4eba/PhXKHbSntD+XVV+FYZsLLOpvSxJ3htR1ebaXKFbS6VWZRmEtbfmnYoh\nyZZFC0eK+82xa/DDuJsdGa6pMaeFA7RV50LuICZoHDBzlj416HUih8v3wEqdcD16\nb7Pu1Oro3qcj/xBQ9HOQ8qTT/FyWrUgWnk/N6y7BX2fpWYH91aiNjov2QzuFTOer\n8NBJTjqbfIJK5xb9lmuKJZ2P0715UJYhAtdgJaEcOd5pzQw6Wutnh/0Z93ryQozJ\n9Q67+XgRNCDsG6CpfVIc40x7nvC0s0EUCM/hwlgf1tdEBWcCAwEAAaN7MHkwDgYD\nVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGsBBdaz20yO\nL3NvzkcI5UU0eHWrMB8GA1UdIwQYMBaAFGsBBdaz20yOL3NvzkcI5UU0eHWrMBYG\nA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCztaw07tp3\nN7oRV2eiHYnvS0r3667IWqYz0T7+epnN5YuOXSNzjgB8Zbytk2mMKDCLg5mczITi\n/naybO9dczbx2SD9FhQwR3vjy+ykcI5NLjgjQ7EZZO6tyS8ZOjK9igrcp+5XG9fw\nkmp9q64em5ZKYwhR6OP2VgUrgn6MR40BFvhRA5oXH6M+UmYebWNqBB8R2z0BTf+U\nOeO9jpsLDVuhQlis91TKJXTZS6MuZwJB5e8H5/foQXKrrprmKZf2nn95qUApwn7H\nd6y6jaa9zBu8w+HZsamZM9Qi93Vg/5XoGcuTm4XE9zcM2DKPtn79TlrElpdXEH+/\nbs7CfJ5Phq5t\n-----END CERTIFICATE-----\n"
],
"certificate": "-----BEGIN CERTIFICATE-----\nMIIDNTCCAh2gAwIBAgIUa1fnttKmxiB1kV8EaWHAVqe1mrEwDQYJKoZIhvcNAQEL\nBQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjIwNjIwMTgyMDIzWhcNMzIw\nNjE3MTgyMDUyWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBALhtn4dJrslD2s00tt9yilRCmtS3/kJWHu/wjkBF\nkCUQ4eba/PhXKHbSntD+XVV+FYZsLLOpvSxJ3htR1ebaXKFbS6VWZRmEtbfmnYoh\nyZZFC0eK+82xa/DDuJsdGa6pMaeFA7RV50LuICZoHDBzlj416HUih8v3wEqdcD16\nb7Pu1Oro3qcj/xBQ9HOQ8qTT/FyWrUgWnk/N6y7BX2fpWYH91aiNjov2QzuFTOer\n8NBJTjqbfIJK5xb9lmuKJZ2P0715UJYhAtdgJaEcOd5pzQw6Wutnh/0Z93ryQozJ\n9Q67+XgRNCDsG6CpfVIc40x7nvC0s0EUCM/hwlgf1tdEBWcCAwEAAaN7MHkwDgYD\nVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGsBBdaz20yO\nL3NvzkcI5UU0eHWrMB8GA1UdIwQYMBaAFGsBBdaz20yOL3NvzkcI5UU0eHWrMBYG\nA1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCztaw07tp3\nN7oRV2eiHYnvS0r3667IWqYz0T7+epnN5YuOXSNzjgB8Zbytk2mMKDCLg5mczITi\n/naybO9dczbx2SD9FhQwR3vjy+ykcI5NLjgjQ7EZZO6tyS8ZOjK9igrcp+5XG9fw\nkmp9q64em5ZKYwhR6OP2VgUrgn6MR40BFvhRA5oXH6M+UmYebWNqBB8R2z0BTf+U\nOeO9jpsLDVuhQlis91TKJXTZS6MuZwJB5e8H5/foQXKrrprmKZf2nn95qUApwn7H\nd6y6jaa9zBu8w+HZsamZM9Qi93Vg/5XoGcuTm4XE9zcM2DKPtn79TlrElpdXEH+/\nbs7CfJ5Phq5t\n-----END CERTIFICATE-----\n",
"issuer_id": "09c2c9a0-a874-36d2-de85-d79a7a51e373",
"issuer_name": "root-2023",
"key_id": "4616f6dc-17a4-a137-79b3-82f6b04f38d7",
"leaf_not_after_behavior": "err",
"manual_chain": null,
"usage": "crl-signing,read-only"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
You'll notice "issuing-certificates" removed from usage
, and now just "crl-signing", and "read-only" are available on the older root CA. This means it can't issue any further certificates going forward.
You can confirm this by attempting to issue a certificate directly from the older root CA.
$ curl \
--silent \
--request PUT \
--header "X-Vault-Request: true" \
--header "X-Vault-Token: $(vault print token)" \
--data '{"common_name":"super.secret.internal.dev","ttl":"10m"}' \
"$VAULT_ADDR"/v1/pki/issuer/root-2023/issue/2023-servers \
| jq
The request fails.
{
"errors": [
"1 error occurred:\n\t* error fetching CA certificate: error while attempting to use issuer 09c2c9a0-a874-36d2-de85-d79a7a51e373: requested usage issuing-certificates for issuer [id:09c2c9a0-a874-36d2-de85-d79a7a51e373 / name:root-2023] but only had usage crl-signing,read-only\n\n"
]
}
CRLs and OCSP
Both Certificate revocation lists (CRL) and online certificate status protocol (OCSP) allow interrogating revocation status of certificates.
These methods include internal security and authenticity (both CRLs and OCSP responses get signed by the issuing CA within Vault). This means both are fine to distribute over non-secure and non-authenticated channels, such as HTTP.
Vault version 1.12.0 and greater supports automated CRL rebuilding (including optional Delta CRLs which it can rebuild more often than complete CRLs) via the /config/crl API endpoint.
You can also configure automatic tidying of revoked and expired certificates with the /config/auto-tidy endpoint.
Tip
It is suggested that these features be enabled when possible to ensure compatibility with the wider PKIX ecosystem, and to enhance performance of the cluster.
Cleanup
You can unset the two environment variables, VAULT_ADDR
and VAULT_TOKEN
, that you set in the beginning of this tutorial and remove the files you created to clean up.
Unset the environment variables.
$ unset VAULT_ADDR VAULT_TOKEN
Remove the files.
$ rm -f \
root_2023_ca.crt \
intermediate.cert.pem \
pki_intermediate.csr \
payload.json \
payload-url.json \
payload-int.json \
payload-int-cert.json \
payload-signed.json \
payload-role.json
If you also followed the advanced steps, you'll need to remove some additional files.
$ rm -f \
root_2023_ca.crt \
cross-signed-intermediate.csr \
cross-signed-intermediate.crt
If you were doing the terraform version of this tutorial, go back to where the learn-vault-pki-engine
repository was cloned.
$ cd ../..
Delete the learn-vault-pki-engine
directory.
$ rm -rf learn-vault-pki-engine
Delete Vault Dedicated clusters created for this tutorial.
Next steps
Check out the Streamline Certificate Management with HashiCorp Vault webinar recording.
Also, refer to the Vault PKI Secrets Engine Integration tutorial for an example of Nomad using the PKI secrets engine to generate and renew the X.509 certificates it uses. To automate the process, this tutorial leverages the Consul Template tool.
Help and reference
This tutorial also appears in:
- Â6 tutorialsManage certificates with VaultCreate, renew, and manage certificates with Vault.
- Vault