Basic Certificate Authority Operations

Before You Begin

This document assumes you have initialized and started up a step-ca instance using the steps in Getting Started.

Working With X.509 Certificates

Let's perform some basic operations on X.509 certificates:

Issue a Certificate From Scratch

Once you have a certificate authority up and running, the step ca certificate command makes generating a private key and obtaining a certificate simple. You need just one command:

$ step ca certificate svc.crt svc.key ✔ Provisioner: (JWK) [kid: JxCvTLC67zKCOi-yyMoHpO5vAj_MnXs80PR0nh7IjUg] ✔ Please enter the password to decrypt the provisioner key: ✔ CA: ✔ Certificate: svc.crt ✔ Private Key: svc.key

The positional arguments in the step ca certificate command indicate the name you would like associated with the certificate (in this case, the DNS host name of a service) and the output filenames for the certificate and private key.

You can check your work using step certificate inspect:

$ step certificate inspect svc.crt --short X.509v3 TLS Certificate (ECDSA P-256) [Serial: 7720...1576] Subject: Issuer: Smallstep Intermediate CA Provisioner: [ID: JxCv...IjUg] Valid from: 2020-09-22T00:59:37Z to: 2020-09-23T01:00:37Z

Let's make a CSR for this example, using step certificate create --csr, then we'll use step ca sign to get a signed certificate from the CA.

$ step certificate create --csr foo.csr foo.key Please enter the password to encrypt the private key: $ step ca sign foo.csr foo.crt ✔ Provisioner: (JWK) [kid: yWa7WGfoSt9yJ0OZCndrvR_m65jzDriY7mhPz094fdw] ✔ Please enter the password to decrypt the provisioner key: ... ✔ CA: ✔ Certificate: foo.crt

Issue a certificate using a single-use CA token

Under the hood, several provisioners use a single-use, signed, short-lived token to authenticate certificate requests. Clients wrap a certificate request inside a signed JavaScript Web Token (JWT), and send them to the step-ca server.

Sometimes it's useful to separate the creation of the CA authentication token from the creation of the private key and the signing of the certificate. For example, you may want to inject a CA token into a Docker container, then have the container use the token to request its own certificate. This way, the container doesn't have to hold any long-lived CA credentials.

How the signed token is generated depends on the provisioner type. For example:

  • The JWK provisioner expects tokens signed using an encrypted JavaScript Web Key stored in the CA provisioner configuration. In the example below, we'll use step ca token to ask the CA to sign a token. But, if you have the private key for the JWK configured in the CA, you can generate CA tokens offline, without contacting the CA.
  • The X5C provisioner expects tokens signed using an X509 certificate private key whose root is configured in the CA.
  • The OIDC provisioner expects an identity token returned from the OAuth flow, which is signed with the OAuth provider's private key.

Let's use a JWK provisioner to separate the token request from the signing request:

$ TOKEN=$(step ca token localhost) ✔ Provisioner: (JWK) [kid: kvPj79hrAnrQywrVy-oFmye4foHq1rdUg55nxsuCvkI] ✔ Please enter the password to decrypt the provisioner key: $ echo $TOKEN | step crypto jwt inspect --insecure { "header": { "alg": "ES256", "kid": "kvPj79hrAnrQywrVy-oFmye4foHq1rdUg55nxsuCvkI", "typ": "JWT" }, "payload": { "aud": "https://localhost:8443/1.0/sign", "exp": 1634667515, "iat": 1634667215, "iss": "", "jti": "ffa63c06b0f3ccb0de554711836042a877e32a0322df42d617c6da59af65ec7d", "nbf": 1634667215, "sans": [ "localhost" ], "sha": "256538bb3b338289f80308ef286b00d4a4ac87b87fa3fcaff2234f2943572bb6", "sub": "localhost" }, "signature": "g8S-1Mrb9U3l3CbTRE-Mvcy2m2I-M2b_9KXj04SnqSxyMVDRzvnpoW3XYtlGgCcIexo5gQpOpe0QrkdZuKwhUQ" }

The raw $TOKEN is a series of base64-encoded values.

Once you have a token, pass it to the environment where the certificate will be created. Then continue:

