🔑 Creating SSH Keys

Create a secure SSH key pair, protect it, and manage it

Video: Creating SSH Keys (sysadmin video)
Prefer to watch? This video runs the guide's main commands on a real Ubuntu 24.04 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube. It is part of the Services, Storage and Security playlist.
Creating SSH Keys: a key pair for secure, passwordless logins
ssh-keygen -t ed25519 -C "you@example.com"

An SSH key is a pair of files that replaces typing a password: a PRIVATE key that stays on your computer, and a PUBLIC key that you install on the servers you want to
log in to. This guide shows how to create good keys, how to protect them with a passphrase and an agent, how to inspect and manage them, and (near the end) how SSH
certificates work. It is the first of two guides. The second one, "Passwordless SSH Login: Copying Keys to Servers", shows how to put the public key on servers and use it.
Note on words: people say "certs" for many things. Here we mean SSH KEYS (and, at the end, SSH certificates signed by your own SSH CA). TLS/X.509 certificates for websites are a different topic.
Everything below is safe to practice on your own computer. Nothing changes on a server until you copy a key there.

The idea in one minute

Private key    Secret. Proves who you are. NEVER share it, email it, paste it in chat, or commit it to git. Stays in ~/.ssh on your computer. Protect it with a passphrase
Public key     Safe to share. It is like a padlock that only your private key can open. You put copies on every server you want to log in to (in ~/.ssh/authorized_keys)
Login          The server sends a challenge, your computer signs it with the private key (the private key never leaves your machine), and the server checks the signature with the public key
Passphrase     A password that encrypts the private key file on YOUR disk. If someone steals the file, they still cannot use it. It is not sent to any server
Agent          A small program (ssh-agent) that holds your unlocked key in memory, so you type the passphrase once per session instead of at every login
Result: you type a passphrase once (or nothing at all with an agent), and logins to any server that has your public key just work.

Choose a key type

ed25519        THE RECOMMENDED CHOICE. Small, fast, modern and very secure. Supported by OpenSSH 6.5 (2014) and newer, and by all current services (GitHub, GitLab, cloud providers)
rsa (4096)     The old standard. Use it only when a system does not support ed25519 (very old servers, some appliances). Use at least 3072 bits, preferably 4096
ecdsa          Elliptic-curve keys (256/384/521). Fine, but ed25519 is simpler and preferred
ed25519-sk / ecdsa-sk    Keys stored on a hardware security key (YubiKey and other FIDO2 keys). The private half never leaves the device. Needs OpenSSH 8.2 or newer on both ends
dsa            OBSOLETE and disabled in modern OpenSSH. Do not use it
Rule of thumb:  ssh-keygen -t ed25519  for everything, unless something refuses it. Then use  -t rsa -b 4096 .
Check what you have:  ssh -V    (OpenSSH version)     ls ~/.ssh    (existing keys)

Create your first key

ssh-keygen -t ed25519 -C "alice@laptop-2026"
It asks three questions:
    Enter file in which to save the key (/home/alice/.ssh/id_ed25519):        Press Enter to accept the default. Or type a custom path (see "Several keys" below)
    Enter passphrase (empty for no passphrase):                               TYPE A PASSPHRASE. Use a long phrase, for example four random words. Empty means anyone who gets the file can use it
    Enter same passphrase again:
Output:
    Your identification has been saved in /home/alice/.ssh/id_ed25519
    Your public key has been saved in /home/alice/.ssh/id_ed25519.pub
    The key fingerprint is:  SHA256:Yx3...k8Q alice@laptop-2026
Two files were created:
    ~/.ssh/id_ed25519        the PRIVATE key   (mode 600: only you can read it)
    ~/.ssh/id_ed25519.pub    the PUBLIC key    (one line of text; safe to share)
Look at the public key (this is what you copy to servers):
cat ~/.ssh/id_ed25519.pub
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB...long text... alice@laptop-2026
    (type)      (the key itself)                             (comment: a label, usually user@host or your email)
Never share the file WITHOUT the .pub ending. If a service asks for your "public key", it wants the .pub file's contents.

Useful ssh-keygen options

