| Tested on | RHEL 10.2 as SSH server (192.168.56.116); Ubuntu 26.04 as CA/admin workstation AND SSH client (192.168.56.121) |
|---|---|
| Package | openssh-server-9.9p1-25.el10_2.x86_64 / OpenSSH_10.2p1 Ubuntu |
| Applies to | Any Linux distribution with modern OpenSSH (TrustedUserCAKeys, ssh-keygen -s) |
| Privilege | sudo or root on the SSH server for sshd_config and reload; normal user for CA signing, client keys, and login tests |
| Scope | OpenSSH-native user certificate authentication with an SSH CA (TrustedUserCAKeys, principals, expiry, KRL, AuthorizedPrincipalsFile). Brief host certificates section. Does not cover Vault, Smallstep, Teleport, or X.509/TLS certificates. |
| Related guides | Fail2ban for SSH Restrict SSH users and groups SSH host-based authentication Test SSH connection |
In this article we will configure OpenSSH certificate authentication with an SSH CA so servers trust one CA public key instead of copying every user public key into authorized_keys. You still keep a normal SSH key pair; the CA signs the public key and the client presents the resulting user certificate at login.
I ran the full path with Ubuntu 26.04 holding both the CA private key and the SSH client, and RHEL 10.2 as the SSH server: create a user CA, install only the CA public key, set TrustedUserCAKeys, sign a key with principals and a short validity window, log in, expire and restrict the certificate, revoke it with a KRL, then map a role principal with AuthorizedPrincipalsFile. Host certificates appear later as a short follow-on.
What is OpenSSH certificate authentication?
Traditional SSH key authentication usually means copying each user's public key onto every host that should accept them:
User key
|
+---> Server 1 ~/.ssh/authorized_keys
+---> Server 2 ~/.ssh/authorized_keys
+---> Server 3 ~/.ssh/authorized_keysThat works for a handful of boxes. It hurts when you have many users, many servers, or frequent join and leave events.
With OpenSSH certificate authentication, servers trust a certificate authority instead of every individual user key:
SSH CA
|
signs key
|
v
User SSH certificate
|
+---------+---------+
v v v
Server 1 Server 2 Server 3
| | |
+-- trust the same CA --+In practice:
- The user still has an SSH key pair (private key stays on the client).
- The CA signs the user's public key and produces an OpenSSH user certificate.
- Each SSH server trusts the CA public key through
TrustedUserCAKeys. - The certificate can name which accounts it may use (principals) and when it expires.
ssh-keygen. Do not expect openssl x509 or a browser trust store to validate them.
SSH certificates vs normal SSH keys
A certificate does not replace the key. It is signed metadata wrapped around a public key.
| Feature | Normal SSH key | SSH certificate |
|---|---|---|
| Server stores or trusts | Individual public key in authorized_keys |
CA public key (TrustedUserCAKeys) |
| User onboarding | Copy the public key to each server | CA signs the user's public key |
| Expiration | Not built into a normal public key | Built into the certificate |
| Allowed usernames | Usually controlled by which account's authorized_keys holds the key |
Certificate principals (and optional AuthorizedPrincipalsFile) |
| Central revocation | Remove the key from every host | Short-lived certs and/or a KRL |
| Native OpenSSH support | Yes | Yes |
If you already know how public-key login works, think of the certificate as a time-bound, CA-signed permission slip for that same key.
User certificates vs host certificates
These solve different problems. Many tutorials mix them; keep them separate.
User certificate
Used when a user authenticates to an SSH server. The server trusts a user CA through TrustedUserCAKeys.
Host certificate
Used when a client verifies the identity of an SSH server. Clients trust a host CA, usually with a @cert-authority line in known_hosts.
This article focuses first on user certificate authentication, because that is what replaces large-scale authorized_keys management. Host certificates come later as a shorter section.
Lab topology and roles
In this lab, one Ubuntu host does two jobs: it holds the CA private key and it runs the SSH client. The RHEL host is only the SSH server. That split matters when you copy files, because the CA private key must never land on the SSH server.
Ubuntu 26.04 (192.168.56.121)
├─ CA / admin workstation
└─ SSH client
RHEL 10.2 (192.168.56.116)
└─ SSH server| Host | Role | What lives there |
|---|---|---|
Ubuntu 192.168.56.121 |
CA and SSH client | User CA private key, user SSH key, signed *-cert.pub |
RHEL 192.168.56.116 |
SSH server | CA public key only, sshd drop-ins, local Linux accounts |
TrustedUserCAKeys. Anyone who steals the CA private key can mint certificates your fleet will accept.
Check your OpenSSH version
OpenSSH certificate authentication has been available for many years. Confirm versions on both sides so you know which client and server you are debugging.
On the RHEL SSH server, print the daemon version:
sshd -VOpenSSH_9.9p1, OpenSSL 3.5.5 27 Jan 2026Confirm the installed packages as well:
rpm -q openssh openssh-serveropenssh-9.9p1-25.el10_2.x86_64
openssh-server-9.9p1-25.el10_2.x86_64On the Ubuntu client and CA workstation, check the client version:
ssh -VOpenSSH_10.2p1 Ubuntu-2ubuntu3.6Modern packages on RHEL, Rocky Linux, AlmaLinux, Fedora, Ubuntu, and Debian all support this feature. Older hosts may still work; check sshd -V instead of guessing from the distro name alone.
Create an SSH certificate authority
Generate a dedicated CA key pair on the Ubuntu CA workstation. Prefer a strong passphrase in production so a stolen CA file is not immediately usable.
Create an Ed25519 user CA and enter a passphrase when ssh-keygen prompts:
ssh-keygen -t ed25519 -f ~/ssh-user-ca -C lab-user-caGenerating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/deepak/ssh-user-ca
Your public key has been saved in /home/deepak/ssh-user-ca.pub
The key fingerprint is:
SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRk lab-user-ca-N '') so non-interactive re-signing is easy. Do not treat an empty CA passphrase as a production practice.
You get two files:
| File | Role |
|---|---|
ssh-user-ca |
Private CA key (signs certificates; protect it) |
ssh-user-ca.pub |
Public CA key (distribute to SSH servers) |
Show the public key you will install on the server:
cat ~/ssh-user-ca.pubssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMaOnLsLUQtvuKYScyVANFIz1aEx2l1srmj1OYS4boMU lab-user-caDo not copy the private CA key onto ordinary SSH servers. Only the .pub file belongs there.
Transfer the CA public key to the SSH server
Copy only the public CA key from Ubuntu to the RHEL SSH server. Use the scp command so the private key never leaves the CA workstation.
On Ubuntu, upload the public key to a temporary path on the server:
scp ~/ssh-user-ca.pub deepak@192.168.56.116:/tmp/user_ca.pubscp prints a progress line and exits when the transfer finishes. On the RHEL server, install the file with mode 0644 under /etc/ssh/:
sudo install -m 0644 /tmp/user_ca.pub /etc/ssh/user_ca.pubinstall prints nothing when it succeeds. Confirm the installed file matches the CA public key from Ubuntu before you point sshd at it.
Configure TrustedUserCAKeys
Prefer a drop-in under /etc/ssh/sshd_config.d/ so the main config stays clean. Put only TrustedUserCAKeys in this file; later steps add separate drop-ins for revocation and principals.
On the RHEL server, create the CA trust drop-in:
sudo tee /etc/ssh/sshd_config.d/50-user-ca.conf >/dev/null <<'EOF'
TrustedUserCAKeys /etc/ssh/user_ca.pub
EOFTrustedUserCAKeys is the file that lists CA public keys allowed to sign user certificates that sshd will accept. This setting does not disable authorized_keys. Existing public-key entries keep working unless you change other authentication settings.
Validate the config before reloading:
sudo sshd -tA clean sshd -t prints nothing. Reload the daemon on RHEL-family hosts with systemctl:
sudo systemctl reload sshdOn Ubuntu and Debian servers the unit is often named ssh, so use systemctl reload ssh there. Confirm the effective setting:
sudo sshd -T | grep trustedusercakeystrustedusercakeys /etc/ssh/user_ca.pubLowercase names in sshd -T are normal. The path should match the public CA file you installed.
Create or reuse a user SSH key
If the account already has a key, reuse it. Certificates expire; keys do not need to be regenerated every time you re-sign. For flag details beyond this guide, see generate SSH key.
On the Ubuntu client, create an Ed25519 key when needed and enter a passphrase when prompted:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519ssh-keygen writes the key pair under ~/.ssh/ and prints a fingerprint summary when it finishes. The private key stays on the client. An empty passphrase is fine for some automation keys, but it is not a requirement for certificates. In production, send only ~/.ssh/id_ed25519.pub to the CA operator for signing. Do not generate a new key every time a certificate expires; re-sign the same public key with a new validity window.
Sign the user public key
Before you sign: in this lab, Ubuntu is both the CA workstation and the SSH client, so the CA private key and the user key sit on the same host. In production, the user sends only their .pub file to the CA operator and gets *-cert.pub back. The CA private key never travels with the user.
Sign for account deepak with an eight-hour window and a readable key ID:
ssh-keygen -s ~/ssh-user-ca -I deepak-2026-lab -n deepak -V +8h ~/.ssh/id_ed25519.pubSigned user key /home/deepak/.ssh/id_ed25519-cert.pub: id "deepak-2026-lab" serial 0 for deepak valid from 2026-09-22T09:50:00 to 2026-09-22T17:51:29Important options:
| Option | Meaning |
|---|---|
-s |
Path to the CA private key |
-I |
Certificate identity (shows up in logs; use a clear label) |
-n |
Principals (accounts or roles the cert may assert) |
-V |
Validity interval |
| Final argument | Path to the user's .pub file |
The signed certificate is written beside the key as ~/.ssh/id_ed25519-cert.pub. OpenSSH automatically looks for id_ed25519-cert.pub next to id_ed25519 when you connect with that identity.
Understand SSH certificate principals
Principals control which account names the certificate may authenticate as, subject to server policy.
A single principal matching the Linux username:
-n deepakMultiple principals:
-n deepak,adminWhen TrustedUserCAKeys is set and you have not configured AuthorizedPrincipalsFile, the login username must normally appear in the certificate's principal list. If you try to log in as deepak with a certificate that only lists developers, authentication fails until you either re-sign or add AuthorizedPrincipalsFile mapping (covered later).
Principals are not Linux groups by themselves. They are strings the CA puts in the certificate; the server decides how those strings map to local accounts.
Inspect the certificate
Always inspect what you signed before you rely on it in production.
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub/home/deepak/.ssh/id_ed25519-cert.pub:
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU
Signing CA: ED25519 SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRk (using ssh-ed25519)
Key ID: "deepak-2026-lab"
Serial: 0
Valid: from 2026-09-22T09:50:00 to 2026-09-22T17:51:29
Principals:
deepak
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rcRead each field:
- Type — user certificate (not a host certificate).
- Public key — fingerprint of the certified key material.
- Signing CA — must match the CA you configured in
TrustedUserCAKeys. - Key ID — the
-Istring; useful insshdlogs. - Serial — numeric serial (default
0unless you set-z). Use a non-zero-zvalue if you plan to revoke by serial or serial range later. Use a non-zero-zvalue if you plan to revoke by serial or serial range later. - Valid — hard start and end times.
- Principals — names the cert may assert.
- Critical Options — restrictions such as
force-commandorsource-address. - Extensions — optional capabilities such as PTY and forwarding.
If principals or validity look wrong, re-sign before you chase sshd configuration.
Log in with the certificate
From the Ubuntu client, connect as the principal you signed. The ssh command pairs the private key with the matching -cert.pub automatically.
ssh deepak@192.168.56.116A successful login drops you into a shell on the RHEL server. If you need to force the identity file, pass -i or set IdentityFile in ssh_config:
ssh -i ~/.ssh/id_ed25519 deepak@192.168.56.116For proof that the certificate (not a bare key from authorized_keys) was used, raise verbosity and keep only the useful lines:
ssh -vv deepak@192.168.56.116debug1: Offering public key: /home/deepak/.ssh/id_ed25519 ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU
debug1: Server accepts key: /home/deepak/.ssh/id_ed25519 ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU
Authenticated to 192.168.56.116 ([192.168.56.116]:22) using "publickey".ED25519-CERT in the offering and accept lines is the signal you want. Plain ED25519 without -CERT would mean a normal key path.
Verify acceptance in sshd logs
After a successful client login, read the recent sshd journal on the RHEL server. On Ubuntu or Debian servers, swap the unit name to ssh. Unit filtering and time windows are covered in the journalctl reference.
sudo journalctl -u sshd -n 20 --no-pagerSep 22 09:51:31 vm1.lab.example sshd-session[34894]: Accepted publickey for deepak from 192.168.56.121 port 58130 ssh2: ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU ID deepak-2026-lab (serial 0) CA ED25519 SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRkThe server log confirms that sshd accepted the certificate and shows which CA signed it. The Key ID from -I and the serial appear on the same line.
Certificate auth rides on the public-key path. It does not require a separate AuthenticationMethods value by itself, and other OpenSSH authentication methods can still be enabled in sshd_config.
Set certificate expiration
Expiration is one of the strongest reasons to prefer certificates over permanent authorized_keys entries. A short-lived certificate reduces the window in which a stolen key plus its currently issued certificate can be used.
Sign with a short window. A small negative start offset helps when client and server clocks differ slightly:
ssh-keygen -s ~/ssh-user-ca -I deepak-short -n deepak -V -5m:+1m ~/.ssh/id_ed25519.pubInspect the new validity range:
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pubConfirm the Valid: line shows the intended start and end. After the end time, the server rejects the certificate even if the CA is still trusted and the private key is intact:
ssh deepak@192.168.56.116deepak@192.168.56.116: Permission denied (publickey,password).Issue a fresh certificate against the same user key when access should continue. For many environments, short-lived certificates reduce how often you need explicit revocation.
Restrict certificate capabilities
By default, a freshly signed user certificate includes several extensions: PTY allocation, agent forwarding, port forwarding, X11 forwarding, and permit-user-rc. Those defaults are convenient for interactive admin use. They are too broad for automation accounts or break-glass keys.
-O clear removes those default extensions first. After clear, you add only the options you want, such as force-command or selective permit-* flags. The user cannot strip a critical option from a signed certificate.
Clear defaults and force a fixed command string:
ssh-keygen -s ~/ssh-user-ca -I deepak-forced -n deepak -V +8h -O clear -O force-command='echo FORCED_ONLY; /usr/bin/id' ~/.ssh/id_ed25519.pubInspect the certificate and confirm Critical Options list force-command while Extensions is empty:
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub Critical Options:
force-command echo FORCED_ONLY; /usr/bin/id
Extensions: (none)Connect. Instead of an interactive shell, the forced command runs and exits:
ssh deepak@192.168.56.116FORCED_ONLY
uid=1012(deepak) gid=1012(deepak) groups=1012(deepak) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023Useful related options include source-address and selective permit flags. Start with one controlled restriction like force-command before you layer many options.
Revoke a certificate with a KRL
When you must cut access before a certificate expires, build a Key Revocation List (KRL) and point sshd at it with RevokedKeys. This section covers the shortest create-and-enforce path. For serial ranges, Key IDs, fingerprints, plain-key revoke, and fleet distribution, use the dedicated OpenSSH KRL revocation guide.
Copy the certificate you want to revoke onto the RHEL server (for example under /tmp/), or build the KRL on the CA host and copy the KRL file. On the server, create a new KRL from the CA public key and the certificate. Do not use -u on the first create:
sudo ssh-keygen -k -f /etc/ssh/revoked_keys -s /etc/ssh/user_ca.pub /tmp/id_ed25519-cert.pubssh-keygen -k writes the KRL file and prints nothing on success when the path is new. Later, when you need to add another certificate to an existing KRL, update it with -u:
sudo ssh-keygen -k -u -f /etc/ssh/revoked_keys -s /etc/ssh/user_ca.pub /tmp/another-cert.pubPoint sshd at the KRL in a separate drop-in. Do not overwrite 50-user-ca.conf:
sudo tee /etc/ssh/sshd_config.d/70-user-cert-revocation.conf >/dev/null <<'EOF'
RevokedKeys /etc/ssh/revoked_keys
EOFValidate the drop-in before you reload:
sudo sshd -tA clean sshd -t prints nothing. Reload so RevokedKeys is active:
sudo systemctl reload sshdQuery the KRL before you test login (run this where the certificate file is available):
ssh-keygen -Q -f /etc/ssh/revoked_keys /tmp/id_ed25519-cert.pub/tmp/id_ed25519-cert.pub REVOKEDA login attempt with that certificate should fail:
ssh deepak@192.168.56.116Permission denied (publickey,password).Confirm the server rejected the credential because of the KRL:
sudo journalctl -u sshd -n 20 --no-pagererror: Authentication key ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU revoked by file /etc/ssh/revoked_keysLeave revoked certificates in the KRL. Issue a new certificate with a new serial when access should return, rather than trying to resurrect the old credential.
Configure AuthorizedPrincipalsFile
Until now the basic path used -n deepak so the certificate principal matched the login username. AuthorizedPrincipalsFile moves that mapping onto the server so a role name such as developers can authorize one or more local accounts.
Add a separate drop-in. Do not fold this into the CA or revocation files:
sudo tee /etc/ssh/sshd_config.d/60-user-principals.conf >/dev/null <<'EOF'
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
EOFCreate the principals directory. %u expands to the target username, so the file for this account is /etc/ssh/auth_principals/deepak:
sudo mkdir -p /etc/ssh/auth_principalsmkdir -p prints nothing when the directory already exists or was created. Write the allowed principal string into the account file:
echo developers | sudo tee /etc/ssh/auth_principals/deepakdevelopersValidate the updated drop-in before you reload:
sudo sshd -tA clean sshd -t prints nothing. Reload so the principals file takes effect:
sudo systemctl reload sshdOpenSSH consults AuthorizedPrincipalsFile only for certificates authenticated via a CA listed in TrustedUserCAKeys.
If you still hold a certificate whose only principal is deepak, login fails once the principals file requires developers. Re-sign the user key with the role principal the file allows:
ssh-keygen -s ~/ssh-user-ca -I deepak-dev-role -n developers -V +8h ~/.ssh/id_ed25519.pubAfter that, ssh deepak@192.168.56.116 succeeds again because the certificate carries developers and /etc/ssh/auth_principals/deepak lists that string. The journal shows the new Key ID:
sudo journalctl -u sshd -n 20 --no-pagerAccepted publickey for deepak from 192.168.56.121 port 48684 ssh2: ED25519-CERT SHA256:Dh0YicssAH/zkefNQQTmvkkw9FIGvSbijtaP3nx6RVU ID deepak-dev-role (serial 0) CA ED25519 SHA256:sm7WB0v4N6lt0f7g2t0KGtQsFZ7HPjDnD1ien0tXFRkWithout AuthorizedPrincipalsFile
The certificate principal normally matches the login username (-n deepak to log in as deepak).
With AuthorizedPrincipalsFile
The server maps certificate principals to local accounts. The same principal, such as developers, can be authorized for multiple local accounts by listing it in each account's principals file.
SSH CA vs authorized_keys
| Topic | authorized_keys |
SSH CA |
|---|---|---|
| Trust model | Individual public keys | CA public key |
| Adding a user | Update servers | Issue a certificate |
| Removing access | Remove the key everywhere | Let a short-lived cert expire, or revoke |
| Expiration | Manual or external process | Native in the certificate |
| Principals | Limited | Native |
| Large fleets | Harder to keep consistent | Easier once the CA is trusted |
| One user, one server | Usually simpler | Often unnecessary overhead |
SSH certificates pay off when you manage multiple users or multiple servers. For a single admin key on a single host, plain authorized_keys is usually enough. Remember that enabling TrustedUserCAKeys does not turn off authorized_keys by itself.
SSH certificates vs X.509
Keep this distinction clear when searching or talking to colleagues:
- OpenSSH certificates are not TLS or SSL certificates.
- There is no X.509 chain, CSR, or web PKI involved.
- You create them with
ssh-keygen -s, not withopenssl req. - Only OpenSSH (and tools that speak its certificate format) understand them.
If a product asks for an X.509 client certificate for HTTPS or LDAP, that is a different system. This guide stays on the OpenSSH-native path.
Host certificates
Host certificates solve a different problem: clients trust a host CA instead of learning every server host key through TOFU and known_hosts sprawl. Keep this short and sign off the SSH server.
Generate a separate host CA on the Ubuntu CA workstation (do not reuse the user CA). Enter a passphrase when prompted, the same way you protected the user CA:
ssh-keygen -t ed25519 -f ~/ssh-host-ca -C lab-host-caShow the host CA public key you will trust on clients:
cat ~/ssh-host-ca.pubssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGd6rD+vUVC3vp4BBRy3lKsC1k1OMyI3rzgFlV4L3ybT lab-host-caCopy the server's host public key from RHEL to Ubuntu. Sign on Ubuntu. Never place the host CA private key on the SSH server.
On the RHEL server, stage the host public key where the CA operator can fetch it:
sudo cp /etc/ssh/ssh_host_ed25519_key.pub /tmp/From Ubuntu, pull that public key onto the CA workstation:
scp deepak@192.168.56.116:/tmp/ssh_host_ed25519_key.pub ~/Sign it as a host certificate with hostname and address principals:
ssh-keygen -s ~/ssh-host-ca -I vm1-lab -h -n vm1.lab.example,192.168.56.116 -V +52w ~/ssh_host_ed25519_key.pubCopy only the resulting certificate back to the server:
scp ~/ssh_host_ed25519_key-cert.pub deepak@192.168.56.116:/tmp/On RHEL, install the host certificate and point sshd at it:
sudo install -m 0644 /tmp/ssh_host_ed25519_key-cert.pub /etc/ssh/ssh_host_ed25519_key-cert.pubCreate a host-certificate drop-in:
sudo tee /etc/ssh/sshd_config.d/50-host-cert.conf >/dev/null <<'EOF'
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
EOFValidate the host-certificate drop-in:
sudo sshd -tA clean sshd -t prints nothing. Reload so sshd presents the host certificate:
sudo systemctl reload sshdOn the Ubuntu client, trust the host CA in ~/.ssh/known_hosts. Scope the @cert-authority line to the lab hosts, not a bare *:
@cert-authority vm1.lab.example,192.168.56.116 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGd6rD+vUVC3vp4BBRy3lKsC1k1OMyI3rzgFlV4L3ybT lab-host-caA *.lab.example pattern is also fine when every host in that DNS zone presents certificates from this host CA. Connect and confirm the client accepts the host certificate:
ssh -o StrictHostKeyChecking=yes deepak@192.168.56.116 'echo HOST_CERT_OK'HOST_CERT_OKInspect a host certificate the same way as a user certificate (ssh-keygen -L); the type line says host certificate, and principals list vm1.lab.example and 192.168.56.116.
User CA vs host CA
Keep signing keys separate.
User CA
|
+-- signs user certificates (TrustedUserCAKeys)
Host CA
|
+-- signs host certificates (HostCertificate / @cert-authority)Keep the two CA keys separate. If the user CA is compromised, an attacker should not also gain the ability to create trusted host certificates.
Distro differences
The directives are OpenSSH-standard. Distro differences are mostly packaging and unit names.
| Item | RHEL / Rocky Linux / AlmaLinux | Ubuntu / Debian |
|---|---|---|
| Config | /etc/ssh/sshd_config and /etc/ssh/sshd_config.d/ |
Same layout on modern releases |
| Service unit | sshd |
Often ssh |
| Logs | journalctl -u sshd |
journalctl -u ssh |
| Core directives | TrustedUserCAKeys, AuthorizedPrincipalsFile, RevokedKeys, HostCertificate |
Same OpenSSH names |
TrustedUserCAKeys, principals, ssh-keygen -s, and ssh-keygen -L are OpenSSH features, not RHEL-only features. Fedora follows the same pattern as other modern OpenSSH packages.
Troubleshooting
| Problem | Likely cause | Fix |
|---|---|---|
Permission denied (publickey) |
Principal does not match the login user or principals file | Run ssh-keygen -L -f *-cert.pub and re-sign with the right -n |
| Certificate ignored | CA not trusted on the server | sshd -T | grep trustedusercakeys and confirm the file contents |
| Certificate expired | Validity window ended | Re-sign with a new -V window |
| Certificate not offered | Filename does not sit beside the private key | Keep id_ed25519-cert.pub next to id_ed25519 |
| Key works, certificate does not | TrustedUserCAKeys or principals mismatch |
Use ssh -vv on the client and journalctl on the server |
Certificate does not contain an authorized principal |
Missing from AuthorizedPrincipalsFile |
Add the principal string for that account, or re-sign |
| Revoked cert still works | RevokedKeys missing or not reloaded |
Set the directive in its own drop-in, run sshd -t, reload |
| Reload fails | Syntax error in a drop-in | Fix the file using sshd -t output |
If failed attempts trigger temporary refusals on newer OpenSSH, check OpenSSH PerSourcePenalties so a noisy test client is not blocking itself while you debug certificates. Pair that with the usual steps to prevent brute-force SSH attacks when you expose SSH on the network.
Security recommendations
- Protect the CA private key; treat it like a root password for your SSH fleet.
- Prefer signing on a dedicated admin host, not on every SSH server.
- Use short certificate lifetimes; add a KRL when you must revoke early.
- Put meaningful values in
-I, and set a non-zero serial with-zwhen you expect to revoke by serial or serial range later. - Keep user CA and host CA keys separate.
- Use separate
sshddrop-ins for CA trust, principals, and revocation so one change does not erase another. - Always run
sshd -tbefore reload when you change authentication. - Keep a second recovery session open while you change remote
sshdauthentication settings. - Scope
@cert-authoritylines to real hostnames or address lists, not a bare*.
This is enough to run OpenSSH-native certificates safely at small and medium scale. Full offline CA ceremonies and hardware tokens are a larger operations topic.
Summary
OpenSSH certificate authentication lets each server trust one CA public key through TrustedUserCAKeys instead of collecting every user public key in authorized_keys. You create a CA on an admin workstation, copy only the CA public key to servers, sign user public keys with principals and a validity window, and the client presents the resulting *-cert.pub automatically beside the private key.
Principals decide which identities a certificate may assert. Short lifetimes reduce exposure; a KRL with RevokedKeys handles early revocation. AuthorizedPrincipalsFile moves role mapping onto the server so names like developers can authorize existing local accounts. Host certificates are a separate trust path for server identity and should use a different CA, with signing done off the SSH server.
The main pitfall is treating OpenSSH certificates like X.509, or copying the CA private key onto the servers that only need the public key. Start with one user CA, one TrustedUserCAKeys drop-in, one signed key, and a journal line that shows ED25519-CERT with your Key ID. Expand expiry, restrictions, revocation, and principals once that path is solid.
References
- sshd_config(5) —
TrustedUserCAKeys,AuthorizedPrincipalsFile,RevokedKeys,HostCertificate - ssh-keygen(1) — CA creation,
-ssigning,-Linspect,-k/-QKRL
Frequently Asked Questions
What is an SSH certificate authority?
An SSH CA is an OpenSSH key pair whose private key signs user or host certificates and whose public key is trusted by servers (TrustedUserCAKeys) or clients (@cert-authority). It is not a separate daemon; it is a carefully protected ssh-keygen key used for signing.
Is an SSH certificate the same as an SSL certificate?
No. OpenSSH certificates use OpenSSH's own format. They are not X.509, are not used by HTTPS browsers, and are created with ssh-keygen, not with a typical TLS CSR workflow.
Does SSH certificate authentication replace authorized_keys?
It can replace per-user public-key distribution at scale. Servers still authenticate public-key material; the trust anchor becomes the CA instead of each user's line in authorized_keys. Enabling TrustedUserCAKeys does not disable authorized_keys by itself. Small single-host setups often keep plain authorized_keys.
How long should SSH certificates be valid?
As short as your operations can support. Hours to a day is a common starting point for interactive user access. A short-lived certificate reduces the window in which a stolen key plus its currently issued certificate can be used. Use a KRL when you must revoke before expiry.
What is TrustedUserCAKeys?
It is an sshd setting that points at a file of CA public keys. Certificates signed by those CAs may be accepted for user authentication, subject to principals, validity, revocation, and other sshd policy.
What are SSH certificate principals?
Principals are names embedded in the certificate with ssh-keygen -n. They declare which identities the certificate may assert. Without AuthorizedPrincipalsFile, they usually must match the login username; with that file, the server maps principals to local accounts. A certificate does not create Linux users; the account must already exist.
Can one certificate authenticate to multiple Linux usernames?
Yes, if you list multiple principals and the server accepts them, or if several accounts' AuthorizedPrincipalsFile entries share a common principal string such as developers. The same principal can be authorized for multiple local accounts by listing it in each account's principals file.
How do I revoke an SSH certificate?
Prefer letting short-lived certificates expire. For immediate revocation, create a KRL with ssh-keygen -k (add -u only when updating an existing KRL), configure RevokedKeys in its own drop-in, reload sshd, and confirm with ssh-keygen -Q.
Can I use the same SSH CA for users and hosts?
You can technically sign both types with one key, but you should not. Keep the two CA keys separate. If the user CA is compromised, an attacker should not also gain the ability to create trusted host certificates.
Do RHEL, Ubuntu and Debian support OpenSSH certificates?
Yes, on modern OpenSSH packages. The feature is part of OpenSSH itself. Check sshd -V and sshd -T on the host you manage; service unit names may differ (sshd versus ssh).

