Published: 2026-05-23
How to Generate an SSH Key Safely (2026 Guide)
Generate an SSH key the right way: ed25519 over RSA, a high-entropy passphrase, and proper key hygiene. The complete DevOps guide for 2026.

The command you want is ssh-keygen -t ed25519 -C "[email protected]". When it asks for a passphrase, do not press Enter to skip it — type a strong one. That's the entire safe-SSH-key recipe in one line, and most tutorials get the second half wrong.
Here's why the passphrase matters more than the algorithm: your private key is a file sitting on disk. If a laptop gets stolen, a backup leaks, or malware reads ~/.ssh/, an unencrypted private key is an instant root shell on every server it can reach. The passphrase encrypts that file with the bcrypt KDF, turning a one-second theft into a multi-century brute-force problem. This guide covers the algorithm choice, the key-generation steps, the entropy math behind the passphrase, and the deployment hygiene that keeps your keys from becoming someone else's access.
Why SSH Keys Beat Passwords
Password authentication over SSH is a brute-force target. Every internet-facing server with PasswordAuthentication yes gets hammered by bots trying root:123456 thousands of times per hour. Key-based auth removes that attack surface entirely.
An SSH key pair is asymmetric cryptography: a public key you hand out freely (it goes in ~/.ssh/authorized_keys on the server) and a private key you guard with your life. The server encrypts a challenge with your public key; only your private key can answer it. The private key never crosses the network. There's nothing for a network sniffer to capture and nothing for a server breach to leak — the server only ever stores public keys.
Client Server
│ │
│ 1. "Log me in as alex" │
│ ────────────────────────────────────────►
│ │
│ 2. Random challenge, encrypted │
│ with your PUBLIC key │
◄────────────────────────────────────────│
│ │
│ 3. Decrypt with PRIVATE key │
│ (never leaves this machine), │
│ sign the proof │
│ ────────────────────────────────────────►
│ │
│ 4. Proof valid → access granted │
◄────────────────────────────────────────│
The private key stays pinned to your machine through the entire handshake. ssh-agent (covered below) holds the decrypted key in volatile memory for the session — but even then it never travels over the wire; only signatures do.
Once keys are working, set PasswordAuthentication no in sshd_config. That single change defeats every credential-stuffing bot on the internet. It just works.
Choosing the Algorithm: ed25519 vs RSA vs ECDSA
This is where 90% of outdated tutorials fail you. They still tell you to generate RSA-2048. Don't.
| Algorithm | Key Size | Security Level | Speed | Verdict (2026) |
|---|---|---|---|---|
| ed25519 | 256-bit | ~128-bit | Fastest | ✅ Default choice |
| RSA-4096 | 4096-bit | ~140-bit | Slow | ⚠️ Only for legacy hosts |
| RSA-2048 | 2048-bit | ~112-bit | Slow | ❌ Below modern minimum |
| ECDSA (P-256) | 256-bit | ~128-bit | Fast | ⚠️ NIST-curve trust concerns |
| DSA | 1024-bit | Broken | — | ❌ Removed from OpenSSH |
Ed25519 is the answer for new keys. It's built on the Edwards-curve digital signature algorithm over Curve25519, designed by Daniel J. Bernstein with explicit resistance to the side-channel and bad-randomness failures that have repeatedly broken RSA and ECDSA implementations in the wild. The keys are tiny — a 68-character public key versus RSA-4096's wall of base64.
Reach for RSA-4096 only when you're connecting to an ancient appliance or a CI system that hasn't seen an OpenSSH update since 2014. ECDSA works but relies on NIST P-curves, whose parameter generation has never fully shaken off the post-Dual_EC_DRBG trust questions. When ed25519 is available, there's no reason to pick it.

