Cryptographic Protection

Cryptographic Protection

By default, step-ca stores its signing keys encrypted on disk.

For security hardening, you may desire more advanced cryptographic protection (or hardware protection) of your CA's signing keys.

For these scenarios, step-ca integrates with the following key management systems:

For a complete, end-to-end example using a YubiKey, see our blog post Build a Tiny Certificate Authority For Your Homelab.

Before You Begin

The step ca init command has very limited support for keys not stored on disk. We've created the step kms plugin for managing the keys and certificates on cloud KMSs and on hardware devices. Please install this plugin before continuing with any of the examples below.

Google Cloud KMS

Cloud KMS is Google's cloud-hosted KMS that allows you to store the cryptographic keys and sign certificates using their infrastructure. Cloud KMS supports two key protection levels: SOFTWARE and HSM.

Creating your PKI in Google Cloud KMS

Also, make sure you have installed the gcloud CLI and have configured Google Cloud application default credentials in your local environment, eg. by running gcloud auth application-default login.

Next, let's generate a private key for your Root CA. The step kms create command will also create a Cloud KMS key ring for you, if it doesn't exist. Run:

$ step kms create --json --kms 'cloudkms:' \
    'projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/root'

To constructe the key name parameter above, use the resource name of the CryptoKey object you want to create, following the format:

projects/<project-id>/locations/<location>/keyRings/<key-ring-id>/cryptoKeys/<key-id>

Once the key is generated, step outputs the key's name (including a version number), and the public key PEM:

{
  "name": "projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/root/cryptoKeyVersions/1",
  "publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERcTjeMNjBs29ReA1nf6Odyn2l4Yt\nXPo4CUOcCrn6yw7LJmzaDiqIErhuS9r6BNg92kJvFUiuiU8+w+WZOyhZdw==\n-----END PUBLIC KEY-----\n"
}

You'll need this key name for the next step.

Now, let's sign a root CA certificate based on the the key you just created. Substitute the key name output from step kms create here:

$ step certificate create --profile root-ca \
   --kms 'cloudkms:' \
   --key 'projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/root/cryptoKeyVersions/1' \
   "Smallstep Root CA" root_ca.crt

Output:

Your certificate has been saved in root_ca.crt.

Great. Next, repeat the process for the Intermediate CA:

$ step kms create --json --kms 'cloudkms:' \
    'projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/intermediate'
$ step certificate create --profile intermediate-ca \
   --kms 'cloudkms:' \
   --ca-kms 'cloudkms:' \
   --ca root_ca.crt \
   --ca-key 'projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/root/cryptoKeyVersions/1' \
   --key 'projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/intermediate/cryptoKeyVersions/1' \
   "Smallstep Intermediate CA" intermediate_ca.crt

Output:

Your certificate has been saved in intermediate_ca.crt.

Now you should have both root_ca.crt and intermediate_ca.crt certificate PEM files. Use these files in your CA configuration, below.

If you want to run an SSH CA, you also need to create SSH CA key pairs:

$ step kms create --json --kms 'cloudkms:' \
    'projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/ssh-user'
$ step kms create --json --kms 'cloudkms:' \
    'projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/ssh-host'

Configuring step-ca to use Google Cloud KMS

Next, to configure Cloud KMS in step-ca, start with a basic CA configuration created using step ca init. Add the kms object to your ca.json file and replace the property key with the Cloud KMS resource name of your intermediate key:

{
    "root": "/etc/step-ca/certs/root_ca.crt",
    "crt": "/etc/step-ca/certs/intermediate_ca.crt",
    "key": "projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/intermediate/cryptoKeyVersions/1",
    "kms": {
        "type": "cloudkms",
        "uri": "cloudkms:credentials-file=/path/to/gcloud-kms-credentials.json"
    }
}

Finally, copy the root_ca.crt and intermediate_ca.crt files into the root and crt locations:

$ cp root_ca.crt intermediate_ca.crt $(step path)/certs

Your X.509 CA is ready.

To add SSH support, change the SSH key locations to Cloud KMS resource names for the SSH host and user CA keys:

