ssh-keygen command reference and cheat sheet
ssh-keygen creates, converts and inspects SSH keys, and manages known_hosts and certificates. The options you will actually use, with examples.
Quick answer. ssh-keygen -t ed25519 -C "[email protected]" creates a new key pair in
~/.ssh. -p changes its passphrase, -l shows its fingerprint and
-R forgets a server's old host key. Examples for each follow.
The commands you need most
ssh-keygen -t ed25519 -C "[email protected]" # create a new key (recommended)
ssh-keygen -t rsa -b 4096 -C "[email protected]" # RSA, for systems without Ed25519
ssh-keygen -p -f ~/.ssh/id_ed25519 # change the passphrase
ssh-keygen -lf ~/.ssh/id_ed25519.pub # show the fingerprint
ssh-keygen -y -f ~/.ssh/id_ed25519 # print the public key
ssh-keygen -R example.com # forget a host's old key
Options
| Flag | What it does |
|---|---|
-t type | Key type: ed25519, rsa, ecdsa, or ed25519-sk / ecdsa-sk for FIDO2 hardware keys. Default ed25519 in OpenSSH 9.5 and later, rsa before. |
-b bits | Key size. RSA: 3072 by default, 2048 minimum in practice, 4096 common. ECDSA: 256, 384 or 521. Ignored for Ed25519. |
-C comment | Comment stored with the key and appended to the public key line, usually an email or user@host. |
-f file | Key file to create or operate on. The public key is written to the same name plus .pub. |
-N passphrase | New passphrase, non-interactively. -N "" creates an unencrypted key. |
-P passphrase | Old passphrase, for -p and for reading encrypted keys in scripts. |
-a rounds | bcrypt_pbkdf rounds used to encrypt the private key. Default 16; higher is slower to unlock and to brute-force. |
-Z cipher | Cipher that encrypts the private key file. Default aes256-ctr; ssh -Q cipher lists the alternatives. |
-p | Change, add or remove the passphrase of an existing key, without changing the key. |
-c | Change the comment of an existing key, together with -C. |
-l | Print the fingerprint of a key file, authorized_keys or known_hosts. |
-E hash | Fingerprint hash for -l: sha256 (default) or md5. |
-B | Print the bubblebabble digest of a key, an older pronounceable fingerprint format. |
-v | Verbose. With -l, also draws the randomart image. |
-y | Read a private key and print its public key, to recreate a lost .pub file. |
-e | Export a key to another format, by default RFC 4716 (SSH2) used by some commercial SSH servers. |
-i | Import a key from RFC 4716 or PKCS8 format into OpenSSH format. |
-m format | Format for -e, -i and new private keys: RFC4716, PKCS8 or PEM. |
-q | Quiet. Suppresses the progress output and randomart, for scripts. |
-R host | Remove all keys for a host from known_hosts. |
-F host | Find a host in known_hosts, also in hashed files. |
-H | Hash every host name in known_hosts, so the file does not reveal where you connect. |
-A | Create any missing host keys in /etc/ssh with default settings, as installers do. |
-r host | Print SSHFP DNS records for a public key, for publishing host key fingerprints. |
-O option | Extra options, for example resident and verify-required for FIDO keys, or certificate options with -s. |
-K | Download resident keys from a connected FIDO2 authenticator into the current folder. |
-w provider | Use a different FIDO middleware library instead of the built-in one. |
-D pkcs11 | Print the public keys held on a PKCS#11 token such as a smart card, via its library. |
-s ca_key | Sign a public key with a CA key to create a certificate. Used with -I, -n and -V. |
-I id | Key identity recorded in a certificate and in server logs. |
-n principals | Users (or, with -h, host names) a certificate is valid for, comma separated. |
-V interval | Certificate validity, for example +52w or 20260101:20270101. |
-z serial | Serial number embedded in a certificate, so it can be revoked individually. |
-h | Create a host certificate instead of a user certificate. |
-U | The CA key for -s or -Y sign lives in ssh-agent; pass its public key to -s. |
-L | Print the contents of a certificate. |
-k | Create a key revocation list (KRL); -u updates an existing one. |
-Q | Test whether keys are listed in a KRL. |
-Y op | Sign or verify arbitrary data: sign, verify, find-principals, check-novalidate. |
-M op | Generate (generate) or test (screen) Diffie-Hellman group exchange moduli. |
The full list is in man ssh-keygen. -o from older guides has been the default since
OpenSSH 7.8.
Generate keys
A standard key with a passphrase
ssh-keygen -t ed25519 -C "[email protected]"
It asks for a file name (Enter keeps ~/.ssh/id_ed25519) and a passphrase. On OpenSSH 10 the session
looks like this:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/you/.ssh/id_ed25519):
Enter passphrase for "/home/you/.ssh/id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/you/.ssh/id_ed25519
Your public key has been saved in /home/you/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:I2McFhOvOOgBGIf3aLmk0z7TTKxZoLZ32hK6Bxbk7Tw [email protected]
Which type to choose is covered in Ed25519 vs RSA vs ECDSA; platform walkthroughs are in the Linux, macOS and Windows guides.
RSA and ECDSA keys
ssh-keygen -t rsa -b 4096 -C "[email protected]"
ssh-keygen -t ecdsa -b 384 -C "[email protected]"
A second key under its own name
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work -C "[email protected]"
A key for automation, without a passphrase
ssh-keygen -t ed25519 -N "" -q -f ./deploy_key -C "deploy@ci"
Non-interactive, for CI. Restrict the key with command= and from= in
authorized_keys.
A stronger passphrase derivation
ssh-keygen -t ed25519 -a 64 -C "[email protected]"
More rounds make every unlock, and every guess by an attacker who stole the file, slower. See KDF rounds.
A key in the legacy PEM format
ssh-keygen -t rsa -b 4096 -m PEM -f ./legacy_key
Only for software that cannot read the OpenSSH format. PEM private keys are weaker at rest, so convert back when you can.
Change the passphrase and comment
Change, add or remove the passphrase
ssh-keygen -p -f ~/.ssh/id_ed25519
The key and fingerprint stay the same. An empty new passphrase removes protection. In scripts, pass both with
-P "old" -N "new", and raise the KDF rounds at the same time with -a.
Change the comment
$ ssh-keygen -c -C "new comment" -f ~/.ssh/id_ed25519
Old comment: [email protected]
Comment 'new comment' applied
The comment is only a label; servers ignore it. Changing it also rewrites the .pub file, but copies of
the public key already installed elsewhere keep the old comment.
Read the public key from a private key
ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub
Recreates a lost or damaged .pub file. For an encrypted key it asks for the passphrase. It is also the
surest way to check that a private and a public key belong together: compare the output with the
.pub file.
Fingerprints and randomart
$ ssh-keygen -lf ~/.ssh/id_ed25519.pub
256 SHA256:I2McFhOvOOgBGIf3aLmk0z7TTKxZoLZ32hK6Bxbk7Tw [email protected] (ED25519)
$ ssh-keygen -E md5 -lf ~/.ssh/id_ed25519.pub
256 MD5:b5:23:6d:0b:dc:ac:67:62:85:a1:dd:80:15:4c:52:2c [email protected] (ED25519)
$ ssh-keygen -lvf ~/.ssh/id_ed25519.pub
-l also reads authorized_keys and known_hosts, one line of output per key, and
standard input with -f -. Everything about fingerprints is in
SSH key fingerprints.
Convert formats: PEM, PKCS8, RFC 4716
| Format | Looks like | Used by |
|---|---|---|
| OpenSSH private | -----BEGIN OPENSSH PRIVATE KEY----- | Current OpenSSH, the default |
| OpenSSH public | ssh-ed25519 AAAA... comment | authorized_keys, GitHub, every SSH server |
| PEM | -----BEGIN RSA PRIVATE KEY----- | Older tools and libraries |
| PKCS8 | -----BEGIN PUBLIC KEY----- | OpenSSL and TLS tooling |
| RFC 4716 | ---- BEGIN SSH2 PUBLIC KEY ---- | Some commercial SSH servers and appliances |
Rewrite a private key in the old PEM format
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa
For old tools that only read -----BEGIN RSA PRIVATE KEY-----. This rewrites the key in place, so copy
it first. Running ssh-keygen -p without -m converts it back to the OpenSSH format.
Export a public key as RFC 4716 or PKCS8
$ ssh-keygen -e -f ~/.ssh/id_ed25519.pub
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "256-bit ED25519, converted by you@laptop from OpenSSH"
AAAAC3NzaC1lZDI1NTE5AAAAICjC3B8SRdvp0H36PKIVoOSLOR8PA2GXw+6tRPH+FQ0z
---- END SSH2 PUBLIC KEY ----
$ ssh-keygen -e -m PKCS8 -f ~/.ssh/id_rsa.pub
Import an RFC 4716 or PKCS8 public key
ssh-keygen -i -f key_from_vendor.pub >> ~/.ssh/authorized_keys
ssh-keygen -i -m PKCS8 -f public.pem
Work with known_hosts
ssh-keygen -F example.com # show stored keys for a host
ssh-keygen -R example.com # remove them after a legitimate key change
ssh-keygen -R "[example.com]:2222" # a host on a non-standard port
ssh-keygen -H # hash every host name in the file
-R and -H keep the previous version as known_hosts.old. After -H,
delete that file, since it still contains the readable names. Use -f to work on a file other than
~/.ssh/known_hosts.
Sign and verify files and commits
Since OpenSSH 8.1, ssh-keygen signs arbitrary data. Git uses this for commit signing.
ssh-keygen -Y sign -f ~/.ssh/id_ed25519 -n file release.tar.gz
ssh-keygen -Y verify -f allowed_signers -I [email protected] -n file -s release.tar.gz.sig < release.tar.gz
The first command writes release.tar.gz.sig. The allowed_signers file lists trusted keys,
one per line, as [email protected] ssh-ed25519 AAAA.... A good signature prints:
Good "file" signature for [email protected] with ED25519 key SHA256:I2McFhOvOOgBGIf3aLmk0z7TTKxZoLZ32hK6Bxbk7Tw
-
-nis the namespace. A signature made for one namespace does not verify in another, so a file signature cannot be passed off as a git signature. Git usesgit. -Y find-principals -s file.sig -f allowed_signerstells you who made a signature.-Y check-novalidate -n file -s file.sig < filechecks that a signature is intact without deciding whether the signer is trusted.
SSH certificates
A CA key signs user keys with an expiry date, and servers trust the CA with one TrustedUserCAKeys line
in sshd_config. No more copying public keys to every server, and access ends by itself when the
certificate expires.
User certificates
ssh-keygen -t ed25519 -f ca_user_key -C "user CA"
ssh-keygen -s ca_user_key -I alice -n alice,deploy -V +8h ~/.ssh/id_ed25519.pub
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
The second command writes id_ed25519-cert.pub, valid for eight hours as users alice and deploy. ssh
picks it up automatically next to the key. -L shows what is inside:
Type: [email protected] user certificate
Public key: ED25519-CERT SHA256:I2McFhOvOOgBGIf3aLmk0z7TTKxZoLZ32hK6Bxbk7Tw
Signing CA: ED25519 SHA256:upxCLwGfORW/m0nebVB5eDsOeF4LgBghrwbM7m6TqL8 (using ssh-ed25519)
Key ID: "alice"
Serial: 0
Valid: from 2026-10-09T05:42:00 to 2026-10-09T13:43:09
Principals:
alice
deploy
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
Restrict a certificate with -O: -O force-command=/usr/local/bin/backup,
-O source-address=203.0.113.0/24, or -O clear to drop all the permit extensions and add
back only what is needed, such as -O permit-pty. On the server:
TrustedUserCAKeys /etc/ssh/user_ca.pub
Host certificates
The CA can also sign host keys, so clients trust every server it signed without the first-connection prompt:
ssh-keygen -s ca_host_key -h -I web01 -n web01.example.com -V +52w /etc/ssh/ssh_host_ed25519_key.pub
Point sshd at the result with HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub, and add one line
to the clients' known_hosts: @cert-authority *.example.com ssh-ed25519 AAAA... with the
host CA's public key.
Revoke keys with a KRL
A key revocation list is a compact file of keys and certificates sshd must refuse, useful when a key you cannot delete
from every authorized_keys leaks, or for revoking certificates before they expire.
ssh-keygen -k -f /etc/ssh/revoked_keys stolen_key.pub
ssh-keygen -k -u -f /etc/ssh/revoked_keys another_key.pub
ssh-keygen -Q -f /etc/ssh/revoked_keys suspect_key.pub
The first creates the list, the second adds to it, the third checks a key against it. Enable it with
RevokedKeys /etc/ssh/revoked_keys in sshd_config.
FIDO2 security keys
ssh-keygen -t ed25519-sk -O resident -O verify-required -C "yubikey"
The key lives on the token. resident lets ssh-keygen -K fetch it on another computer;
verify-required asks for the PIN as well as a touch. Needs OpenSSH 8.2 on both ends.
| Option | Effect |
|---|---|
-O resident | Store the key handle on the token, so it can be recovered with -K. |
-O verify-required | Require the token's PIN or biometric check on every use. |
-O application=ssh:name | Keep several resident keys apart on one token. Must start with ssh:. |
-O no-touch-required | Allow use without a touch. The server must also allow it with no-touch-required in authorized_keys. |
-O user=name | User name stored with a resident key. |
cd ~/.ssh && ssh-keygen -K
Downloads the resident keys from a connected token as key files in the current folder. If your token lacks Ed25519
support, use -t ecdsa-sk.
Host keys and SSHFP records
sudo ssh-keygen -A
ssh-keygen -r host.example.com -f /etc/ssh/ssh_host_ed25519_key.pub
-A creates any missing host keys in /etc/ssh with an empty passphrase, which is what
package installers run. -r prints SSHFP records for publishing the host key fingerprint in DNS. To give a
reinstalled server a fresh identity, delete its ssh_host_* files, run ssh-keygen -A and
restart sshd; clients will then see the host-key-changed warning.
Moduli for Diffie-Hellman group exchange
/etc/ssh/moduli holds the prime groups sshd uses for the diffie-hellman-group-exchange key
exchange. Current clients and servers usually agree on curve25519 or a post-quantum hybrid instead, so this matters
only for old clients. Generating your own is a two-step job that takes hours:
ssh-keygen -M generate -O bits=3072 moduli-3072.candidates
ssh-keygen -M screen -f moduli-3072.candidates moduli-3072
A common hardening step is simpler: drop the short groups from the shipped file with
awk '$5 >= 3071' /etc/ssh/moduli, written to a new file that then replaces the original.
No terminal at hand?
The sshkeygen.dev generator writes the same key files in your browser.
FAQ
What does ssh-keygen do without any options?
It creates a new key pair interactively, asking for a file name and a passphrase. On OpenSSH 9.5 and later the type is Ed25519, saved as ~/.ssh/id_ed25519; older versions create an RSA key. Passing -t ed25519 makes the result the same everywhere.
How do I generate a key without any prompts?
Give the file and the passphrase on the command line: ssh-keygen -t ed25519 -f ./key -N "" -q. If the file exists, ssh-keygen still asks whether to overwrite it, so remove the old file first in scripts.
What is the difference between ssh-keygen -l and -y?
-l prints the short fingerprint of a key. -y prints the whole public key line from a private key file, which is how you rebuild a missing .pub file.
Can ssh-keygen convert a PuTTY .ppk key?
No. Use PuTTYgen: load the .ppk and export it as an OpenSSH key, or on Linux and macOS run puttygen key.ppk -O private-openssh -o id_ed25519. The other direction also needs PuTTYgen.
What does the -o option do?
It used to select the newer OpenSSH private key format. That format has been the default since OpenSSH 7.8, so -o is accepted but does nothing on current versions.
How do I see which version of ssh-keygen I have?
ssh-keygen has no version flag; -V sets a certificate's validity. Run ssh -V, since ssh and ssh-keygen come from the same OpenSSH release.
Does ssh-keygen need root?
Only to write files that root owns, such as host keys in /etc/ssh. Personal keys, fingerprints, conversions and known_hosts edits run as your own user.