How SSH Certificates Work

Traditional SSH authentication revolves around raw public keys. You generate a key pair, copy the public half into ~/.ssh/authorized_keys on each target host, and manage trust through file distribution. This works for small environments. It collapses at scale. SSH certificates solve this by introducing a Certificate Authority (CA) model that is entirely separate from the X.509 PKI used by TLS. OpenSSH certificates are simpler, more flexible, and purpose-built for the SSH ecosystem — and they have been available since OpenSSH 5.4 (2010), yet remain underused in most organizations.

The Problem with Raw Public Keys

With raw public key authentication, every trust relationship is a point-to-point binding: this specific public key is authorized to access this specific account on this specific host. The result is an O(n×m) management problem — n users times m servers. Every time you add a server, you must distribute authorized keys to it. Every time an employee leaves, you must remove their key from every server. Every time a key is rotated, the same distribution happens again.

Host key verification has the same problem in reverse. The first time a user connects to a new server, they see the The authenticity of host ... can't be established prompt. Most people type "yes" without verifying the fingerprint — training users to blindly accept TOFU (trust on first use) is an invitation for man-in-the-middle attacks. Even organizations that verify fingerprints out-of-band must maintain a known_hosts registry and update it every time a host key rotates.

SSH certificates eliminate both problems by introducing a single trust anchor: the CA key.

Two Types of SSH Certificates

OpenSSH supports two distinct certificate types, each solving a different half of the trust equation:

Certificate Authority ca_key / ca_key.pub User Certificate id_ed25519-cert.pub Host Certificate ssh_host_ed25519-cert.pub signs signs Server (sshd) TrustedUserCAKeys ca_key.pub Client (ssh) @cert-authority in known_hosts presented to presented to

User certificates authenticate users to servers. Instead of listing every user's public key in authorized_keys, the server trusts a single CA public key. Any user who presents a certificate signed by that CA is accepted (subject to principal matching). This flips the model from O(n×m) to O(1) configuration per server.

Host certificates authenticate servers to clients. Instead of maintaining a known_hosts file with every server's fingerprint, the client trusts the CA and verifies that the server's host certificate is signed by it. No more TOFU prompts. No more stale known_hosts entries when servers are reprovisioned.

The OpenSSH Certificate Format

OpenSSH certificates are not X.509. They use a custom binary format defined by OpenSSH, which is intentionally simpler than the ASN.1/DER encoding of X.509. You can inspect a certificate with ssh-keygen -L:

$ ssh-keygen -L -f id_ed25519-cert.pub
id_ed25519-cert.pub:
        Type: [email protected] user certificate
        Public key: ED25519-CERT SHA256:xK3...
        Signing CA: ED25519 SHA256:mR7... (using ssh-ed25519)
        Key ID: "[email protected]"
        Serial: 42
        Valid: from 2026-04-24T00:00:00 to 2026-04-25T00:00:00
        Principals:
                alice
                deploy
        Critical Options: (none)
        Extensions:
                permit-pty
                permit-user-rc
                permit-agent-forwarding
                permit-port-forwarding

The certificate is a single line in base64, appended with the certificate type identifier. Its wire format contains these fields:

This simplicity is a feature. X.509 certificates have extension mechanisms so complex that entire systems had to be built to audit how CAs use them. OpenSSH's format covers 95% of use cases with a fraction of the complexity.

Setting Up a Certificate Authority

An SSH CA is just a key pair. There is no enrollment ceremony, no CSR process, no certificate chain. You generate a key, protect it, and start signing.

Creating the CA Key

# Generate the CA key pair (protect this with a strong passphrase)
ssh-keygen -t ed25519 -f ca_key -C "SSH CA for example.com"

# This produces:
#   ca_key       (private key — guard this carefully)
#   ca_key.pub   (public key — distribute freely)

In practice, you want separate CA keys for user certificates and host certificates. This limits blast radius: if the user CA key is compromised, attackers can forge user identities but cannot impersonate hosts (and vice versa).

Signing Host Certificates