{
    "ssh": {
        "hostKey": "projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/ssh-host/cryptoKeyVersions/1",
        "userKey": "projects/smallstep/locations/global/keyRings/step-ca/cryptoKeys/ssh-user/cryptoKeyVersions/1"
    }
}

When you start step-ca, it prints your X.509 root fingerprint, and the SSH user and host CA keys in SSH key format:

2022/09/20 16:28:45 The primary server URL is https://localhost:443
2022/09/20 16:28:45 Root certificates are available at https://localhost:443/roots.pem
2022/09/20 16:28:45 X.509 Root Fingerprint: b061dfca1013c074244b0f376e5be70b6eb0bd7f21d5438aa3af71fe62b0acf5
2022/09/20 16:28:45 SSH Host CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIwmL7aDOJId/9UOVJGhVux6Rlvea+q2017aLsfze+/EwGQ5BdZ4k2Qh+5VekebBKZYLNO0LkSf9bZb4o9GSxIs=
2022/09/20 16:28:45 SSH User CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFE0VY9eLxkoHrXoWk5VxeOQTUt53U5xIo89pfsgYHh450cdE4c3mYw5YeOueESyu/lFUHfJoNS6twVR1wuCOdc=
2022/09/20 16:28:45 Serving HTTPS on :443 ...

Notes on Google Cloud KMS Permissions

Following the principle of least privilege, you'll want to define an IAM operational role for step-ca that limits its access. See Google Cloud KMS: Access Control with IAM for an overview.

Here are some notes on granting a custom IAM role that provides step-ca with access to Google Cloud KMS:

  • You can constrain the role to the key ring that you created for step-ca.
  • For day-to-day operation, grant step-ca the following default roles:
  • More specifically, a running step-ca needs access to the following KMS APIs:
    • resourcemanager.projects.get
    • cloudkms.locations.get
    • cloudkms.keyRings.get
    • cloudkms.cryptoKeys.get
    • cloudkms.cryptoKeyVersions.get
    • cloudkms.cryptoKeyVersions.useToSign
    • cloudkms.cryptoKeyVersions.useToVerify
    • cloudkms.cryptoKeyVersions.useToDecrypt

AWS KMS

AWS KMS is Amazon's managed encryption and key management service. It creates and stores the cryptographic keys and uses AWS infrastructure for signing operations. Amazon KMS operations are always backed by HSMs.

Creating your PKI in AWS KMS

Next, let's generate a private key for your root CA inside AWS KMS. Run:

$ step kms create --json --kms 'awskms:region=us-east-2' root-ca

Once the key is generated, step outputs the key ID and the public key PEM:

{
  "name": "awskms:key-id=78980acd-a42d-4d84-97ba-1e50d3082214",
  "publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEH2ls6h1y2jsXV+IeuhDVkb68zkMe\nKPtI7L6vBIa5ThxOyFaZFnUrGWU6B+KQjProAntgKyNTqOnAh7Eyr3RmgQ==\n-----END PUBLIC KEY-----\n"
}

When creating the key, step kms create will also add an AWS key alias root-ca-78980acd for the key. The alias is concatenates the key name and the first segment of the key's ID.

You'll need this key name for the next step.

Now, let's sign a root CA certificate based on the the key you just created. Substitute the key name output from step kms create here:

$ step certificate create --profile root-ca \
   --kms 'awskms:region=us-east-2' \
   --key 'awskms:key-id=78980acd-a42d-4d84-97ba-1e50d3082214' \
   "Smallstep Root CA" root_ca.crt

Output:

Your certificate has been saved in root_ca.crt.

Great. Next, we'll repeat the process for the Intermediate CA:

$ step kms create --json --kms 'awskms:region=us-east-2' intermediate-ca
$ step certificate create --profile intermediate-ca \
   --kms 'awskms:region=us-east-2' \
   --ca-kms 'awskms:region=us-east-2' \
   --ca root_ca.crt \
   --ca-key 'awskms:key-id=78980acd-a42d-4d84-97ba-1e50d3082214' \
   --key 'awskms:key-id=9432458d-1e67-4a74-9a23-8f94708b45fe' \
   "Smallstep Intermediate CA" intermediate_ca.crt

