step-ca Cheat Sheet
Overview
step-ca is an open-source online Certificate Authority (CA) from Smallstep that enables organizations to run their own private PKI infrastructure for issuing and managing X.509 certificates (TLS/HTTPS) and SSH certificates. Unlike traditional CAs that require manual certificate signing requests and approval workflows, step-ca provides automated certificate issuance through ACME (the same protocol used by Let’s Encrypt), JWK provisioners, OAuth/OIDC integration, and cloud provider identity tokens. This automation enables short-lived certificates (hours instead of years) that dramatically reduce the risk of certificate compromise.
step-ca is designed for modern infrastructure patterns including mutual TLS (mTLS) between microservices, SSH certificate authentication (eliminating SSH key sprawl), Kubernetes service mesh certificate management, and zero-trust networking. It supports multiple key types (ECDSA, Ed25519, RSA), hardware security modules (HSMs) via PKCS#11 and cloud KMS (AWS KMS, Google Cloud KMS, Azure Key Vault), and integrates with identity providers for certificate issuance based on SSO authentication. The companion step CLI tool handles certificate operations, key management, and CA interaction from the command line.
Installation
step CLI
# macOS
brew install step
# Linux (Debian/Ubuntu)
wget https://dl.smallstep.com/cli/docs-cli-install/latest/step-cli_amd64.deb
sudo dpkg -i step-cli_amd64.deb
# Linux (RHEL/CentOS)
wget https://dl.smallstep.com/cli/docs-cli-install/latest/step-cli_amd64.rpm
sudo rpm -i step-cli_amd64.rpm
# Windows
scoop install step
step-ca Server
# macOS
brew install step-ca
# Linux (Debian/Ubuntu)
wget https://dl.smallstep.com/certificates/docs-ca-install/latest/step-ca_amd64.deb
sudo dpkg -i step-ca_amd64.deb
# Docker
docker run -d -p 9000:9000 \
-v step:/home/step \
smallstep/step-ca
# Verify
step-ca version
step version
Initial Setup
Initialize CA
# Interactive CA initialization
step ca init
# Non-interactive initialization
step ca init \
--name "My Private CA" \
--provisioner admin \
--dns ca.example.com \
--address :443 \
--password-file /path/to/password.txt
# With specific key type
step ca init \
--name "My Private CA" \
--provisioner admin \
--dns ca.example.com \
--address :443 \
--key-type EC \
--curve P-256
# Initialize with SSH certificate support
step ca init --ssh
Start the CA
# Start CA server
step-ca $(step path)/config/ca.json
# Start with password from file
step-ca $(step path)/config/ca.json --password-file /path/to/password.txt
# Start as systemd service
sudo systemctl enable --now step-ca
# Verify CA is running
step ca health
Bootstrap Client
# Bootstrap client to trust the CA
step ca bootstrap --ca-url https://ca.example.com --fingerprint <root-fingerprint>
# Or with automatic trust
step ca bootstrap --ca-url https://ca.example.com --install
Core Commands
Certificate Operations
| Command | Description |
|---|---|
step ca certificate <cn> cert.pem key.pem | Request a certificate |
step ca renew cert.pem key.pem | Renew a certificate |
step ca revoke <serial> | Revoke a certificate |
step ca token <cn> | Generate a one-time token |
step certificate inspect cert.pem | Inspect certificate details |
step certificate verify cert.pem | Verify certificate chain |
step ca root | Download CA root certificate |
# Request TLS certificate
step ca certificate server.example.com server.crt server.key
# Request with specific provisioner
step ca certificate server.example.com server.crt server.key \
--provisioner admin
# Request with SANs
step ca certificate server.example.com server.crt server.key \
--san server.example.com \
--san 192.168.1.10 \
--san server.internal
# Request with custom duration
step ca certificate server.example.com server.crt server.key \
--not-after 720h
# Request wildcard certificate
step ca certificate "*.example.com" wildcard.crt wildcard.key
# Inspect issued certificate
step certificate inspect server.crt
# Verify certificate
step certificate verify server.crt --roots $(step path)/certs/root_ca.crt
# Renew certificate
step ca renew server.crt server.key
# Auto-renew with daemon
step ca renew --daemon server.crt server.key
# Revoke certificate
step ca revoke --serial <serial-number> --reason "Key Compromise"
step ca revoke server.crt server.key
SSH Certificate Operations
# Issue SSH user certificate
step ssh certificate user@example.com id_ecdsa --provisioner admin
# Issue SSH host certificate
step ssh certificate server.example.com ssh_host_ecdsa_key \
--host --provisioner admin \
--principal server.example.com \
--principal 192.168.1.10
# Configure SSH client to use certificates
# Add to ~/.ssh/config:
# Host *.example.com
# CertificateFile ~/.ssh/id_ecdsa-cert.pub
# Configure SSH server to trust CA
step ssh config --roots > /etc/ssh/ssh_user_key.pub
# Add to sshd_config: TrustedUserCAKeys /etc/ssh/ssh_user_key.pub
# List SSH hosts
step ssh hosts
# Inspect SSH certificate
step ssh inspect id_ecdsa-cert.pub
Configuration
CA Configuration
{
"root": "/etc/step-ca/certs/root_ca.crt",
"federatedRoots": null,
"crt": "/etc/step-ca/certs/intermediate_ca.crt",
"key": "/etc/step-ca/secrets/intermediate_ca_key",
"address": ":443",
"dnsNames": ["ca.example.com"],
"logger": {"format": "text"},
"db": {
"type": "badgerv2",
"dataSource": "/etc/step-ca/db"
},
"authority": {
"provisioners": [
{
"type": "JWK",
"name": "admin",
"key": { "...": "..." },
"encryptedKey": "..."
}
],
"claims": {
"minTLSCertDuration": "5m",
"maxTLSCertDuration": "720h",
"defaultTLSCertDuration": "24h",
"minHostSSHCertDuration": "5m",
"maxHostSSHCertDuration": "1680h",
"defaultHostSSHCertDuration": "720h",
"minUserSSHCertDuration": "5m",
"maxUserSSHCertDuration": "24h",
"defaultUserSSHCertDuration": "16h"
}
},
"tls": {
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
],
"minVersion": 1.2,
"maxVersion": 1.3
}
}
Provisioner Types
# Add ACME provisioner (Let's Encrypt compatible)
step ca provisioner add acme --type ACME
# Add OIDC provisioner (SSO-based certificates)
step ca provisioner add google --type OIDC \
--client-id <client-id> \
--client-secret <client-secret> \
--configuration-endpoint https://accounts.google.com/.well-known/openid-configuration \
--domain example.com
# Add AWS provisioner (EC2 instance identity)
step ca provisioner add aws --type AWS \
--aws-account <account-id>
# Add GCP provisioner
step ca provisioner add gcp --type GCP \
--gcp-project <project-id>
# Add Azure provisioner
step ca provisioner add azure --type Azure \
--azure-tenant <tenant-id>
# Add X5C provisioner (certificate-based)
step ca provisioner add x5c --type X5C \
--x5c-roots /path/to/trusted-root.pem
# List provisioners
step ca provisioner list
Advanced Usage
ACME with Certbot/Caddy
# Use step-ca as ACME server with certbot
certbot certonly --standalone \
--server https://ca.example.com/acme/acme/directory \
-d server.example.com
# Caddy automatic HTTPS with step-ca
# Caddyfile:
# {
# acme_ca https://ca.example.com/acme/acme/directory
# acme_ca_root /etc/step-ca/certs/root_ca.crt
# }
# server.example.com {
# respond "Hello, mTLS!"
# }
Mutual TLS (mTLS)
# Issue client certificate
step ca certificate client.example.com client.crt client.key
# Configure Nginx for mTLS
# nginx.conf:
# server {
# listen 443 ssl;
# ssl_certificate /etc/nginx/server.crt;
# ssl_certificate_key /etc/nginx/server.key;
# ssl_client_certificate /etc/nginx/ca.crt;
# ssl_verify_client on;
# }
# Test mTLS connection
curl --cert client.crt --key client.key \
--cacert $(step path)/certs/root_ca.crt \
https://server.example.com
Kubernetes Integration
# Deploy step-ca with Helm
# helm install step-certificates smallstep/step-certificates
# cert-manager integration
apiVersion: certmanager.step.sm/v1beta1
kind: StepIssuer
metadata:
name: step-issuer
spec:
url: https://ca.example.com
caBundle: <base64-encoded-root-ca>
provisioner:
name: admin
kid: <provisioner-kid>
passwordRef:
name: step-provisioner-password
key: password
HSM/KMS Integration
# Initialize CA with Google Cloud KMS
step ca init \
--name "HSM-backed CA" \
--provisioner admin \
--kms googlekms \
--dns ca.example.com
# Initialize with AWS KMS
step ca init \
--name "AWS KMS CA" \
--provisioner admin \
--kms awskms \
--dns ca.example.com
# Initialize with YubiKey
step ca init \
--name "YubiKey CA" \
--provisioner admin \
--kms yubikey \
--dns ca.example.com
Troubleshooting
| Issue | Solution |
|---|---|
| CA won’t start | Check password file, verify certificate/key paths in ca.json |
| Certificate request denied | Verify provisioner name and credentials, check CA logs |
| Client doesn’t trust CA | Run step ca bootstrap --install to install root cert |
| ACME challenge fails | Ensure port 80/443 is accessible, check DNS resolution |
| SSH certificate rejected | Verify TrustedUserCAKeys in sshd_config points to correct CA public key |
| Certificate expired too quickly | Adjust defaultTLSCertDuration in CA claims configuration |
| Connection refused | Verify CA address/port in config, check firewall rules |
| Database corruption | Stop CA, backup db directory, restart to rebuild |