Step-by-Step: Generate Your Key Pair
1. Run ssh-keygen
ssh-keygen -t ed25519 -C "[email protected]"
The -C flag adds a comment — use an identifier like your email or laptop-2026 so you can tell keys apart later in authorized_keys.
2. Choose a file location
Enter file in which to save the key (~/.ssh/id_ed25519):
Press Enter for the default, or specify a name like ~/.ssh/id_ed25519_github if you keep per-service keys (a good habit — see below).
3. Set a strong passphrase
Enter passphrase (empty for no passphrase):
This is the step nobody should skip. The passphrase encrypts your private key at rest using OpenSSH's bcrypt KDF (bcrypt_pbkdf). The KDF runs a fixed number of expansion rounds — 16 by default — and each round multiplies the cost of every guess an attacker has to make. You can crank that higher at generation time:
# 100 KDF rounds — barely slower for you, far slower to brute-force
ssh-keygen -t ed25519 -a 100 -C "[email protected]"
A higher -a value adds a near-imperceptible delay when you unlock the key but multiplies an attacker's per-guess cost linearly. We'll do the math on this next.
4. Confirm what got created
ls -la ~/.ssh/
# id_ed25519 <- private key (NEVER share, NEVER commit)
# id_ed25519.pub <- public key (safe to distribute)
You now have a key pair. The .pub file is the only half that ever leaves your machine.
The Math: Why Your Passphrase Length Decides Everything
An empty passphrase means your private key file is plaintext gold. Steal it, use it — game over. A passphrase changes the threat model completely: an attacker who copies the file still has to brute-force the encryption.
Passphrase strength is measured in entropy bits. For a passphrase built from random dictionary words:
$$H = W \times \log_2(7776) \approx W \times 12.9$$
Where H = entropy in bits, W = number of words, and 7,776 is the size of the EFF large wordlist (each word contributes ~12.9 bits). For a character-based passphrase the formula is:
$$H = L \times \log_2(R)$$
Where L = length in characters and R = the character pool size.
Now the part that matters: OpenSSH encrypts the private key with bcrypt_pbkdf, a deliberately slow KDF that runs many internal expansion passes per guess (16 rounds by default, more if you used -a). This is not the standard password-table bcrypt you see quoted at ~184,000 h/s. Those extra key-expansion passes drag the attack rate down to under 100 attempts per second on a top-tier RTX 4090 — a glacial crawl next to the ~164 billion/sec that same card manages against raw MD5. Here's what that buys you:
| Passphrase | Entropy | RTX 4090 Crack Time (bcrypt_pbkdf, 16 rounds) |
|---|---|---|
| (empty) | 0 bits | Instant — file is plaintext |
letmein2026 | ~28 bits | Minutes — known dictionary pattern |
| 2 random EFF words | ~26 bits | ~3 days |
| 3 random EFF words | ~39 bits | ~70 years |
| 4 random EFF words | ~52 bits | ~500,000 years |
| 5 random EFF words | ~65 bits | Billions of years |
| 16-char random (full ASCII) | ~105 bits | Heat death of the universe |
Four random words is the comfortable floor. Three already buys decades against a stolen key file; four pushes it into the hundreds-of-thousands-of-years range, and five is the right call for keys guarding production. And "random" is the operative word — correct horse battery staple is famous, which means it's in every cracking dictionary. Your brain is a terrible CSPRNG.
Generate the passphrase, don't invent it. Our Password Generator — runs 100% in your browser, zero data sent to any server — has a passphrase mode that pulls true random words and shows live entropy in bits as you adjust the word count. Set it to 4–5 words and you've cleared the table above.
Avoid tools that use Math.random(). Our Password Generator uses the Web Crypto API (crypto.getRandomValues()), ensuring your entropy source is as secure as your operating system's kernel. A passphrase generated from a predictable PRNG isn't random at all — it just looks that way until someone reverse-engineers the seed.
Want to sanity-check a passphrase you already use? Drop it into the Password Strength Checker to see its entropy and estimated crack time before you commit it to a key that guards production.
Managing Keys: ssh-agent, Permissions, and Fingerprints