-t ed25519            Key type                                          -b 4096       Key size in bits (RSA only; ed25519 has a fixed size)
-C "comment"          A label to identify the key later (which laptop, which purpose, which year). It is only a label
-f ~/.ssh/id_work     File name for the key (the .pub is added for the public half)
-N "passphrase"       Set the passphrase on the command line. Avoid it for real keys: it lands in your shell history and process list. Use the prompt
-N ""                 An EMPTY passphrase (for automation only, and see the safety notes below)
-a 100                Key derivation rounds: more rounds make brute-forcing the passphrase slower. 100 is a good value (the default in current OpenSSH is 16)
-q                    Quiet                                             -v    Verbose
-o                    Use the new OpenSSH private key format (already the default since OpenSSH 7.8; needed only for old RSA keys on very old versions)
-t ed25519-sk         A hardware-backed key (FIDO2)                     -O resident    Store it on the device so it can be re-created on another computer
A stronger version of the standard command:
ssh-keygen -t ed25519 -a 100 -C "alice@laptop-2026" -f ~/.ssh/id_ed25519
Use separate keys for separate jobs: personal servers, work servers, GitHub. If one leaks, you replace one, not all. Name them clearly:
ssh-keygen -t ed25519 -a 100 -C "alice work 2026" -f ~/.ssh/id_work
ssh-keygen -t ed25519 -a 100 -C "alice github 2026" -f ~/.ssh/id_github
ssh-keygen -t ed25519 -a 100 -C "alice homelab 2026" -f ~/.ssh/id_homelab
Tell SSH which key to use for which host in ~/.ssh/config (see the second guide):
    Host github.com
        IdentityFile ~/.ssh/id_github
        IdentitiesOnly yes
    Host *.corp.example.com
        User alice
        IdentityFile ~/.ssh/id_work
        IdentitiesOnly yes
Use a per-machine key (do not copy one private key to all your computers). Each laptop gets its own key, so you can remove one without affecting the others.