$ step certificate create --csr localhost localhost.csr localhost.key Please enter the password to encrypt the private key: Your certificate signing request has been saved in localhost.csr. Your private key has been saved in localhost.key. $ step ca sign --token $TOKEN localhost.csr localhost.crt ✔ CA: https://localhost:8443 ✔ Certificate: localhost.crt $ step certificate inspect localhost.crt --short X.509v3 TLS Certificate (ECDSA P-256) [Serial: 1459...0708] Subject: localhost Issuer: Example Intermediate CA Provisioner: [ID: kvPj...CvkI] Valid from: 2021-10-19T18:14:55Z to: 2021-10-20T18:15:55Z

Renew a Certificate

Certificate renewal is easy, and is authenticated using the existing private key:

$ step ca renew foo.crt foo.key
✔ Would you like to overwrite foo.crt [y/n]: y
Your certificate has been saved in foo.crt.

When it comes time to renew your certificate, do not dawdle: Once a certificate expires, the CA will not renew it. To avoid catastrophe, you should set up automated renewals for any certificate that always needs to be valid. Our automation methods will attempt renewal at around two-thirds of a certificate's lifetime.

Note that because step ca renew uses mutual TLS authentication, it can only renew client certificates (certificates that are marked with "Client Authentication" key usage). Any other certificate must be replaced before expiry instead.

Revoke a Certificate

There is no way to "un-issue" a certificate. Once a certificate has been signed and distributed by the CA, it's valid until it expires.

However, you can ask the CA to block the renewal of a certificate. This is called passive revocation.

Let's revoke the svc.crt certificate we created earlier.

$ step ca revoke --cert svc.crt --key svc.key ✔ CA: https://localhost:4337 Certificate with Serial Number 30671613121311574910895916201205874495 has been revoked.

For more on this topic, read All About Certificate Revocation.

Working With SSH Certificates

In this section we'll go over the basics of issuing and renewing SSH certificates for users and hosts.


  • You will need a step-ca Certificate Authority with SSH support enabled. Create one by running step ca init --ssh.
  • To see SSH certificates in action, set up a host or VM running SSHD. In this example, our host is running Ubuntu 18.04 LTS, and it has been configured to access our CA remotely.

Establishing trust

When the CA is initialized with --ssh, it creates two SSH CA key pairs: one for the host CA, and one for the user CA. The user CA key signs SSH user certificates, and the host CA key signs SSH host certificates.

In this section, we will delegate SSH authentication on both ends of the SSH connection:

  • Hosts authenticating users: Hosts will trust the user CA key, thereby delegating the authentication of users to the CA. When a user presents their certificate to a host during the SSH handshake, the host will trust it when it's signed by the user CA key.
  • Users trusting hosts: Users will trust the host CA key, thereby delegating the authentication of hosts to the CA. When a host presents its host certificate to a user during the SSH handshake, the user will trust it when it's signed by the host CA key.

Getting a host to authenticate users

First let's delegate user authentication to the SSH CA.

1. Get your host to trust your SSH user CA

On your host, once you've bootstrapped your CA configuration, run:

$ step ssh config --roots > /path/to/

Add this to key your SSHD configuration. On your host, run:

$ cat <<EOF >> /etc/ssh/sshd_config
# This is the CA's public key for authenticating user certificates:
TrustedUserCAKeys /path/to/

Restart SSHD. Your host will now trust any user certificate issued by the CA. On Ubuntu 18.04 LTS, run:

$ systemctl restart ssh.service
2. Issue an SSH user certificate and test your connection

Let's create an SSH user certificate for the user alice:

$ step ssh certificate id_ecdsa ✔ Provisioner: (JWK) [kid: yWa7WGfoSt9yJ0OZCndrvR_m65jzDriY7mhPz094fdw] ✔ Please enter the password to decrypt the provisioner key: ✔ CA: Please enter the password to encrypt the private key: ✔ Private Key: id_ecdsa ✔ Public Key: ✔ Certificate: ✔ SSH Agent: yes

The CA issued a private key, a public key, and an SSH certificate, and step added the certificate and private key to our local SSH agent. We only need the private key and certificate to use SSH certificate authentication. Let's inspect the certificate:

