Your SSH Key Is Probably Showing Its Age
If you generated your SSH key more than five years ago and haven’t touched it since, there’s a real chance it’s RSA-2048 or smaller. That’s not great. It might still work, OpenSSH will happily connect with it, but “still works” and “is a good security choice” stopped being the same thing a while back.
The answer is Ed25519. It’s smaller, faster, and doesn’t carry RSA’s baggage. Here’s why, and how to actually switch.
RSA: The Arms Race You Can’t Win
RSA was the SSH key type for decades. It’s based on the difficulty of factoring large prime numbers: the bigger your key, the harder it is to crack, in theory.
In practice, this created a key size arms race. RSA-512 was broken in 1999. RSA-1024 is considered broken today; the computational cost to factor it is within reach of well-resourced attackers. RSA-2048 is still technically acceptable, but NIST’s formal deprecation guidance recommends transitioning away from it by 2030, and it’s already disallowed in some compliance frameworks.
RSA-4096 buys you more margin, but at the cost of noticeably slower operations and larger key material. You’re not solving the problem, you’re kicking it down the road.
There’s also an implementation issue. RSA key generation needs a good source of randomness. Poor entropy, common on freshly provisioned VMs or embedded systems, can produce weak keys that look fine on the surface.
DSA: Don’t. Just Don’t
DSA (Digital Signature Algorithm) is deprecated everywhere that matters. OpenSSH disabled DSA key support by default back in OpenSSH 7.0 (2015). Most modern distros won’t even generate DSA keys anymore. If you have one, rotate it immediately. There’s no nuance here.
ECDSA: Better, But Baggage
ECDSA improved on RSA in the obvious ways: smaller keys, faster operations. But it uses NIST P-256 or P-384 curves, and those curves have a trust problem. They were designed with NSA input in the early 2000s, and while no concrete backdoor has been proven, the cryptographic community has been uneasy about them since the Dual_EC_DRBG revelations. “Probably fine” is a phrase you’d rather not apply to your authentication keys.
ECDSA also shares RSA’s weak spot: it needs high-quality randomness at signing time, not just at generation time. A bad random number generator during signing can leak your private key. This has happened in the wild, on real hardware.
Ed25519: Why It Wins
Ed25519 is based on the Curve25519 elliptic curve, designed by Daniel Bernstein with explicit, auditable parameters and no NIST involvement. The design choices are documented and reviewable, not handed down from a government agency.
Here’s why it’s the right default:
- Small key size, high security. A 256-bit Ed25519 key gives you roughly the security of RSA-3072. Your
authorized_keysfile stops looking like a paragraph. - Fast. Ed25519 signing and verification beat RSA at equivalent security levels.
- No randomness dependency during signing. Ed25519 uses deterministic signing. A bad RNG during key generation is still bad, but a bad RNG during signing won’t leak your private key, a real advantage on embedded systems and early-boot environments.
- The OpenSSH default since 8.0 (2019). Every actively maintained Linux distro ships a version well past this point.
Generating an Ed25519 Key
One command, on Linux, macOS, or WSL:
The -C flag is just a comment: use your email, hostname, or whatever helps identify the key later. It shows up in authorized_keys and makes auditing easier down the line.
You’ll be prompted for a save location (accept the default ~/.ssh/id_ed25519 unless you have a reason not to) and a passphrase.
Generating public/private ed25519 key pair.Enter file in which to save the key (/home/you/.ssh/id_ed25519):Enter passphrase (empty for no passphrase):Enter same passphrase again:Your identification has been saved in /home/you/.ssh/id_ed25519Your public key has been saved in /home/you/.ssh/id_ed25519.pubPassphrase: Not Optional, Actually
When ssh-keygen asks for a passphrase, use one. Here’s what it actually protects: if your private key file is ever exfiltrated, laptop stolen, backup misconfigured, S3 bucket left public for three hours, the attacker can’t use it without the passphrase.
Your private key without a passphrase is a credential sitting in plain text. With a passphrase, it’s encrypted at rest using AES-256-CTR (OpenSSH’s key format). The passphrase never leaves your machine; it’s only used locally to decrypt the key in memory.
“But I’ll have to type it every time.” No, you won’t. That’s what ssh-agent is for.
ssh-agent: Type Your Passphrase Once
ssh-agent holds your decrypted key in memory for the rest of your session. Add this to your ~/.bash_profile or ~/.zshrc:
if [ -z "$SSH_AUTH_SOCK" ]; then eval "$(ssh-agent -s)"fiThen add your key:
ssh-add ~/.ssh/id_ed25519Type your passphrase once, and every SSH connection for the rest of the session uses the in-memory key. Close the session, the key is gone from memory.
macOS Keychain handles this automatically. On Linux, keychain (the tool, not the Apple thing) gives you persistent agent management across terminal sessions:
sudo apt install keychain
# add to ~/.bash_profile or ~/.zshrceval "$(keychain --eval --quiet id_ed25519)"~/.ssh/config: Stop Typing Flags
The config file sets per-host defaults so you’re not typing -i ~/.ssh/special_key -p 2222 -J bastion.example.com every single time.
# Default settings for all hostsHost * AddKeysToAgent yes IdentityFile ~/.ssh/id_ed25519
# Jump through a bastion hostHost internal-server HostName 10.10.0.50 User deploy ProxyJump bastion.example.com
# Non-standard port, different keyHost old-server HostName legacy.example.com Port 2222 IdentityFile ~/.ssh/id_ed25519_legacy User ubuntu
# Port forward shortcutHost db-tunnel HostName db.internal.example.com LocalForward 5432 localhost:5432 User adminPermissions matter here, and SSH will silently fail or warn loudly if they’re wrong:
chmod 700 ~/.sshchmod 600 ~/.ssh/configchmod 600 ~/.ssh/id_ed25519chmod 644 ~/.ssh/id_ed25519.pubDeploying Your Public Key
The easy way:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@hostThis appends your public key to ~/.ssh/authorized_keys on the remote host and sets permissions correctly for you.
If ssh-copy-id isn’t available, the manual method works the same way:
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"The permissions aren’t optional. sshd refuses to use authorized_keys if it’s world-writable. Remote directory: 700. Key file: 600. This trips people up constantly, and the error messages don’t exactly point you at the fix.
FIDO2 / Hardware Keys: The Next Level
If you want the private key to never exist on disk at all, hardware security keys are the answer. YubiKey, SoloKey, and others support FIDO2/U2F, and OpenSSH has supported them natively since 8.2.
# Resident key, stored on the hardware key itselfssh-keygen -t ed25519-sk -O resident -C "yubikey"
# Non-resident: a private key handle lives on disk, the crypto lives on hardwaressh-keygen -t ed25519-sk -C "yubikey"The -sk suffix means “security key.” The private key material never leaves the hardware device. Even if your machine is fully compromised, the SSH key can’t be stolen without physical access to the token.
ecdsa-sk is the ECDSA variant; prefer ed25519-sk for the same reasons you’d prefer Ed25519 in general.
You can also require a physical touch per authentication:
ssh-keygen -t ed25519-sk -O verify-required -C "yubikey-tap"For homelab use this is optional. For production servers or anything client-facing, it’s worth the extra button press.
Auditing Your Existing Keys
Check what you’re already carrying around:
# Check one key's type and sizessh-keygen -l -f ~/.ssh/id_rsa
# Check every key in your .ssh directoryfor key in ~/.ssh/id_*; do [[ "$key" == *.pub ]] && continue echo -n "$key: " ssh-keygen -l -f "$key" 2>/dev/null || echo "not a key file"doneOutput from ssh-keygen -l looks like this:
2048 SHA256:abc123... [email protected] (RSA)256 SHA256:xyz789... [email protected] (ED25519)The number at the start is the key size. (RSA) under 3072 bits is a rotation candidate. (DSA) is an immediate rotation, no exceptions.
For servers you manage, check what’s actually sitting in authorized_keys:
ssh-keygen -l -f ~/.ssh/authorized_keysThis lists every authorized key with its type. Any RSA-1024 entries should alarm you. RSA-2048 entries should prompt a migration plan, not panic, just a plan.
Key Type Comparison
| Algorithm | Key Size | Security | Status | Verdict |
|---|---|---|---|---|
| DSA-1024 | 1024-bit | Broken | Deprecated everywhere | Rotate immediately |
| RSA-1024 | 1024-bit | Broken | Disabled in modern OpenSSH | Rotate immediately |
| RSA-2048 | 2048-bit | Marginal | NIST deprecation pending | Rotate soon |
| RSA-4096 | 4096-bit | OK | Still accepted | Acceptable, but why bother |
| ECDSA-256 | 256-bit | Good | Supported | NIST curve concerns |
| Ed25519 | 256-bit | Excellent | OpenSSH default | Use this |
| ed25519-sk | 256-bit | Excellent, plus physical presence | Hardware required | Use for high-value access |
The One-Time Cleanup
Most people have one SSH key they generated years ago and have been carrying everywhere since. It’s probably RSA-2048, probably has no passphrase “because it’s inconvenient,” and has accumulated access to a dozen servers, cloud accounts, and GitHub over the years.
The rotation process: generate a fresh Ed25519 key with a passphrase, set up ssh-agent so you only type that passphrase once per session, deploy the new key everywhere, verify it works, then remove the old key from every authorized_keys file it’s sitting in. Don’t skip the verify step; you don’t want to find out your new key doesn’t work at the same moment you delete the old one.
This takes an afternoon and you won’t have to think about it again for years. Your 2 AM self, locked out of a server because someone finally deprecated your old RSA key on the far end, will wish you’d done this sooner.