Loading learning content...
SSH keys are the foundation of secure authentication in modern infrastructure. Unlike passwords—which can be guessed, stolen through phishing, or compromised through server breaches—cryptographic keys provide authentication that scales from a single developer's laptop to global infrastructure with millions of servers.
But this power comes with responsibility. A compromised SSH key is a complete identity theft. Poorly managed keys accumulate across systems, creating audit nightmares and security vulnerabilities. Organizations have discovered thousands of untracked SSH keys granting access to critical systems, each one a potential entry point for attackers.
By the end of this page, you will understand SSH key generation best practices, secure storage strategies, the authorized_keys file in depth, key rotation policies, and enterprise-scale key management approaches. You'll gain the knowledge to establish SSH key practices that balance security with operational efficiency.
SSH supports multiple key types, each with different security properties, performance characteristics, and compatibility considerations.
The Key Pair Concept:
SSH keys come in pairs:
Anyone with the public key can verify that a signature was created by the corresponding private key—but cannot create signatures themselves.
Modern Key Types:
| Type | Algorithm | Key Size | Security | Recommendation |
|---|---|---|---|---|
| ed25519 | EdDSA (Curve25519) | 256 bits | ~128-bit | Preferred for all new keys |
| ecdsa | ECDSA (NIST P-256/384/521) | 256/384/521 bits | 128-256 bit | Acceptable, prefer ed25519 |
| rsa | RSA | 2048-4096+ bits | 112-140+ bit | Acceptable at 3072+ bits |
| dsa | DSA | 1024 bits (fixed) | ~80-bit | Deprecated, do not use |
Generating Keys:
# Ed25519 (recommended)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Creates: ~/.ssh/id_ed25519, ~/.ssh/id_ed25519.pub
# Ed25519 with specific filename
ssh-keygen -t ed25519 -f ~/.ssh/work_key -C "work laptop 2024"
# RSA (when ed25519 not supported)
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# Creates: ~/.ssh/id_rsa, ~/.ssh/id_rsa.pub
# ECDSA
ssh-keygen -t ecdsa -b 384 -C "your_email@example.com"
# Creates: ~/.ssh/id_ecdsa, ~/.ssh/id_ecdsa.pub
# FIDO2/Hardware key (requires security key)
ssh-keygen -t ed25519-sk -C "hardware key 2024"
# Creates: ~/.ssh/id_ed25519_sk, ~/.ssh/id_ed25519_sk.pub
Generation Options:
| Option | Purpose | Example |
|---|---|---|
| -t type | Key type (ed25519, rsa, ecdsa) | -t ed25519 |
| -b bits | Key size for RSA/ECDSA | -b 4096 |
| -f file | Output file path | -f ~/.ssh/mykey |
| -C comment | Key comment (usually email) | -C user@example.com |
| -N passphrase | Set passphrase non-interactively | -N "secret" |
| -o | New OpenSSH format (default now) | Enables bcrypt KDF |
| -a rounds | KDF rounds for passphrase | -a 100 (more = slower brute force) |
Protect private keys with strong passphrases. If the key file is stolen, the passphrase provides an additional security layer. With SSH agent, you only enter the passphrase once per session. For automation, consider alternatives like SSH certificates with short validity rather than passphrase-less keys.
SSH keys can be stored in several formats, with modern OpenSSH using an encrypted format that resists brute-force attacks on passphrases.
Private Key Formats:
| Format | Header | Encryption | Support |
|---|---|---|---|
| OpenSSH (new) | -----BEGIN OPENSSH PRIVATE KEY----- | bcrypt + aes256-ctr | OpenSSH 6.5+ |
| PEM (old) | -----BEGIN RSA PRIVATE KEY----- | AES/3DES, weak KDF | Legacy, all clients |
| PKCS#8 | -----BEGIN PRIVATE KEY----- | Various | Cross-platform, some clients |
| PuTTY (.ppk) | PuTTY-User-Key-File-* | AES | PuTTY (Windows) |
OpenSSH Format Advantages:
The new OpenSSH format uses bcrypt as the key derivation function:
-a rounds)Converting Formats:
# Convert old PEM to new OpenSSH format
ssh-keygen -p -o -f ~/.ssh/id_rsa
# -p: change passphrase (or convert)
# -o: use new format
# Convert to PEM format (for compatibility)
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa
# Convert PuTTY key to OpenSSH (requires puttygen)
puttygen keyfile.ppk -O private-openssh -o id_rsa
# Extract public key from private key
ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
File Permissions:
SSH enforces strict permissions on key files:
# Private keys: owner read/write onlychmod 600 ~/.ssh/id_ed25519chmod 600 ~/.ssh/id_rsa # Public keys: owner read/write, others readchmod 644 ~/.ssh/id_ed25519.pub # .ssh directory: owner onlychmod 700 ~/.ssh # authorized_keys: owner read/write onlychmod 600 ~/.ssh/authorized_keys # SSH will refuse to use keys with incorrect permissions!# Error: "Permissions 0644 for 'id_ed25519' are too open."Never share private keys. Never store private keys in source control, shared drives, or cloud storage without encryption. Never email private keys. Each user should generate their own key pair; sharing private keys defeats non-repudiation and makes revocation impossible.
The authorized_keys file on servers lists public keys allowed to authenticate as that user. Understanding its format and options is essential for secure, flexible access control.
File Location:
# Default location:
~/.ssh/authorized_keys
# Can be configured in sshd_config:
AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
# Centralized location:
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
# %u expands to username
Basic Format:
[options] key-type base64-key comment
Example File:
# Simple entries (no options)
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyz... alice@laptop
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCz... bob@desktop
# With options
command="/usr/local/bin/backup.sh" ssh-ed25519 AAAA... backup@server
from="192.168.1.*,10.0.0.0/8" ssh-ed25519 AAAA... admin@internal
Available Options:
| Option | Purpose | Example |
|---|---|---|
| command="cmd" | Force specific command execution | command="/bin/rsync --server" |
| from="hosts" | Restrict source IPs | from="192.168.1.*,!192.168.1.5" |
| environment="VAR=val" | Set environment variable | environment="GIT_AUTHOR=" |
| no-port-forwarding | Disable port forwarding | no-port-forwarding |
| no-X11-forwarding | Disable X11 forwarding | no-X11-forwarding |
| no-agent-forwarding | Disable agent forwarding | no-agent-forwarding |
| no-pty | Disable terminal allocation | no-pty (for automated commands) |
| permitopen="host:port" | Limit port forwarding destinations | permitopen="db:5432" |
| principals="list" | For certificates: required principals | principals="admin,deploy" |
| restrict | Enable all restrictions at once | restrict (then selectively permit) |
| expiry-time="time" | Key expiration date | expiry-time="20251231" |
Practical Examples:
# Backup key: only runs backup, no shell, no forwarding
command="/usr/local/bin/backup.sh",no-port-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAA... backup-agent
# Restricted key with specific port forward allowed
restrict,permitopen="database:5432" ssh-ed25519 AAAA... db-access-user
# Time-limited access key
expiry-time="20240630",from="10.0.0.0/8" ssh-ed25519 AAAA... contractor
# Git server: only git commands
command="/usr/bin/git-shell -c \"$SSH_ORIGINAL_COMMAND\"",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAA... git-user
# Multiple restrictions combined
from="192.168.1.0/24",command="df -h",no-pty ssh-ed25519 AAAA... monitoring-agent
The 'restrict' keyword enables all restrictions at once: no forwarding, no PTY, no agent forwarding, no X11. Then selectively permit what's needed: restrict,pty,permit-port-forwarding. This is safer than remembering to add each individual restriction.
Getting public keys to servers—and keeping them current—is one of the most challenging aspects of SSH key management at scale.
Manual Distribution:
# Copy public key to server (ssh-copy-id)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# Appends to ~/.ssh/authorized_keys on server
# Manual method (when ssh-copy-id unavailable)
cat ~/.ssh/id_ed25519.pub | ssh user@server \
'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
# Copy to multiple servers
for server in server1 server2 server3; do
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@$server
done
Configuration Management:
Configuration management tools automate key distribution:
12345678910111213141516171819
# Ansible playbook for SSH key management- name: Manage SSH authorized keys hosts: all tasks: - name: Add developer SSH keys authorized_key: user: "{{ item.user }}" state: present key: "{{ item.key }}" loop: - { user: 'deploy', key: 'ssh-ed25519 AAAA... alice@company' } - { user: 'deploy', key: 'ssh-ed25519 AAAA... bob@company' } - { user: 'admin', key: 'ssh-ed25519 AAAA... ops-team' } - name: Remove former employee keys authorized_key: user: deploy state: absent key: "ssh-ed25519 AAAA... former-employee@company"LDAP/Directory Integration:
PublicKeys can be stored in LDAP directories:
# sshd_config:
AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys
AuthorizedKeysCommandUser nobody
# Fetches keys from LDAP/AD via SSSD
# User's public keys stored as sshPublicKey attribute
GitHub/GitLab Keys:
Public services expose user keys:
# Fetch keys from GitHub
curl https://github.com/username.keys
# Use in authorized_keys via AuthorizedKeysCommand
#!/bin/bash
curl -s "https://github.com/$1.keys"
# sshd_config:
AuthorizedKeysCommand /usr/local/bin/fetch-github-keys %u
AuthorizedKeysCommandUser nobody
Centralized key management (LDAP, certificates) simplifies administration and revocation. Distributed management (authorized_keys files) is simpler initially but becomes unmanageable at scale. Plan for growth: start with certificates for new infrastructure, even if migrating existing systems takes time.
SSH certificates fundamentally solve the key distribution problem. Instead of distributing individual public keys, you distribute a CA public key once, then issue signed certificates to users.
Certificate-Based Infrastructure:
Creating a User CA:
# Generate CA key pair (protect the private key!)
ssh-keygen -t ed25519 -f /secure/path/user_ca -C "SSH User CA"
# No passphrase for automated issuance (secure the private key physically)
# Or with passphrase for manual issuance
Issuing User Certificates:
# Sign a user's public key
ssh-keygen -s /secure/path/user_ca \
-I "alice@example.com" \
-n alice,ops-team \
-V +1d \
~/.ssh/id_ed25519.pub
# Options:
# -s: CA private key for signing
# -I: Certificate identity (for audit logs)
# -n: Principals (usernames user can authenticate as)
# -V: Validity period (+1d = one day from now)
#
# Output: ~/.ssh/id_ed25519-cert.pub
Server Configuration:
# /etc/ssh/sshd_config
# Trust certificates signed by this CA
TrustedUserCAKeys /etc/ssh/user_ca.pub
# Optional: restrict to specific principals
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
# File contents: one principal per line
# /etc/ssh/auth_principals/deploy:
# ops-team
# ci-pipeline
User Configuration:
# Certificate is automatically used if present alongside the key
# ~/.ssh/id_ed25519
# ~/.ssh/id_ed25519-cert.pub
# SSH client automatically presents both
# Or explicitly specify:
ssh -i ~/.ssh/id_ed25519 user@server
# SSH sends id_ed25519-cert.pub if it exists
Issue certificates with very short validity (hours to days). Users request new certificates regularly. This eliminates revocation complexity—expired certificates are automatically invalid. Combine with SSO: authenticate to SSO → receive fresh SSH certificate → use until expiry → re-authenticate if needed.
Host key certificates solve the "Trust on First Use" problem. Instead of manually verifying host key fingerprints, clients trust certificates signed by a known CA.
Creating a Host CA:
# Generate host CA (separate from user CA)
ssh-keygen -t ed25519 -f /secure/path/host_ca -C "SSH Host CA"
Signing Host Keys:
# On each server, sign the host key
ssh-keygen -s /secure/path/host_ca \
-I "server.example.com" \
-h \
-n server.example.com,server,10.0.1.50 \
-V +52w \
/etc/ssh/ssh_host_ed25519_key.pub
# Options:
# -h: This is a host certificate (not user)
# -n: Hostnames this cert is valid for
#
# Output: /etc/ssh/ssh_host_ed25519_key-cert.pub
Server Configuration:
# /etc/ssh/sshd_config
# Present the host certificate
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
# Can have multiple host keys/certificates
HostKey /etc/ssh/ssh_host_ed25519_key
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
Client Configuration:
# ~/.ssh/known_hosts
# Instead of individual host keys, trust the CA:
@cert-authority *.example.com ssh-ed25519 AAAA...host-ca-public-key...
# All hosts matching *.example.com with valid CA-signed certs are trusted
# No more "authenticity can't be established" prompts!
# Or in /etc/ssh/ssh_known_hosts for system-wide trust
| Challenge | Without Certificates | With Certificates |
|---|---|---|
| New server deployment | Distribute fingerprint, users verify manually | Automatic trust via CA |
| Server replacement/rebuild | Host key changes, users see warnings | Issue new cert, seamless |
| Initial connection | Interactive fingerprint verification | Silent CA validation |
| Audit trail | known_hosts files scattered on clients | Centralized CA signing log |
Compromise of the Host CA allows issuing certificates for any hostname—enabling man-in-the-middle attacks against all clients trusting the CA. Store CA private keys on air-gapped systems or HSMs. Implement strict access controls and audit logging for certificate issuance.
Keys don't last forever. Employees leave, keys get compromised, security policies require periodic rotation. Having robust rotation and revocation processes is essential.
Key Rotation Strategies:
| Approach | Method | Complexity | Best For |
|---|---|---|---|
| Manual removal | Delete from authorized_keys | High (per-server) | Small environments |
| Configuration management | Ansible/Puppet/Chef updates | Medium | Medium environments |
| Short-lived certificates | Certificates expire, no action needed | Low (after setup) | Large environments |
| Revocation list | Explicitly revoke keys/certs | Medium | When cert lifetime > revocation need |
SSH Revoked Keys (Key Revocation List):
# /etc/ssh/sshd_config:
RevokedKeys /etc/ssh/revoked_keys
# The revoked_keys file is a Key Revocation List (KRL)
# Create/update using ssh-keygen:
# Create KRL from public key(s)
ssh-keygen -k -f /etc/ssh/revoked_keys \
compromised_key.pub former_employee.pub
# Add to existing KRL
ssh-keygen -k -u -f /etc/ssh/revoked_keys \
another_key.pub
# Revoke by serial number (for certificates)
ssh-keygen -k -f /etc/ssh/revoked_keys -z 1234
# Revoke by key ID (for certificates)
ssh-keygen -k -f /etc/ssh/revoked_keys \
-s user_ca -I "user@example.com"
Certificate Revocation:
# Revoke a certificate by serial number
ssh-keygen -k -f /etc/ssh/revoked_keys -s user_ca -z 42
# Revoke all certificates with a specific key ID
ssh-keygen -k -f /etc/ssh/revoked_keys -s user_ca \
-I "alice@example.com"
# The KRL must be distributed to all servers
# This is why short-lived certificates are often preferred.
Rotation Best Practices:
If certificates expire in 8 hours, you rarely need active revocation. A compromised credential becomes useless the same day. For immediate revocation needs, combine short-lived certs with CA-revocation: stop issuing new certs and add keys to KRL for the brief remaining validity window.
Large organizations face scale challenges: thousands of users, hundreds of thousands of servers, compliance requirements, and audit demands. Dedicated SSH key management solutions address these needs.
Key Management Challenges at Scale:
Enterprise Solutions:
1. SSH Certificate Authorities (DIY) Build your own CA infrastructure with automation:
# HashiCorp Vault SSH Certificate Signing
vault write ssh/sign/my-role \
public_key=@~/.ssh/id_ed25519.pub
# Returns signed certificate
2. Commercial SSH Key Management
3. Cloud Provider Solutions
4. Bastion-as-a-Service
These solutions provide:
For new infrastructure, implement certificates from the start. For existing environments, focus on high-value targets first: production servers, database hosts, security infrastructure. Gradually migrate less critical systems. The investment in certificate infrastructure pays dividends in reduced operational burden and improved security posture.
We've explored SSH key management comprehensively—from generating individual keys to enterprise-scale certificate infrastructure. Let's consolidate the essential practices:
Module Complete:
With this final page, you've completed the comprehensive SSH module. You now understand SSH from first principles—secure remote access needs, protocol architecture, authentication mechanisms, tunneling capabilities, and key management practices. This knowledge enables you to implement, secure, and manage SSH infrastructure at any scale.
Congratulations! You've mastered SSH—from understanding why it replaced insecure predecessors, through protocol internals and cryptographic foundations, to practical authentication, tunneling, and key management. You're now equipped to design and implement secure SSH infrastructure that balances security requirements with operational efficiency.