Here, the --ca-key is the root CA key id; the --key is the intermediate CA key id.

Output:

Your certificate has been saved in intermediate_ca.crt.

Now you should have both root_ca.crt and intermediate_ca.crt certificate PEM files. You'll need these files for your CA configuration, below.

If you want to run an SSH CA, you'll also need to create SSH CA key pairs:

$ step kms create --json --kms 'awskms:region=us-east-2' ssh-user-ca
$ step kms create --json --kms 'awskms:region=us-east-2' ssh-host-ca

Hold onto the key IDs from these commands; you'll need them below.

Configuring step-ca to use AWS KMS

To use step-ca with AWS KMS, create a scoped IAM role that has kms:GetPublicKey permissions on all of your CA keys, and kms:Sign permission on your intermediate CA key.

To configure AWS KMS in your certificate authority, add the kms object to ca.json and replace the key property with the AWS KMS key ID of your intermediate CA key:

{
    "root": "/etc/step-ca/certs/root_ca.crt",
    "crt": "/etc/step-ca/certs/intermediate_ca.crt",
    "key": "awskms:key-id=f879f239-feb6-4596-9ed2-b1606277c7fe",
    "kms": {
        "type": "awskms",
        "uri": "awskms:region=us-east-2;profile=foo;credentials-file=/path/to/credentials"
    }
}