# Sign the host's public key
ssh-keygen -s ca_key \
  -I "web-01.example.com-20260424" \
  -h \
  -n web-01.example.com,web-01,10.0.1.5 \
  -V +52w \
  /etc/ssh/ssh_host_ed25519_key.pub

# This creates: /etc/ssh/ssh_host_ed25519_key-cert.pub

The -h flag marks this as a host certificate. The -n flag lists the hostnames the certificate is valid for — clients will reject the certificate if the hostname they connected to is not in this list. -V +52w sets the validity to 52 weeks.

Configure sshd to present the certificate:

# /etc/ssh/sshd_config
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

Configure clients to trust the CA:

# ~/.ssh/known_hosts (or system-wide /etc/ssh/ssh_known_hosts)
@cert-authority *.example.com ssh-ed25519 AAAA...CA_PUBLIC_KEY...

After this, clients connecting to any *.example.com host will verify the host certificate against the CA rather than checking individual fingerprints. No more TOFU.

Signing User Certificates

# Sign the user's public key
ssh-keygen -s ca_key \
  -I "[email protected]" \
  -n alice,deploy \
  -V +8h \
  -O no-port-forwarding \
  ~/.ssh/id_ed25519.pub

# This creates: ~/.ssh/id_ed25519-cert.pub

The -n flag lists principals (usernames) this certificate is valid for. The user can authenticate as alice or deploy on any server that trusts this CA, but not as root or any other username.

Configure sshd to trust user certificates:

# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/ca_user_key.pub

That is the entire server-side configuration for user certificate authentication. No authorized_keys management. No key distribution automation. One line in sshd_config.

Principals: The Authorization Layer

Principals are how SSH certificates separate authentication from authorization. The CA certifies that this key belongs to Alice, and the principals list says Alice is allowed to act as these specific usernames.

By default, if a certificate has principals, the user can only authenticate as one of those principals. If the certificate has no principals, it is valid for any username — which is dangerous. Production CAs should always set principals.

Servers can further restrict this with AuthorizedPrincipalsFile:

# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/ca_user_key.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
# /etc/ssh/auth_principals/deploy
deploy
sre-team
release-engineering

This file lists which principals are accepted for a given local account. If Alice's certificate has principal sre-team and the server's auth_principals/deploy file includes sre-team, Alice can log in as deploy. This enables role-based access: instead of naming specific users, you assign group principals like sre-team or oncall and manage membership at the CA level.

Critical Options and Force-Command

Critical options are constraints baked into the certificate that the server must enforce. The most important one is force-command:

# Issue a certificate that can only run backups
ssh-keygen -s ca_key \
  -I "backup-agent-20260424" \
  -n backup \
  -V +1h \
  -O force-command="/usr/local/bin/run-backup" \
  -O no-port-forwarding \
  -O no-pty \
  -O no-agent-forwarding \
  -O no-x11-forwarding \
  backup_key.pub

This certificate can only execute /usr/local/bin/run-backup. Even if the holder tries ssh backup@server /bin/bash, the server will execute the forced command instead. Combined with no-pty (no interactive terminal) and no-port-forwarding, this creates a tightly scoped credential — the backup agent can trigger backups and nothing else.

The source-address critical option restricts which IP addresses can use the certificate:

ssh-keygen -s ca_key \
  -I "alice-vpn-only" \
  -n alice \
  -V +8h \
  -O source-address=10.0.0.0/8,172.16.0.0/12 \
  alice_key.pub

If Alice tries to use this certificate from a public IP address, the server will reject the authentication attempt. This is a defense-in-depth measure for environments where certificates might be exfiltrated from a compromised workstation.

Short-Lived Certificates

The validity window on SSH certificates is their single most transformative feature. With raw public keys, revocation is a distribution problem: you have to push an updated authorized_keys or a Key Revocation List (KRL) to every server. With short-lived certificates, revocation becomes unnecessary — the certificate expires on its own.

A certificate issued with -V +8h is valid for eight hours. After that, it is cryptographically inert. Even if an attacker steals it, they have a shrinking window of opportunity. If the validity is reduced to minutes, the credential is essentially a session token with the properties of a public key.