$ cat | tail -1 | step ssh inspect -: Type: user certificate Public key: ECDSA-CERT SHA256:EPVTWfml136JG5FNR5xkFz7PRhUvuMUWzRXyQ+2zJfE Signing CA: ECDSA SHA256:yrTW8Ej/0kzGebLRvXIVFclXfA1dF/9VRiGjRnRcXl4 Key ID: "" Serial: 7887351871112993341 Valid: from 2020-09-22T11:27:56 to 2020-09-23T03:28:56 Principals: alice Critical Options: (none) Extensions: permit-pty permit-user-rc permit-X11-forwarding permit-agent-forwarding permit-port-forwarding

By default, when using SSH certificates, SSHD will allow you to connect as any of the listed principals. If the list of principals is empty, SSHD will allow you to authenticate as anyone on the host.

Now you have a certificate for your user, try to login on your host.

Getting SSH clients to trust hosts

Hosts can have SSH certificates, too, instead of the typical host public key pair. Host certificates allow clients who trust the host CA to avoid the trust on first use prompt.

1. Issue a certificate for your host

On your host, run:

$ step ssh certificate --host ssh_host_ecdsa_key ✔ Provisioner: (JWK) [kid: yWa7WGfoSt9yJ0OZCndrvR_m65jzDriY7mhPz094fdw] ✔ Please enter the password to decrypt the provisioner key: ✔ CA: Please enter the password to encrypt the private key: ✔ Private Key: ssh_host_ecdsa_key ✔ Public Key: ✔ Certificate:

Let's see what you created:

$ cat | step ssh inspect -: Type: host certificate Public key: ECDSA-CERT SHA256:Dc+Mzy43RzKoIaLrI8GzeHcQLbIa3hQ3mhirIDjbu0s Signing CA: ECDSA SHA256:IG7xVPz9kC6PwUTDDRvn+HIy1xUf/zllPo0InlSfaTg Key ID: "" Serial: 14283430004353679661 Valid: from 2020-09-22T11:34:40 to 2020-10-22T11:35:40 Principals: Critical Options: (none) Extensions: (none)

By default, host certificates expire in a month.

2. Install your host key and certificate

If you want your host to use this key, move the files to /etc/ssh and add the following to your SSHD configuration. On your host:

$ mv ssh_host_ecdsa_key /etc/ssh $ cat <<EOF >> /etc/ssh/sshd_config # This is our host private key and certificate: HostKey /etc/ssh/ssh_host_ecdsa_key HostCertificate /etc/ssh/ EOF
3. Automate host key rotation

Because the host certificate expires in a month, you'll also need to set up automated renewal of the certificate. Let's add a weekly renewal cron script that runs step ssh renew, on your host:

$ cat <<EOF > /etc/cron.weekly/rotate-ssh-certificate #!/bin/sh export STEPPATH=/root/.step cd /etc/ssh && step ssh renew ssh_host_ecdsa_key --force 2> /dev/null exit 0 EOF chmod 755 /etc/cron.weekly/rotate-ssh-certificate
4. Configure SSH clients to trust your host CA

To get your SSH clients to trust host certificates issued by your CA, you'll need to add the host CA key to SSH's .ssh/known_hosts file.

To view the host key, run:

$ step ssh config --host --roots ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFM2fTh3Utg3eGMqy2mJqa48Qsr7onZfOtIpJRNvvd71+eBUHBQdhnGmo2NPksNV3GxEuUZKAjZMlNv5c13XvfY=

Add this to the .ssh/known_hosts file on your SSH client, prepending @cert-authority * to it to mark it as a Certificate Authority, eg:

@cert-authority * ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFM2fTh3Utg3eGMqy2mJqa48Qsr7onZfOtIpJRNvvd71+eBUHBQdhnGmo2NPksNV3GxEuUZKAjZMlNv5c13XvfY=

Your SSH client will now trust any host with a valid certificate signed by the CA.

Test your connection

You're all done. Now test your SSH connection.

Next Steps

  • Our Tutorials can guide you through more complex scenarios for using step-ca in different contexts and workflows.
  • Run step ca help or step ssh help for many more flags and examples.
  • See Configuration, Core Concepts, and Production Considerations to learn more about tailoring step-ca to your infrastructure.