How-to guide · Labs and advanced
How to use SSH certificates instead of known_hosts
In one sentence
You will have OpenSSH host and user certificates signed by your own CAs, so clients trust servers without first-use prompts and servers trust short-lived user certificates.
- Platforms
- Linux
- macOS
- Windows
- Level
- Practitioner · 6 steps
On this page
Before you start
- OpenSSH on clients and servers. Any current version works; this guide was tested with OpenSSH 8.9 and 9.9.
- Administrator access to the servers’
sshd_config. - A safe place for two CA private keys: an offline machine, a hardware token or a signing service. Whoever holds the user CA key can log in anywhere that trusts it.
Why this matters: plain SSH relies on trust on first use. The first connection asks “The authenticity of host … can’t be established”, and almost everyone types yes. Each server also keeps its own authorized_keys list, which rarely gets cleaned up. An SSH certificate fixes both: a CA vouches for host and user keys, with names and an expiry date.
SSH certificates are not X.509 certificates. They use OpenSSH’s own simpler format, but the idea is the same: a trusted signer binds a key to a name for a limited time.
Steps
1. Create a host CA and a user CA
Use two separate CAs, so that the key that vouches for servers can never log anyone in:
ssh-keygen -t ed25519 -f host_ca -C "Example host CA"
ssh-keygen -t ed25519 -f user_ca -C "Example user CA"
Give both a strong passphrase when asked. host_ca.pub and user_ca.pub are public and go to every machine; the private halves stay on the signing machine.
2. Sign each server’s host key
Copy the server’s public host key to the signing machine, sign it with -h (host certificate), and list every name clients use with -n:
ssh-keygen -s host_ca -I web01.lab.test -h -n web01.lab.test,localhost -V +52w ssh_host_ed25519_key.pub
Signed host key ssh_host_ed25519_key-cert.pub: id "web01.lab.test" serial 0 for web01.lab.test,localhost valid from 2026-10-20T04:36:00 to 2027-10-19T04:37:01
Our lab server also answered as localhost, so that name is in the list; a real server lists its short name, its full name and any aliases. Copy ssh_host_ed25519_key-cert.pub back to /etc/ssh/ on the server and add one line to /etc/ssh/sshd_config:
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
3. Make clients trust the host CA
Add one @cert-authority line to known_hosts, globally in /etc/ssh/ssh_known_hosts or per user in ~/.ssh/known_hosts (on Windows, %USERPROFILE%\.ssh\known_hosts). The pattern says which host names this CA may vouch for:
@cert-authority *.lab.test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA… Example host CA
The rest of the line is the content of host_ca.pub. From now on, any server whose certificate is signed by this CA and lists the name you typed is accepted without a prompt, including servers that were rebuilt with new keys.
4. Issue short-lived user certificates
Users keep their own key pair. The CA signs the public key with a key ID (for logs), the principals (account names it may use) and a short validity. A unique serial number with -z makes revocation by serial possible:
ssh-keygen -s user_ca -I alice@example.com -n alice,deploy -V +8h -z 1001 id_ed25519.pub
ssh-keygen -L -f id_ed25519-cert.pub
id_ed25519-cert.pub:
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:fv87OrfS8dOFa/vp4pMtWjrbvCMrCb57fZ4MQoox024
Signing CA: ED25519 SHA256:5xODG4rQWAa3niuhYfPuIAzl9VpmBr4/L4I5ZXJeOTY (using ssh-ed25519)
Key ID: "alice@example.com"
Serial: 1001
Valid: from 2026-10-20T04:36:00 to 2026-10-20T12:37:33
Principals:
alice
deploy
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
The client finds id_ed25519-cert.pub automatically when it sits next to id_ed25519. Eight hours covers a working day; the next day the user gets a new certificate. Restrict what a certificate allows with -O, for example -O clear -O permit-pty to drop forwarding.
5. Make servers trust the user CA
Copy user_ca.pub to each server and add to sshd_config:
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/principals/%u
RevokedKeys /etc/ssh/revoked.krl
- With
TrustedUserCAKeysalone, a certificate may log in to an account whose name is one of its principals. AuthorizedPrincipalsFilechanges that to an explicit list per account:/etc/ssh/principals/deploymight containdeployandoncall. Accounts without a file accept no certificates.RevokedKeyspoints at a key revocation list (step 6). Create it before you add the line:sshdrefuses keys when the file cannot be read.
Check the configuration and reload: sudo sshd -t && sudo systemctl reload ssh (the service is called sshd on some distributions).
6. Revoke when needed
A key revocation list (KRL) can revoke certificates by serial number, by key ID or by key. Revoke serial 1001 from the user CA, then check it next to a second certificate for the same key with another serial (id_alice-cert.pub):
echo "serial: 1001" > revoke.txt
ssh-keygen -k -f revoked.krl -s user_ca.pub revoke.txt
ssh-keygen -Q -f revoked.krl id_ed25519-cert.pub id_alice-cert.pub
Revoking from revoke.txt
id_ed25519-cert.pub (alice laptop): REVOKED
id_alice-cert.pub (alice laptop): ok
Distribute revoked.krl to every server. With short-lived certificates, most departures need no revocation at all: you simply stop issuing.
Check it worked
Connect with verbose output and look for the certificate lines. Our lab server listened on port 2222, its known_hosts held only the @cert-authority line (with the pattern [localhost]:2222), and the account’s principals file allowed deploy:
ssh -v -p 2222 localhost true
debug1: Server host certificate: ssh-ed25519-cert-v01@openssh.com SHA256:amKQ68HAp56XNHO9957Og3G3CU+xr82HdvwD3VFigmE, serial 0 ID "web01.lab.test" CA ssh-ed25519 SHA256:r/i7Yd+fhYPjPqVrBuLr6q3yV9ng3JWbyT0PFPlQr8Q valid from 2026-10-20T04:36:00 to 2027-10-19T04:37:01
debug1: Host '[localhost]:2222' is known and matches the ED25519-CERT host certificate.
debug1: Server accepts key: …/id_ed25519 ED25519-CERT SHA256:fv87OrfS8dOFa/vp4pMtWjrbvCMrCb57fZ4MQoox024 explicit
Authenticated to localhost ([127.0.0.1]:2222) using "publickey".
The host was trusted through its certificate, with no prompt, and the server accepted the user’s certificate rather than a plain key.
If something goes wrong
On the server, sshd logs the reason a certificate was refused (journalctl -u ssh or /var/log/auth.log). The client only says Permission denied (publickey). Real messages from our lab:
Certificate does not contain an authorized principal: the certificate’s principals do not match the account, or the account’sAuthorizedPrincipalsFile. Re-issue with the right-n, or update the file.Certificate invalid: expired: issue a new certificate. If they expire mid-task, make the validity a little longer, not months.… revoked by file /etc/ssh/revoked.krl: the certificate’s serial, key ID or key is in the KRL.No ED25519 host key is known for … and you have requested strict checkingon the client: no@cert-authorityline matches the host name you typed, or the host certificate does not list that name. Comparessh-keygen -L -fon the host certificate with the name in your command.- The prompt still appears for some servers: those servers present no certificate (check
HostCertificateand reloadsshd), or the name you typed is missing from the certificate’s principals. Old plain entries inknown_hostscan also keep the client on the plain key; remove them withssh-keygen -R <host>.
Why this works
A certificate is a CA’s signature over a public key, a name and a validity period (binding identity to keys). Clients trust the host CA’s key instead of each server’s key, and servers trust the user CA instead of copies of every user’s key, exactly as browsers trust a root instead of every website. Keeping the CA keys offline and the lifetimes short follows the same logic as a private CA, and the KRL plays the role of a CRL (revocation).
Go deeper
- OpenBSD manual: ssh-keygen(1): signing,
-n,-V,-Ooptions and KRLs. - OpenBSD manual: sshd_config(5):
HostCertificate,TrustedUserCAKeys,AuthorizedPrincipalsFile,RevokedKeys. - OpenBSD manual: sshd(8), SSH_KNOWN_HOSTS FILE FORMAT: the
@cert-authoritymarker. - IETF draft: SSH Certificate Format: the OpenSSH certificate format, written up for the IETF’s SSH working group.
Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.