File permissions (SSH is strict about them)

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/id_work           # private keys: owner-only. SSH refuses keys that other users can read
chmod 644 ~/.ssh/*.pub                                # public keys may be readable
chmod 600 ~/.ssh/config ~/.ssh/authorized_keys
chmod go-w ~                                          # your home directory must not be writable by group or others
ls -la ~/.ssh                                         # check:  drwx------ for the folder, -rw------- for private keys
Error "WARNING: UNPROTECTED PRIVATE KEY FILE! ... Permissions 0644 for '...' are too open" means run chmod 600 on the private key.

Look at, verify and identify keys

ssh-keygen -l -f ~/.ssh/id_ed25519.pub               # the fingerprint and type:  256 SHA256:Yx3...k8Q alice@laptop-2026 (ED25519)
ssh-keygen -lv -f ~/.ssh/id_ed25519.pub              # plus a "randomart" picture (a visual fingerprint)
ssh-keygen -l -E md5 -f ~/.ssh/id_ed25519.pub        # the older MD5 style fingerprint some services (AWS) show
ssh-keygen -y -f ~/.ssh/id_ed25519                   # regenerate the PUBLIC key from a private key (you lost the .pub file)
ssh-keygen -l -f ~/.ssh/authorized_keys              # fingerprints of every key in a file (on a server: who can log in?)
The fingerprint is a short summary of the key. Compare fingerprints (not the whole key) to confirm two keys are the same, and to check a key listed in GitHub or a cloud console.
Key strength checks:  ssh-keygen -l -f key.pub  shows the bits (RSA below 2048 is weak; DSA is obsolete; ed25519 shows 256, which is correct and strong).

Passphrases: protect the key file

A key without a passphrase is a password written on a sticky note: whoever copies the file can log in as you. Use a passphrase on every personal key.
ssh-keygen -p -f ~/.ssh/id_ed25519                   # change the passphrase (or add one to a key that has none): asks for the old one, then the new one twice
ssh-keygen -p -N "" -f ~/.ssh/id_ed25519             # REMOVE the passphrase (do this only for automation keys, see below)
ssh-keygen -p -a 100 -f ~/.ssh/id_ed25519            # re-encrypt with more rounds
Choose something long and memorable (four or five random words), not a short "clever" password. Store it in a password manager if you like.
"Enter passphrase for key '/home/alice/.ssh/id_ed25519':" at login is your LOCAL prompt to unlock the file. It is NOT the server's password, and nothing is sent to the server.

ssh-agent: type the passphrase once
The agent keeps your unlocked key in memory and answers signing requests, so you are not asked again until it stops (logout, reboot, or a timeout you set).
eval "$(ssh-agent -s)"                               # start an agent for this shell (many desktops and servers start one for you: check with  echo $SSH_AUTH_SOCK )
ssh-add                                              # add the default keys (~/.ssh/id_ed25519 and others)   asks for the passphrase once
ssh-add ~/.ssh/id_work                               # add a specific key
ssh-add -t 4h ~/.ssh/id_work                         # add it for 4 hours only, then it is forgotten (a good habit for sensitive keys)
ssh-add -l                                           # list loaded keys (fingerprints)          ssh-add -L    list their public keys
ssh-add -d ~/.ssh/id_work                            # remove one              ssh-add -D    remove all
ssh-add -K / --apple-use-keychain ~/.ssh/id_ed25519   # macOS: store the passphrase in the Keychain
Load keys automatically when first used, by adding to ~/.ssh/config:
    Host *
        AddKeysToAgent yes
        IdentitiesOnly yes
        UseKeychain yes                              # macOS only. Remove this line on Linux and Windows (it is an error on OpenSSH versions without it: add  IgnoreUnknown UseKeychain  above it)
Per platform:
Linux desktop     GNOME Keyring or KDE Wallet usually run an agent and can remember passphrases at login
Linux server/WSL  Start an agent from your profile, or use the  keychain  tool (sudo apt install keychain):   eval "$(keychain --eval --quiet id_ed25519)"   in ~/.bashrc (reuses ONE agent across terminals)
systemd (user)    systemctl --user enable --now ssh-agent   then   export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"   (available on newer distributions)
macOS             The built-in agent works with the Keychain (see UseKeychain above)
Windows           OpenSSH is built in. Enable the agent service (PowerShell as admin):  Get-Service ssh-agent | Set-Service -StartupType Automatic ; Start-Service ssh-agent      then   ssh-add $env:USERPROFILE\.ssh\id_ed25519
PuTTY             Use Pageant as the agent, and PuTTYgen for keys (see "Converting formats")
Agent FORWARDING (ssh -A, ForwardAgent yes) lets a server use your keys to hop to another server, but it lets anyone with root on that server use your agent while you are connected. Prefer ProxyJump (ssh -J) instead, and forward only to hosts you trust.

Backups and moving keys between computers

- Best practice: create a NEW key per computer instead of copying. Then losing a laptop means removing one line, not rotating everything
- If you must copy a private key: use an encrypted channel (scp/rsync over SSH, or an encrypted USB), keep the passphrase, set permissions 600 on the destination, and delete the copy from anything temporary
- Back up ~/.ssh/ in an ENCRYPTED backup (a password manager attachment, an encrypted volume, or an offline drive). Never in plain cloud storage or a public repository
- Keep a list of which key is installed where (see key comments and the second guide's audit section)
- A hardware key (ed25519-sk) cannot be copied: register a second hardware key as a spare

Converting formats

Different tools want different formats. The modern OpenSSH format starts with  -----BEGIN OPENSSH PRIVATE KEY-----.
ssh-keygen -p -m PEM -f ~/.ssh/old_key               # convert a private key to the older PEM format (some old tools need it). Work on a COPY
ssh-keygen -e -m RFC4716 -f ~/.ssh/id_rsa.pub        # export a public key in the RFC 4716 format (some appliances and older servers want it)
ssh-keygen -i -m RFC4716 -f key_rfc.pub              # import that format back into OpenSSH's one-line format
PuTTY (Windows) uses .ppk files:
puttygen id_rsa -o id_rsa.ppk                        # OpenSSH private key to PuTTY key         (Linux: sudo apt install putty-tools)     On Windows use the PuTTYgen program: Load / Save private key
puttygen id_rsa.ppk -O private-openssh -o id_rsa     # PuTTY key back to OpenSSH format
AWS and other clouds give you a .pem private key file: chmod 400 key.pem, then  ssh -i key.pem ec2-user@HOST . You can also create your own ed25519 key and import its PUBLIC half into the cloud console.

Hardware keys (FIDO2, YubiKey)

ssh-keygen -t ed25519-sk -C "alice yubikey"          # touch the key when it blinks. Creates id_ed25519_sk and id_ed25519_sk.pub
ssh-keygen -t ed25519-sk -O resident -O verify-required -C "alice yubikey"      # resident = stored on the device; verify-required = ask for the device PIN too
ssh-add -K                                           # load resident keys from a plugged-in device (macOS/Linux; the flag varies by version: see ssh-add -h)
ssh-keygen -K                                        # download resident keys from the device onto a new computer
Every login needs a touch on the key, which stops malware from silently using your key. Needs OpenSSH 8.2+ on the client and server (server support is for the key TYPE only).
Some cloud providers and older servers do not accept -sk keys yet. Keep a regular ed25519 key as a fallback.

Host keys (the server's identity: not your login key)
Servers have their own keys in /etc/ssh/ssh_host_*_key. Your computer remembers them in ~/.ssh/known_hosts the first time you connect ("The authenticity of host ... can't be established"). Check the fingerprint against a trusted source before typing yes.
ssh-keygen -A                                        # (server, as root) generate any missing host keys        ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub    show the server's fingerprint
ssh-keygen -F server.example.com                     # is this host in my known_hosts?                         ssh-keygen -R server.example.com    remove it (after a legitimate server rebuild)
ssh-keyscan -t ed25519 server.example.com            # fetch a server's public host key (for pre-populating known_hosts in scripts)

Advanced: SSH certificates (your own SSH CA)

Copying public keys to every server does not scale to hundreds of servers. An SSH CERTIFICATE is a user's public key SIGNED by a certificate authority (CA) key that the servers trust. Servers then need only ONE line (the CA), and certificates can expire automatically.
1. Create a CA key pair (guard the CA private key like a master key: offline, encrypted, backed up):
     ssh-keygen -t ed25519 -f ~/ca/ssh_user_ca -C "example.com user CA"
2. Sign a user's public key (the -n list is the accounts the certificate may log in as; -V is the validity; -I is an ID that shows in logs):
     ssh-keygen -s ~/ca/ssh_user_ca -I "alice@example.com" -n alice,deploy -V +8h ~/.ssh/id_ed25519.pub
     (creates ~/.ssh/id_ed25519-cert.pub, valid for 8 hours. Use  -V +52w  for a year.)
3. Tell every server to trust the CA (once), in /etc/ssh/sshd_config:
     TrustedUserCAKeys /etc/ssh/ssh_user_ca.pub          (copy the CA PUBLIC key there)      then    sudo sshd -t && sudo systemctl reload ssh
4. The user connects normally. SSH offers id_ed25519-cert.pub automatically when it sits next to the private key.
Inspect a certificate:  ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub     (shows the validity window, principals and extensions)
Revoke: put the certificate's serial or key in a revocation list (RevokedKeys in sshd_config), or simply let short-lived certificates expire. Host certificates work the same way in reverse (ssh-keygen -s ca -h ...) so you never see "unknown host" prompts.
Tools that automate this: HashiCorp Vault's SSH secrets engine, Smallstep, Teleport, and cloud identity services. Short-lived certificates are the modern replacement for long-lived keys.

Security best practices

[ ] ed25519 (or RSA 4096 when required), with a passphrase on every personal key
[ ] One key per computer and per purpose, named and commented clearly
[ ] Private keys are mode 600, in a 700 ~/.ssh, never in git, chat, tickets, or shared drives
[ ] An agent with timeouts (ssh-add -t) instead of empty-passphrase keys. If automation needs a key without a passphrase, restrict it (see the second guide: from=, command=, restrict) and use a dedicated account
[ ] Do not forward your agent to servers you do not fully trust. Use ProxyJump
[ ] Rotate keys on a schedule (for example yearly) and when people leave, laptops are lost or stolen, or you suspect a leak
[ ] Encrypted backups of ~/.ssh, and a list of where each public key is installed
[ ] Keep OpenSSH updated. Old versions have weaknesses (and disable DSA and weak RSA)
[ ] For high-value accounts consider hardware keys (FIDO2) or short-lived SSH certificates
If a private key is exposed: treat it as compromised. Remove its public key from every authorized_keys file, create a new key, and check the servers' logs for unexpected logins.

Common problems

"Permissions 0644 for '/home/alice/.ssh/id_ed25519' are too open"        Run  chmod 600 ~/.ssh/id_ed25519
"Bad permissions" or the key is ignored                                  ~/.ssh must be 700, and your home directory must not be group or world writable
"Load key ...: invalid format" / "error in libcrypto"                    The file is damaged, has Windows line endings, is a PuTTY .ppk file, or the passphrase is wrong. Convert it, or restore from backup. View the first line:  head -1 key
"Enter passphrase" every single time                                     No agent is running or the key is not loaded. Start an agent, run ssh-add, and set AddKeysToAgent yes. On macOS use UseKeychain
"Could not open a connection to your authentication agent"               ssh-add cannot find an agent. Run  eval "$(ssh-agent -s)"  first (and echo $SSH_AUTH_SOCK to check)
Lost the passphrase                                                      It cannot be recovered. Generate a new key and replace the public key on your servers (you need another way in: console, another key, or an admin)
Lost the .pub file                                                       Recreate it:  ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
"invalid key length" or a service rejects the key                        The service does not accept that type or size. Use ed25519, or RSA 4096. Some legacy systems need RSA and a specific format
"Too many authentication failures" later, when connecting               The agent offers many keys and the server counts each. Use IdentitiesOnly yes and IdentityFile in ~/.ssh/config, or  ssh -o IdentitiesOnly=yes -i key host
The key works locally but Windows/PuTTY refuses it                       PuTTY needs .ppk. Convert with PuTTYgen, or use the built-in OpenSSH client
"sign_and_send_pubkey: signing failed ... agent refused operation"       The agent has a hardware/confirm constraint, or the key needs a PIN or touch. Watch the key for a blink, or check ssh-add -l
Key type "ssh-rsa" refused by a new server                               Recent OpenSSH disables SHA-1 RSA signatures. Use an ed25519 key, or an RSA key with rsa-sha2-512 (OpenSSH negotiates this automatically with current clients)

Quick reference

ssh-keygen -t ed25519 -a 100 -C "you@host-2026"          Create a key                         ssh-keygen -p -f KEY        Change the passphrase
ssh-keygen -l -f KEY.pub                                  Show the fingerprint                 ssh-keygen -y -f KEY        Rebuild the public key
eval "$(ssh-agent -s)" ; ssh-add ; ssh-add -l             Start the agent, load keys, list them
chmod 700 ~/.ssh ; chmod 600 ~/.ssh/id_* ; chmod 644 ~/.ssh/*.pub
ssh-keygen -t ed25519-sk                                  A hardware-backed key                ssh-keygen -s CA -I id -n user -V +8h KEY.pub      Sign a certificate
Next: "Passwordless SSH Login: Copying Keys to Servers" (ssh-copy-id, authorized_keys, ~/.ssh/config, many servers, disabling passwords).

Tips

    Use ed25519 with a passphrase: ssh-keygen -t ed25519 -a 100 -C "name-purpose-year". Only fall back to RSA 4096 when a system requires it.

    The private key never leaves your computer and is never shared. Only the .pub file goes to servers and services.

    Load your key into ssh-agent once per session (ssh-add, or AddKeysToAgent yes) so you can log in many times without retyping the passphrase.

    Use a separate key per computer and per purpose, and label them with -C so you can tell them apart in authorized_keys.

    Keep ~/.ssh at 700 and private keys at 600, and keep your home directory from being group- or world-writable, or SSH will ignore your keys.

    Back up ~/.ssh only in an encrypted place, and rotate keys when a laptop is lost or someone leaves.

    Do not forward your agent to untrusted servers. Use ProxyJump for hopping through a bastion.

    For many servers or many people, look at SSH certificates with short lifetimes instead of copying keys around.

    A passphrase prompt at login is your local unlock, not a server password. If SSH asks for the server's password, your key was not accepted (see the second guide).