This transforms SSH credential management from a state synchronization problem into a token issuance problem. The CA does not need to maintain revocation lists. Servers do not need to poll for revocation updates. The clock handles it.

Short-Lived Certificate Issuance Flow Engineer authenticates Identity Provider SSO / MFA Signing Service CA key in HSM Server trusts CA token signed certificate (valid 10m – 8h) SSH with certificate (no authorized_keys needed) Valid Expired — no revocation needed

Netflix BLESS: Pioneering Short-Lived SSH Certificates

Netflix open-sourced BLESS (Bastion's Lambda Ephemeral SSH Service) in 2016, and it became the canonical reference implementation for short-lived SSH certificate issuance. BLESS runs as an AWS Lambda function, which means the CA signing key exists only in memory during the brief Lambda invocation — there is no persistent server to compromise.

The architecture is elegantly simple:

  1. An engineer authenticates to AWS via their corporate IdP (SSO + MFA).
  2. They invoke the BLESS Lambda with their public key and requested username.
  3. BLESS validates the request against IAM policies, generates a certificate with a 5-minute validity window, signs it with the CA key (decrypted from an encrypted blob using KMS), and returns the certificate.
  4. The engineer uses the certificate to SSH into any server that trusts the CA.

Key design decisions in BLESS:

BLESS demonstrated that SSH certificate issuance could be reduced to a stateless function call, and that extremely short validity periods (minutes, not hours or days) are practical for interactive use.

Facebook and Uber: Scaling Certificate Infrastructure

Facebook's SSH certificate system operates at a different scale entirely. With hundreds of thousands of servers and tens of thousands of engineers, the challenges go beyond issuance into realm management and operational segmentation.

Facebook's approach uses a tiered principal model where certificates carry principals that map to infrastructure tiers rather than individual server hostnames. An engineer on the web tier gets a certificate with a web-tier principal; the production database servers accept a db-admin principal. This decouples certificate issuance from host inventory — the CA does not need to know every hostname, and adding new servers requires only that they be assigned to the correct tier.

Uber's Pam Ussh system took a different architectural path, integrating certificate validation directly into the PAM (Pluggable Authentication Module) stack. This allowed them to layer SSH certificate authentication alongside their existing PAM modules for audit logging, session recording, and multi-factor enforcement. Rather than replacing their PAM configuration, they added certificate validation as another PAM module that runs during the authentication phase.

Both systems share common architectural principles with BLESS: centralized issuance, short validity periods, principals-based authorization, and integration with the corporate identity provider. The differences are in how they handle the operational complexity of very large, heterogeneous environments.

Certificate Extensions and Permissions

The extensions field in an SSH certificate controls what capabilities the certificate holder has once authenticated. These are additive permissions — absent by default.

ExtensionEffect when present
permit-ptyAllows requesting a pseudo-terminal (interactive shell)
permit-agent-forwardingAllows forwarding the SSH agent
permit-port-forwardingAllows local, remote, and dynamic port forwarding
permit-X11-forwardingAllows X11 display forwarding
permit-user-rcAllows execution of ~/.ssh/rc
no-touch-requiredDisables the FIDO2 touch requirement for security keys

When you sign a certificate with ssh-keygen, all extensions are enabled by default. To strip them, use -O flags that negate the defaults:

# Minimal certificate: no PTY, no forwarding, no agent
ssh-keygen -s ca_key \
  -I "ci-runner-20260424" \
  -n deploy \
  -V +30m \
  -O clear \
  -O permit-pty \
  deploy_key.pub

The -O clear flag removes all extensions, then -O permit-pty adds back only the pseudo-terminal permission. The resulting certificate can get a shell but cannot forward ports, forward the agent, or execute user rc scripts. This is the principle of least privilege applied at the credential level.

Certificate Revocation

Short-lived certificates minimize the need for revocation, but they do not eliminate it entirely. If a CA key is compromised, or if a certificate needs to be invalidated before its natural expiry (e.g., an employee is terminated mid-day), OpenSSH provides Key Revocation Lists (KRLs).

# Create a KRL revoking a specific certificate by serial number
ssh-keygen -k -f /etc/ssh/revoked_keys -s ca_key.pub -z 42

# Revoke by key ID
ssh-keygen -k -f /etc/ssh/revoked_keys -s ca_key.pub id_ed25519.pub

# Configure sshd to check the KRL
# /etc/ssh/sshd_config
RevokedKeys /etc/ssh/revoked_keys

KRLs are binary files that support efficient lookup by serial number, key ID, or raw key hash. They are far more compact than text-based lists: a KRL with a million revoked serials is only a few megabytes, because serial ranges are stored as bitmap intervals.

The operational challenge with KRLs is distribution — the same problem that certificates were supposed to solve. Every server needs an up-to-date copy of the KRL. Organizations that use KRLs typically distribute them via configuration management (Puppet, Chef, Ansible) or a pull-based sync mechanism. This is why short validity periods are so strongly preferred: a 10-minute certificate eliminates the need for KRL distribution in most scenarios.

Certificate Rotation and Key Ceremony

Rotating the CA key itself is the most disruptive operation in an SSH certificate infrastructure. Every server's TrustedUserCAKeys and every client's @cert-authority entry must be updated. There is no chain-of-trust mechanism in OpenSSH certificates — unlike X.509, there are no intermediate CAs.

The recommended approach for CA rotation is to run both old and new CA keys in parallel during a transition period:

# sshd_config during rotation — trust both CAs
TrustedUserCAKeys /etc/ssh/ca_user_key_old.pub /etc/ssh/ca_user_key_new.pub

# known_hosts during rotation — trust both CAs for hosts
@cert-authority *.example.com ssh-ed25519 AAAA...OLD_CA_KEY...
@cert-authority *.example.com ssh-ed25519 AAAA...NEW_CA_KEY...

Gradually issue new certificates signed by the new CA, and once all outstanding old certificates have expired, remove the old CA key from the trust configuration. If all certificates are short-lived (minutes to hours), this rotation can complete in a single day.

For the CA key itself, production environments should protect it with a hardware security module (HSM) or a cloud KMS. The key never exists in plaintext on disk. Signing operations happen through the HSM API, and access is controlled by IAM policies with multi-party approval for sensitive operations.

Comparison: SSH Certificates vs. mTLS Client Certificates

Both SSH certificates and mTLS client certificates solve the same fundamental problem — mutual authentication — but their designs reflect very different ecosystems.

PropertySSH CertificatesX.509 (mTLS)
FormatOpenSSH binary formatASN.1 DER / PEM
CA hierarchyFlat (no intermediate CAs)Hierarchical (root → intermediate → leaf)
RevocationKRL (binary, compact)CRL / OCSP / Certificate Transparency
Identity bindingPrincipals (username, role)Subject, SAN (DNS, IP, URI)
Typical validityMinutes to hoursDays to years
ConstraintsCritical options, extensionsKey Usage, Extended Key Usage, constraints extensions
Tooling complexityssh-keygen onlyopenssl, cfssl, step-ca, Vault PKI

The flat CA model is both SSH certificates' greatest strength and their main limitation. Simplicity means less operational overhead, but it also means you cannot delegate signing authority to teams or regions via intermediate CAs. Organizations that need hierarchical delegation often layer their own signing service on top (like BLESS) that controls who can request certificates and with what principals.

Practical Deployment: Putting It All Together

A complete SSH certificate deployment typically involves four components: a user CA, a host CA, a signing service, and configuration management.

1. Generate Separate CA Keys

# User CA
ssh-keygen -t ed25519 -f /secure/user_ca -C "User CA"

# Host CA
ssh-keygen -t ed25519 -f /secure/host_ca -C "Host CA"

2. Sign All Host Keys and Configure sshd

# On each server (or via configuration management):
ssh-keygen -s /secure/host_ca \
  -I "$(hostname)-$(date +%Y%m%d)" \
  -h \
  -n "$(hostname),$(hostname -f)" \
  -V +52w \
  /etc/ssh/ssh_host_ed25519_key.pub

# sshd_config additions:
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

3. Distribute CA Trust to Clients

# System-wide known_hosts or per-user:
@cert-authority *.example.com ssh-ed25519 AAAA...HOST_CA_PUB...

4. Build a Signing Service

For manual setups, a script wrapping ssh-keygen -s works. For production environments, use a dedicated service like BLESS, step-ca, Teleport, or HashiCorp Vault's SSH secrets engine. The signing service should:

OpenSSH Certificate Types in the Wire Protocol

At the protocol level, SSH certificates are identified by key type strings with the [email protected] suffix. When a client authenticates with a certificate, it sends a SSH_MSG_USERAUTH_REQUEST with the certificate as the public key and the algorithm set to the certificate type:

# Certificate key types:
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]      # FIDO2 security keys
[email protected]

