SSH key passphrases: why you want one and how to live with it

A passphrase encrypts your private key file, so a copy of it is useless on its own. With ssh-agent you type it once per session.

updated

Quick answer. Set a passphrase when ssh-keygen asks for one, or add it later with ssh-keygen -p. Load the key into ssh-agent so you type it once per session. A forgotten passphrase cannot be recovered; make a new key.

ssh-keygen -p -f ~/.ssh/id_ed25519
ssh-add ~/.ssh/id_ed25519

Why a passphrase matters

Without a passphrase, the file is the credential. Anyone with a copy, from a stolen laptop, a backup or a malicious dependency that reads ~/.ssh, can log in to every server that trusts it.

With one, an attacker must crack the passphrase offline first, which a good passphrase and OpenSSH's slow key derivation make impractical. That gives you time to notice and remove the public key from your servers and accounts.

The passphrase is not a password for the server. It never leaves your computer and the server cannot tell whether your key has one. It only decides whether the private key file is stored in plain form or encrypted.

What it protects against, and what not

SituationDoes a passphrase help?
Laptop stolen or lost, disk not encryptedYes. The thief has an encrypted file.
Key file copied from a backup, sync folder or old diskYes.
A malicious package or script reads ~/.ssh and uploads itYes, if the passphrase is strong enough to resist offline guessing.
Malware running as you while the key is unlocked in the agentNo. It can ask the agent to sign for it.
Keylogger on your machineNo. It captures the passphrase as you type.
Someone using your unlocked, logged-in computerNo, while the agent holds the key.

A passphrase protects the key at rest. It does not help if malware runs as you while the key is unlocked in the agent. For that threat, a hardware-backed ed25519-sk key that needs a physical touch is the next step.

How OpenSSH encrypts the key

Keys written by any current ssh-keygen use the openssh-key-v1 format. When you set a passphrase, OpenSSH:

  1. draws a random 16-byte salt;
  2. runs bcrypt_pbkdf over the passphrase and salt, 16 rounds by default, to derive a key and an IV;
  3. encrypts the private section of the file with aes256-ctr.

The public key and the comment stay readable, which is why ssh-keygen -l can show the fingerprint of an encrypted key without asking for the passphrase. The file always begins with -----BEGIN OPENSSH PRIVATE KEY-----, encrypted or not.

The sshkeygen.dev generator uses the same format, cipher and 16 rounds, so a key generated in your browser behaves exactly like one from ssh-keygen.

KDF rounds: ssh-keygen -a

bcrypt is slow on purpose. Each guess an attacker makes costs the same work as each unlock you do. Raise the cost with -a, when creating a key or with -p on an existing one:

ssh-keygen -t ed25519 -a 100 -C "[email protected]"
ssh-keygen -p -a 100 -f ~/.ssh/id_ed25519

The cost grows linearly with the rounds. On a current desktop, decrypting a key took about 0.12 seconds at the default 16 rounds and about 0.5 seconds at 100. With an agent you pay that once per session, so 64 to 100 rounds is a painless upgrade. Rounds do not rescue a weak passphrase, though; they multiply the attacker's cost by a constant.

Choosing a good passphrase

Length beats complexity. Four to six random words, such as pelican-oxygen-marble-sofa-cobalt, are easy to type and hard to guess. Pick them with a password manager or dice, and store the passphrase there: it cannot be recovered.

  • Do not reuse your login, email or Wi-Fi password. A leak of one should not unlock the others.
  • Do not build it from facts about you, song lyrics or quotes; attackers try those first.
  • ssh-keygen rejects anything shorter than five characters. Aim for at least 20.

ssh-agent: type it once

ssh-agent holds decrypted keys in memory. The file stays encrypted and you type the passphrase once per session.

eval "$(ssh-agent -s)"            # start an agent if none is running
ssh-add ~/.ssh/id_ed25519        # unlock and add the key
ssh-add -l                       # list loaded keys
ssh-add -t 4h ~/.ssh/id_ed25519  # add with a four-hour lifetime
ssh-add -d ~/.ssh/id_ed25519     # remove one key
ssh-add -D                       # remove all keys

Adding a key looks like this:

$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/you/.ssh/id_ed25519:
Identity added: /home/you/.ssh/id_ed25519 ([email protected])

To have keys added on first use, put AddKeysToAgent yes in ~/.ssh/config. Each platform also has a way to keep the passphrase across reboots:

  • macOS: ssh-add --apple-use-keychain ~/.ssh/id_ed25519 plus UseKeychain yes in the config stores it in the Keychain. See the macOS guide.
  • Windows: the ssh-agent service keeps added keys, protected by your Windows account, until you remove them. See the Windows guide.
  • Linux desktops: GNOME Keyring and KDE Wallet can unlock keys at login. On servers and minimal systems, use ssh-agent per session. See the Linux guide.

Be careful with agent forwarding (ssh -A or ForwardAgent yes). Anyone with root on the remote host can use your forwarded agent to log in elsewhere as you while you are connected. Prefer ProxyJump (ssh -J) to reach hosts behind a bastion.

Agent timeouts and confirmation