By default, step-ca (and, more broadly, AWS's SDK) looks for credentials stored in ~/.aws/credentials. Use the credentials-file option to override. The region and profile options specify the AWS region and configuration profiles respectively. These options can also be configured using environment variables as described in the AWS SDK for Go session documentation.

Finally, copy the root_ca.crt and intermediate_ca.crt files into the root and crt locations:

$ cp root_ca.crt intermediate_ca.crt $(step path)/certs

Your X.509 CA is ready.

To configure an SSH CA, replace the SSH key locations with the SSH CA keys you created above:

{
    "ssh": {
        "hostKey": "awskms:key-id=d48e502a-09bc-4bf7-9af8-ae1bccedc931",
        "userKey": "awskms:key-id=cf28e942-1e10-4a08-b84c-5359af1b5f12"
    }
}

When you start step-ca, it prints your X.509 root fingerprint, and the SSH user and host CA keys in SSH key format:

2022/09/20 16:28:45 The primary server URL is https://localhost:443
2022/09/20 16:28:45 Root certificates are available at https://localhost:443/roots.pem
2022/09/20 16:28:45 X.509 Root Fingerprint: b061dfca1013c074244b0f376e5be70b6eb0bd7f21d5438aa3af71fe62b0acf5
2022/09/20 16:28:45 SSH Host CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIwmL7aDOJId/9UOVJGhVux6Rlvea+q2017aLsfze+/EwGQ5BdZ4k2Qh+5VekebBKZYLNO0LkSf9bZb4o9GSxIs=
2022/09/20 16:28:45 SSH User CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFE0VY9eLxkoHrXoWk5VxeOQTUt53U5xIo89pfsgYHh450cdE4c3mYw5YeOueESyu/lFUHfJoNS6twVR1wuCOdc=
2022/09/20 16:28:45 Serving HTTPS on :443 ...

Notes on AWS KMS Permissions

Following the principle of least privilege, you'll want to define an IAM operational role for step-ca that limits its access. See Using IAM Policies With AWS KMS for an overview.

Here are some notes on granting a custom IAM role that provides step-ca with access to AWS KMS:

  • You can constrain the role to specific keys that you created for step-ca, by listing each key's ARN in the Resource block of the JSON policy.
  • For day-to-day operation, grant step-ca the following permissions:
    • kms:GetPublicKey
    • kms:Sign

Azure Key Vault

Azure Key Vault is Microsoft's managed key management service.

Authentication

When using Azure Key Vault with step-ca, first authenticate to Azure.

Authentication to Azure is handled via environment variables; we recommend using either file-based authentication via the AZURE_AUTH_LOCATION environment variable, or using a managed identity and setting the AZURE_TENANT_ID and AZURE_CLIENT_ID variables when starting step-ca. Alternatively, you can create a service principal and set the AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET variables. See Option 1 under Authentication Methods for Azure SDK for Go for examples of authentication methods and environment variables.

For local development and testing, Azure CLI credentials are used if no authentication environment variables are set.

Initialize a PKI

To initialize a PKI backed by Azure Key Vault, start by authenticating to Azure using one of the above approaches. Set the environment variables necessary for authentication to your tenant.

Then, run:

$ step ca init --kms azurekms

This walks you through the process of creating root and intermediate CA signing keys in Key Vault, and configuring the CA to use them. If you're creating an SSH CA, SSH host and user CA keys are created in Key Vault as well.

Manual Configuration

To configure an existing CA for Azure Key Vault, or to import an existing Azure Key Vault signing key, add the kms object to your ca.json, and replace the key properties with the key name, vault name, and version of your intermediate (signing) key in Azure Key Vault:

{
    "root": "/etc/step-ca/certs/root_ca.crt",
    "crt": "/etc/step-ca/certs/intermediate_ca.crt",
    "key": "azurekms:name=intermediate-ca-key;vault=example-vault-0?version=15faf8b8b80d4f1ead067c6383a38b8f&hsm=true",
    "kms": {
        "type": "azurekms"
    }
}
  • In the key URI, the name and vault refer to the key name and vault name of your intermediate key in Azure Key Vault.
  • In the key URI, the version is the version of the Azure Key Vault key name. Though it is optional, we recommend setting this value explicitly. If omitted, the latest version is used.
  • In the key URI, the optional hsm property can be set to true if HSM protection is desired. This is only used when the key is being created by step-ca. The default is to use software-protected (non-HSM-backed) keys. See Key Vault's About Keys page for more details.
  • In kms, an optional uri property can be added to provide client credentials (eg. azurekms:client-id=fooo;client-secret=bar;tenant-id=9de53416-4431-4181-7a8b-23af3EXAMPLE) instead of using the environment variables described above.

Notes on Azure Key Vault IAM Permissions

Following the principle of least privilege, you'll want to define an IAM operational role for step-ca that limits its access. See Provide access to Key Vault keys, certificates, and secrets with an Azure role-based access control for an overview.

Here are some notes on granting a custom IAM role that provides step-ca with access to Azure Key Vault:

  • You can constrain the role to the vault that you created for step-ca.
  • For day-to-day operation, grant step-ca the following default RBAC roles on the vault:
    • Key Vault Crypto User
    • Key Vault Reader
  • More specifically, a running step-ca needs access to the following APIs:
    • GetKey
    • Sign

PKCS #11

A Hardware Security Module (HSM) is a specialized piece of hardware that is designed to generate and store private keys, and sign messages using those keys. The private keys on an HSM cannot be exported from the device. One can only run signing operations using the key. This is an excellent way to protect private keys for a Certificate Authority, which in normal operation simply needs to be able to sign Certificate Signing Requests.

Public-Key Cryptography Standards #11 (PKCS #11) is the most common platform-independent API used to access HSM hardware. It's supported by most HSM hardware, like Yubico's YubiHSM2, and the Nitrokey HSM 2. There's also a software-based "HSM," SoftHSMv2, which offers a PKCS #11 interface without the hardware.

0. Before you begin

Your HSM may need to be prepared before you can initialize a PKI on it. Preparation steps depend on the device and are beyond the scope of this documentation. For example, with YubiHSM2, first create an authentication key with appropriate capabiliites.

Plan how you will backup and restore your CA keys, for offline storage.

The step kms plugin will create CA keys and sign certificates on your device. To use it, you'll need to provide a PKCS #11 URI for accessing the device, using the --kms flag.

Here are some examples of PKCS #11 URIs for accessing various devices in Linux:

HSMURI format
YubiHSM2pkcs11:module-path=/usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so;token=YubiHSM
AWS CloudHSMpkcs11:module-path=/opt/cloudhsm/lib/libcloudhsm_pkcs11.so;token=cavium?pin-value=$HSM_USER:$HSM_PASSWORD
SoftHSMpkcs11:module-path=/usr/lib/softhsm/libsofthsm2.so;token=token1?pin-value=$HSM_PASSWORD
nCipher nShieldpkcs11:module-path=/opt/nfast/toolkits/pkcs11/libcknfast.so;token=rjk?pin-source=/etc/step-ca/hsm-pin.txt

Substitute $HSM_USER and $HSM_PASSWORD with your own values.

In this URI,

  • module-path points to your PKCS #11 .dll, .so, or .dylib library file,
  • token is the label (CKA_LABEL) of the HSM you're using,
  • pin-value contains hardcoded HSM credentials. It may be a PIN, username and password, password, or a filename. The YubiHSM2 is special in that the PIN value is the concatenation of the four-digit authorization key ID (eg. 0001) and the PIN.
  • Or, pin-source is a filename containing HSM credentials.

1. Create your PKI

Once you've constructed the right URI for accessing your device, use it in place of $PKCS_URI in the commands below.

Next, let's ask the device to generate a private key for your root CA. Run:

$ step kms create --json --kms "$PKCS_URI" "pkcs11:id=7331;object=root-ca"

Once the key is generated, step will output the key ID and the public key PEM:

{
  "name": "pkcs11:id=7331;object=root-ca",
  "publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEH2ls6h1y2jsXV+IeuhDVkb68zkMe\nKPtI7L6vBIa5ThxOyFaZFnUrGWU6B+KQjProAntgKyNTqOnAh7Eyr3RmgQ==\n-----END PUBLIC KEY-----\n"
}

You'll need this key name for the next step.

Now, let's sign a root CA certificate based on the the key you just created. Substitute the key name output from step kms create here:

$ step certificate create --profile root-ca \
   --kms "$PKCS_URI"
   --key "pkcs11:id=7331;object=root-ca" \
   "Smallstep Root CA" root_ca.crt

Output:

Your certificate has been saved in root_ca.crt.

Great. Next, we'll repeat the process for the Intermediate CA:

$ step kms create --json --kms "$PKCS_URI" "pkcs11:id=7332;object=intermediate-ca"
$ step certificate create --profile intermediate-ca \
   --kms "$PKCS_URI" \
   --ca-kms "$PKCS_URI" \
   --ca root_ca.crt \
   --ca-key "pkcs11:id=7331;object=root-ca" \
   --key "pkcs11:id=7332;object=intermediate-ca" \
   "Smallstep Intermediate CA" intermediate_ca.crt

Here, the --ca-key is the root CA key id; the --key is the intermediate CA key id.

Output:

Your certificate has been saved in intermediate_ca.crt.

Now you should have both root_ca.crt and intermediate_ca.crt certificate PEM files. You'll need these files for your CA configuration, below.

If you want to run an SSH CA, you'll also need to create SSH CA key pairs:

$ step kms create --json --kms "$PKCS_URI" "pkcs11:id=7333;object=ssh-host-ca"
$ step kms create --json --kms "$PKCS_URI" "pkcs11:id=7334;object=ssh-user-ca"

Hold onto the key IDs from these commands; you'll need them below.

Configuring step-ca to use PKCS #11

One you've created your PKI on the HSM using step-kms-plugin, you'll need to configure step-ca to use the HSM.

To configure your certificate authority, add the kms object to ca.json and replace the key property with the object ID of your intermediate CA key:

{
    "root": "/etc/step-ca/certs/root_ca.crt",
    "crt": "/etc/step-ca/certs/intermediate_ca.crt",
    "key": "pkcs11:id=7332;object=intermediate-ca",
    "kms": {
        "type": "pkcs11",
        "uri": "$PKCS_URI"
    }
}

Finally, copy the root_ca.crt and intermediate_ca.crt files into the root and crt locations:

$ cp root_ca.crt intermediate_ca.crt $(step path)/certs

Your X.509 CA is ready.

To configure an SSH CA, replace the SSH key locations with the SSH CA keys you created above:

{
    "ssh": {
        "hostKey": "pkcs11:id=7333;object=ssh-host-ca",
        "userKey": "pkcs11:id=7334;object=ssh-user-ca"
    }
}

When you start step-ca, you will see your X.509 root fingerprint, and the SSH user and host CA keys in SSH key format:

2022/09/20 16:28:45 The primary server URL is https://localhost:443
2022/09/20 16:28:45 Root certificates are available at https://localhost:443/roots.pem
2022/09/20 16:28:45 X.509 Root Fingerprint: b061dfca1013c074244b0f376e5be70b6eb0bd7f21d5438aa3af71fe62b0acf5
2022/09/20 16:28:45 SSH Host CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIwmL7aDOJId/9UOVJGhVux6Rlvea+q2017aLsfze+/EwGQ5BdZ4k2Qh+5VekebBKZYLNO0LkSf9bZb4o9GSxIs=
2022/09/20 16:28:45 SSH User CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFE0VY9eLxkoHrXoWk5VxeOQTUt53U5xIo89pfsgYHh450cdE4c3mYw5YeOueESyu/lFUHfJoNS6twVR1wuCOdc=
2022/09/20 16:28:45 Serving HTTPS on :443 ...

TPM 2.0

You can leverage a TPM 2.0 chip to store your CA key and sign TLS certificates.

Prerequisites and Caveats

First, let's generate your Root CA on disk. After this tutorial, you will only need the root private key if you issue another Intermediate CA.

Run:

$ step certificate create --profile root-ca \
    "Smallstep Root CA" \
    root_ca.crt \
    root_ca.key

Output:

Your certificate has been saved in root_ca.crt.
Your private key has been saved in root_ca.key.

Great. For a production PKI, it is recommended that you create this key on an airgapped device with secure backups.

Next, we'll create an Intermediate CA key on the TPM, and sign an Intermediate CA certificate:

$ step kms create --json 'tpmkms:name=my-intermediate-ca'
$ step certificate create --profile intermediate-ca \
   --kms 'tpmkms:' \
   --ca root_ca.crt \
   --ca-key root_ca.key \
   --key 'tpmkms:name=my-intermediate-ca' \
   "Smallstep Intermediate CA" intermediate_ca.crt

Here, the --ca-key is your root CA; the --key is the intermediate CA key id.

Output:

Your certificate has been saved in intermediate_ca.crt.

Now you should have both root_ca.crt and intermediate_ca.crt certificate PEM files. You no longer need your root_ca.key, so you can now store it in a safe place.

Finally, to enable your CA in ca.json, point the root and crt options to the generated certificates, replace the key option with the TPM KMS URI:

{
    "root": "/etc/step-ca/certs/root_ca.crt",
    "crt": "/etc/step-ca/certs/intermediate_ca.crt",
    "key": "tpmkms:name=my-intermediate-ca",
    "kms": {
        "type": "tpmkms",
        "uri": "tpmkms:"
    }
}

Finally, copy the root_ca.crt and intermediate_ca.crt files into the root and crt locations:

$ cp root_ca.crt intermediate_ca.crt $(step path)/certs

Your X.509 CA is ready. When you start step-ca, it prints your X.509 root fingerprint:

2022/09/20 16:28:45 The primary server URL is https://localhost:443
2022/09/20 16:28:45 Root certificates are available at https://localhost:443/roots.pem
2022/09/20 16:28:45 X.509 Root Fingerprint: b061dfca1013c074244b0f376e5be70b6eb0bd7f21d5438aa3af71fe62b0acf5
2022/09/20 16:28:45 Serving HTTPS on :443 ...

YubiKey PIV

You can leverage a hardware YubiKey—and the YubiKey PIV application—to store your CA keys and sign TLS and SSH certificates.

Prerequisites and Caveats

  • To enable YubiKey support in step-ca, you must follow our Instructions for building from source using CGO
  • You will need a YubiKey 5 series device that supports the PIV application
  • Certificate slots 9a, 9c, 9d, 9e, and 82-95 are supported
  • You can use the YubiKey for X.509 and SSH CAs

Now, insert your YubiKey. Let's generate a private key for your root CA in slot 82 on the YubiKey. Run:

$ step kms create --json 'yubikey:slot-id=82'

Once the key is generated, step outputs the key name and public key PEM:

{
  "name": "yubikey:slot-id=82",
  "publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAED3T/7q+p+6239Ri35TBVoChM6VNq\n1buLfql1acRl7F2qf/L96x9XHY5GHoqYNCAm/ocL9hTl8ytWJao+JSNE+Q==\n-----END PUBLIC KEY-----\n"
}

Now, let's sign a root CA certificate based on the the key you just created. Substitute the key name output from step kms create here:

$ step certificate create --profile root-ca \
   --kms 'yubikey:pin-value=123456' \
   --key 'yubikey:slot-id=82' \
   "Smallstep Root CA" root_ca.crt

Here we're using the default PIN code of 123456 to access the YubiKey.

Output:

Your certificate has been saved in root_ca.crt.

Great. Next, we'll repeat the process for the Intermediate CA:

$ step kms create --json 'yubikey:slot-id=83'
$ step certificate create --profile intermediate-ca \
   --kms 'yubikey:pin-value=123456' \
   --ca-kms 'yubikey:pin-value=123456' \
   --ca root_ca.crt \
   --ca-key 'yubikey:slot-id=82' \
   --key 'yubikey:slot-id=83' \
   "Smallstep Intermediate CA" intermediate_ca.crt

Here, the --ca-key is the root CA key id; the --key is the intermediate CA key id.

Output:

Your certificate has been saved in intermediate_ca.crt.

Now you should have both root_ca.crt and intermediate_ca.crt certificate PEM files. You'll need these files for your CA configuration, below.

$ ykman piv certificates import 82 root_ca.crt
$ ykman piv certificates import 83 intermediate_ca.crt

(While step-ca won't use these copies of the certificates, you can always use ykman piv certificates export to download the certificates later.)

Next, if you want to run an SSH CA, you'll also need to create two SSH CA keys:

$ step kms create --json 'yubikey:slot-id=84'
$ step kms create --json 'yubikey:slot-id=85'

Finally, to enable your CA in ca.json, point the root and crt options to the generated certificates, replace the key option with the YubiKey URI generated in the previous part, and configure the kms option with the appropriate type and pin.

{
    "root": "/etc/step-ca/certs/root_ca.crt",
    "crt": "/etc/step-ca/certs/intermediate_ca.crt",
    "key": "yubikey:slot-id=83",
    "kms": {
        "type": "yubikey",
        "uri": "yubikey:management-key=01020304...?pin-value=123456"
    }
}

Finally, copy the root_ca.crt and intermediate_ca.crt files into the root and crt locations:

$ cp root_ca.crt intermediate_ca.crt $(step path)/certs

Your X.509 CA is ready.

To configure an SSH CA, replace the SSH key locations with the SSH CA keys you created in AWS KMS:

{
    "ssh": {
        "hostKey": "yubikey:slot-id=84",
        "userKey": "yubikey:slot-id=85"
    }
}

When you start step-ca, it prints your X.509 root fingerprint, and the SSH host and user CA keys in SSH key format:

2022/09/20 16:28:45 The primary server URL is https://localhost:443
2022/09/20 16:28:45 Root certificates are available at https://localhost:443/roots.pem
2022/09/20 16:28:45 X.509 Root Fingerprint: b061dfca1013c074244b0f376e5be70b6eb0bd7f21d5438aa3af71fe62b0acf5
2022/09/20 16:28:45 SSH Host CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIwmL7aDOJId/9UOVJGhVux6Rlvea+q2017aLsfze+/EwGQ5BdZ4k2Qh+5VekebBKZYLNO0LkSf9bZb4o9GSxIs=
2022/09/20 16:28:45 SSH User CA Key: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFE0VY9eLxkoHrXoWk5VxeOQTUt53U5xIo89pfsgYHh450cdE4c3mYw5YeOueESyu/lFUHfJoNS6twVR1wuCOdc=
2022/09/20 16:28:45 Serving HTTPS on :443 ...

Last updated on March 26, 2024