The server verifies the certificate by: (1) checking the certificate type and version, (2) verifying the CA signature against its configured TrustedUserCAKeys, (3) checking validity times against the current clock, (4) checking that the requested username matches one of the certificate's principals (or the AuthorizedPrincipalsFile), (5) checking the KRL if configured, and (6) enforcing critical options. Only if all checks pass is authentication successful.

Integration with FIDO2 and Hardware Security Keys

OpenSSH 8.2+ supports FIDO2/WebAuthn security keys, and these can be combined with certificates. The security key generates a key pair where the private key never leaves the hardware token. The CA signs the public key into a certificate, and authentication requires both the valid certificate and physical presence (touching the security key).

# Generate a FIDO2-backed key
ssh-keygen -t ed25519-sk -f ~/.ssh/id_ed25519_sk

# Sign it into a certificate
ssh-keygen -s ca_key \
  -I "alice-fido-20260424" \
  -n alice \
  -V +8h \
  ~/.ssh/id_ed25519_sk.pub

This achieves multi-factor authentication at the protocol level: the certificate proves the user's identity was verified by the CA (something the organization knows), and the FIDO2 key proves physical possession (something the user has). The touch requirement ensures that even malware on the user's machine cannot silently use the key — every authentication requires a deliberate physical action.

Common Pitfalls