An unlocked key in the agent is the weak spot from the table above. Three options shrink it:

  • A lifetime per key. ssh-add -t 1h removes the key after an hour. In ~/.ssh/config, AddKeysToAgent 1h does the same for keys added on first use.
  • A default lifetime for the agent. ssh-agent -t 8h applies to every key added without its own -t.
  • Confirm each use. ssh-add -c makes the agent ask for confirmation every time the key signs, through an askpass dialog. Software that tries to use the key behind your back shows up immediately. It needs a graphical askpass program, so it suits desktops.

ssh-add -x locks the agent with a password until ssh-add -X unlocks it, which is useful before leaving a session running.

Add, change or remove a passphrase

ssh-keygen -p re-encrypts an existing key. The key itself, its public half and its fingerprint do not change, so nothing on your servers needs updating.

$ ssh-keygen -p -f ~/.ssh/id_ed25519
Enter old passphrase:
Key has comment '[email protected]'
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.

It asks for the old passphrase (only if there is one), then the new one twice. An empty new passphrase removes it. For scripts (both end up in your shell history):

ssh-keygen -p -f ~/.ssh/id_ed25519 -P "old passphrase" -N "new passphrase"

On a legacy PEM key (-----BEGIN RSA PRIVATE KEY-----), -p also rewrites it in the stronger openssh-key-v1 format. Old PEM keys use a single MD5 pass, which is easy to brute-force.

A key already loaded in the agent stays loaded with no change. Copies of the key elsewhere, such as in a backup, keep the old passphrase.

Check whether a key has a passphrase

$ ssh-keygen -y -P "" -f ~/.ssh/id_ed25519
Load key "/home/you/.ssh/id_ed25519": incorrect passphrase supplied to decrypt private key

That answer means the key is encrypted. An unencrypted key prints its public key line instead. Legacy PEM files show it in the header: Proc-Type: 4,ENCRYPTED means a passphrase is set.

Forgot the passphrase?

There is no reset. If the key is still loaded in a running agent you can keep using it for now, but you cannot export it from there. Plan the replacement while you still have access:

  1. Generate a new key with ssh-keygen -t ed25519 and a passphrase you store in a password manager.
  2. Install the new public key on every server and git host (servers, GitHub and others).
  3. Remove the old public key from those places and delete the old files.

Trying to brute-force your own passphrase is only realistic if you remember most of it, and even then it is slow by design.

Keys without a passphrase for automation

Cron jobs, CI pipelines and backup scripts cannot type a passphrase. Give them their own key with an empty passphrase and make that key worth little to an attacker:

ssh-keygen -t ed25519 -N "" -f ~/.ssh/backup_key -C "backup@db-01"
  • One key per job and per machine, so one leak does not expose the rest.
  • On the server, restrict it in authorized_keys with restrict, command= and from=.
  • On git hosts, use a read-only deploy key for a single repository.
  • Store CI keys in the platform's secret store, never in the repository.

Troubleshooting

ssh asks "Enter passphrase for key" on every connection

No agent holds the key. Load it with ssh-add, or add AddKeysToAgent yes to ~/.ssh/config. If ssh-add answers "Could not open a connection to your authentication agent", start one with eval "$(ssh-agent -s)".

"incorrect passphrase supplied to decrypt private key"

The passphrase is wrong, or the keyboard layout differs from the one you used when setting it. Check Caps Lock and the input language. If you never set a passphrase, the file may belong to someone else or be damaged.

"Load key ... error in libcrypto"

Usually not the passphrase at all: the key file was damaged when copied, most often by Windows line endings or a missing final newline after pasting it into an editor. Copy the file again as a file, not through the clipboard, or make sure it ends with a newline and uses LF line endings.

"sign_and_send_pubkey: signing failed ... agent refused operation"

The agent lists the key but cannot use it, for example because the file it came from changed or, for a hardware key, because the token was not touched. Run ssh-add -D and add the key again.

FAQ

I forgot my SSH key passphrase. Can I recover it?

No. The passphrase is the only way to decrypt the key, and that is the point. Generate a new key, install its public half wherever the old one was, and remove the old public key from those places.

Does changing the passphrase change the key?

No. ssh-keygen -p re-encrypts the same key under a new passphrase. The public key and its fingerprint stay identical, so nothing needs updating on servers or GitHub.

Is a key without a passphrase ever acceptable?

For automation such as CI jobs, backups or deploy hooks, yes, because nobody is there to type it. Limit the damage instead: one key per job, a deploy key with read-only access, or command= and from= restrictions in authorized_keys.

How do I check whether a key has a passphrase?

Run ssh-keygen -y -P "" -f ~/.ssh/id_ed25519. If it prints the public key, the file is not encrypted. If it complains about an incorrect passphrase, it is.

Is the passphrase sent to the server?

No. It never leaves your computer. It only decrypts the private key file locally; the server sees a signature made with the key and nothing else.

What is the minimum passphrase length?

ssh-keygen refuses passphrases shorter than five characters, with "passphrase is too short (minimum five characters)". That is a floor, not a recommendation: aim for several random words.

Does a passphrase slow down every SSH connection?

Only when the key is decrypted, which takes a fraction of a second with the default settings. With ssh-agent that happens once per session, and connections after that are as fast as with an unencrypted key.