A passphrase you type on every connection gets annoying fast, and annoyed engineers disable security. The fix is ssh-agent — it holds your decrypted key in memory for the session, so you authenticate once.
# Start the agent and add your key (prompts for passphrase once)
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# macOS: store the passphrase in Keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Lock down file permissions. OpenSSH refuses to use keys with loose permissions, and for good reason:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 # private key: owner read/write only
chmod 644 ~/.ssh/id_ed25519.pub # public key: world-readable is fine
chmod 600 ~/.ssh/authorized_keys
Verify key fingerprints. Every public key has a SHA-256 fingerprint — your defense against accepting a man-in-the-middle host key:
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# 256 SHA256:abc123...xyz [email protected] (ED25519)
That fingerprint is just a SHA-256 hash of the public key, base64-encoded. If you ever need to compute or compare SHA-256 / SHA-512 digests by hand — verifying a host key out-of-band, checking a downloaded known_hosts entry — the Hash Generator does it client-side via the Web Crypto API, no upload required.
Deploying the Public Key (and the Mistakes to Avoid)
Getting your public key onto a server is one command:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
This appends your public key to the server's ~/.ssh/authorized_keys. From then on, key auth just works. Here are the mistakes that turn good keys into incidents:
- Committing the private key to git. The
id_ed25519file (no.pub) must never touch a repository. Addid_*and*.pemto your global gitignore. A leaked private key is a data breach, not a typo. - Reusing one key everywhere. Generate a separate key per context — one for GitHub, one for production, one for your personal VPS. If one leaks, you rotate one, not your entire identity.
- No rotation policy. Keys don't expire on their own. Rotate them annually and immediately when someone leaves the team or a laptop goes missing.
- Skipping the passphrase "just for CI." Use dedicated deploy keys with restricted scope for automation instead. A passphrase-less key in a build runner is the most-stolen credential type in 2026.
For phishing-resistant, hardware-backed keys, OpenSSH 8.2+ supports FIDO2 security keys with ssh-keygen -t ed25519-sk. The private key material lives on the hardware token and never touches disk — the gold standard for production access.
🛡️ Security Checkpoint — Complete This Step
A passphrase-less or weak SSH key is a single stolen file away from full server compromise. Lock yours down before your next deploy.
- → Generate a 4–5 word passphrase — true-random words via the Web Crypto API to encrypt your private key
- → Check your passphrase entropy — confirm it clears 52+ bits against bcrypt-KDF cracking
- → Verify a host key fingerprint — compute SHA-256 digests client-side to defeat man-in-the-middle attacks
Frequently Asked Questions
What is the best command to generate an SSH key in 2026?
Run ssh-keygen -t ed25519 -C "[email protected]". Ed25519 gives you ~128-bit security in a 256-bit key that's faster and smaller than RSA. When it prompts for a passphrase, type a strong one (4+ random words) — that passphrase encrypts the private key on disk with the bcrypt KDF, so a stolen key file is useless without it. Skip RSA unless you're talking to a legacy host that genuinely can't handle ed25519.
Should I use ed25519 or RSA for SSH keys?
Use ed25519 for anything new. It's immune to the weak-randomness and side-channel failures that have broken RSA and ECDSA implementations repeatedly, it's faster to verify, and its keys are a fraction of the size. RSA-4096 is your only fallback, reserved for legacy systems that lack ed25519 support. Never generate RSA-2048 in 2026 — it sits below the modern security minimum.
Does an SSH key really need a passphrase?
Yes, with one exception. An unencrypted private key is plaintext access — copy the file, own the servers, no math required. A passphrase encrypts it with bcrypt_pbkdf, whose multiple key-expansion passes hold an attacker to under 100 guesses/sec even on a top-tier RTX 4090 — centuries of brute force for a 4-word passphrase. The exception is scoped CI deploy keys, where you use restricted, rotatable keys instead of passphrases. For any key tied to a human, always set a passphrase.
How do I add a passphrase to an existing key without regenerating it?
Run ssh-keygen -p -f ~/.ssh/id_ed25519. It prompts for the old passphrase (leave empty if there was none) and lets you set a new one. This re-encrypts the same key pair in place, so your public key stays valid and you don't have to redeploy it to any server. Use it the moment you realize you created a key with an empty passphrase.
Where are SSH keys stored and how do I back them up?
Keys live in ~/.ssh/ — id_ed25519 (private) and id_ed25519.pub (public). Back up the private key only to encrypted storage: a password manager's secure file vault, an encrypted disk image, or a hardware token. Never put it in cloud sync folders, email, or git. The public key needs no protection — you can regenerate or redistribute it freely since it grants no access on its own.