Several failure modes recur in SSH certificate deployments:

Beyond OpenSSH: Teleport, Vault, and step-ca

Several production-grade systems build on OpenSSH's certificate primitive:

Teleport (by Gravitational) is a full access plane that uses SSH certificates under the hood. It adds session recording, RBAC, per-session MFA, and an audit log. Users authenticate through a web UI or CLI, receive a short-lived certificate, and connect to servers registered with the Teleport cluster. Teleport also handles host certificates automatically when servers join the cluster.

HashiCorp Vault's SSH secrets engine can sign SSH certificates on demand. Engineers authenticate to Vault (using LDAP, OIDC, or other auth backends), request a signed certificate with specific principals, and receive a certificate scoped to their Vault policy. Vault handles CA key storage, access control, and audit logging.

step-ca (by Smallstep) is a lightweight open-source CA that supports both X.509 and SSH certificates. It provides OIDC-based authentication, automatic certificate renewal, and integration with cloud identity providers. It is less opinionated than Teleport and lighter weight than Vault, making it a good fit for organizations that want certificate infrastructure without a full access management platform.

All three systems address the same fundamental gap in OpenSSH: the ssh-keygen -s command is a signing tool, not an issuance service. Production deployments need authentication, authorization, auditing, and automation around that signing operation, and these tools provide it.

Further Reading

SSH certificates build on the fundamentals of SSH key exchange and authentication. The trust model shares principles with TLS certificate validation and mutual TLS, though the format and mechanics are entirely different. For the X.509 side of certificate auditing, see Certificate Transparency. Understanding how man-in-the-middle attacks exploit TOFU makes clear why host certificates matter. Try looking up your own network's SSH infrastructure using the BGP Looking Glass to see how your servers are connected to the internet.

See BGP routing data in real time

Open Looking Glass
More Articles
How TLS/HTTPS Works: Securing the Internet's Traffic
Certificate Transparency: How CT Logs Secure the Web's PKI
How Firewalls Work: Packet Filtering, Stateful Inspection, and Beyond
What is Cross-Site Scripting (XSS)?
What is Cross-Site Request Forgery (CSRF)?
What is Server-Side Request Forgery (SSRF)?