ssh-keygen: Generating SSH Keys, Registering Public Keys, and Using ssh-copy-id
ssh-keygen is the OpenSSH command that creates the private and public key pair used for SSH logins, and it also checks fingerprints, changes passphrases and cleans up known_hosts. I created ed25519 and rsa keys on an AlmaLinux 9 server, checked their fingerprints, changed the passphrase and comment, then ran ssh-copy-id as a dry run: the only thing that goes to the server is the single public key line in the .pub file.
The account names and key file paths in the output below are example values, and the fingerprints, public keys and randomart come from keys created for this article and then deleted. The command that actually registers a public key on a server is shown but was not run.
1️⃣ ssh-keygen key generation: -t ed25519 and the -C comment
Keys are created in the form ssh-keygen -t TYPE -C COMMENT. -t is the key type, and -C is a comment appended to the end of the public key, so you write an email address or what the key is for. Below is the full output with the file set by -f and Enter pressed at both passphrase prompts.
ssh-keygen -t ed25519 -C "user@example.com" -f ~/.ssh/id_ed25519Generating public/private ed25519 key pair.
Enter passphrase for "/home/user/.ssh/id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/id_ed25519
Your public key has been saved in /home/user/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:c73KJh85pPijvy43kS1uaQo77fSsGlE4Wz+aMF+sTy8 user@example.com
The key's randomart image is:
+--[ED25519 256]--+
| |
| . |
| o o |
| = o . |
| = Soo . |
| = *+*.. . |
| o.B.o++ . |
| .=o*Eo.+ |
| o+=@OO= |
+----[SHA256]-----+Pressing just Enter at the passphrase prompt gives a key without a passphrase. If you leave out -f, a line asking where to save the file appears before the passphrase prompt. The default path, .ssh/id_ed25519 under your home folder, is shown in parentheses, and pressing Enter saves the key there.
The randomart at the end of the output is the fingerprint turned into a picture. The same picture appeared when the key was created and when it was checked again with -lv.
ls -l ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub-rw------- 1 user user 411 Oct 29 08:22 /home/user/.ssh/id_ed25519
-rw-r--r-- 1 user user 98 Oct 29 08:22 /home/user/.ssh/id_ed25519.pubThe private key was created with 600 permissions and the public key with 644. Keep the private key on your own computer only and never send it anywhere.
cat ~/.ssh/id_ed25519.pubssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJVLiw04ugfXq15n2m8ZDW0OywkRoug8beXw/9VnVY/L user@example.comThe public key is a single line made of the key type, the key value and the comment separated by spaces. That line goes into authorized_keys on the server you connect to. The role of each file in the folder is grouped in a tree.
2️⃣ ssh-keygen options: -f, -N, -b, -q
Answering prompts is handy when typing by hand, but a script needs to get rid of the prompts entirely. Set the file with -f and the passphrase with -N, and the key is created right away without any questions.
ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -N "" -C "deploy@example.com"Generating public/private ed25519 key pair.
Your identification has been saved in /home/user/.ssh/deploy_key
Your public key has been saved in /home/user/.ssh/deploy_key.pub
The key fingerprint is:
SHA256:Rhk2/tgn59K8jtvDWJTOLiYnToM8t4gkdaMfu6TJ0oM deploy@example.com
The key's randomart image is:
+--[ED25519 256]--+
| + |
| o + |
| + . |
| . + o |
| . o S ++o |
| . + + B+ |
| .oo * + .=+ |
| E+o* Boo=++. |
| .=.=oo=o++. |
+----[SHA256]-----+Giving -N an empty pair of quotes creates a key without a passphrase, and the save location and fingerprint appeared in one go without any prompt.
ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -N ""Generating public/private ed25519 key pair.
/home/user/.ssh/deploy_key already exists.
Overwrite (y/n)?Running it again with the same name asks whether to overwrite after already exists. Typing n changed nothing and ended with exit code 1. To avoid overwriting a key in use by mistake, create new keys under a different name.
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" -C "user@example.com"Generating public/private rsa key pair.
Your identification has been saved in /home/user/.ssh/id_rsa
Your public key has been saved in /home/user/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:f+uwCSQjMka1BEHzXADwIWbrlbVRiP6Z4tjnwNwPVAA user@example.com
The key's randomart image is:
+---[RSA 4096]----+
|o=BE*=+. |
|oo.Oo++ |
| .+o+. . |
|.... . |
| .+ o.= S |
| .o+o= + . |
| ++.o . o . |
| . o..o . = . |
| o. . o.o |
+----[SHA256]-----+For rsa, -b sets the length. According to the manual the default is 3072 bits; above, the key was created with 4096 bits and RSA 4096 appeared in the randomart header. The manual says ed25519 has a fixed length, so -b is ignored for it.
ssh-keygen -f ~/.ssh/default_key -N "" -C "user@example.com"Generating public/private ed25519 key pair.
Your identification has been saved in /home/user/.ssh/default_key
Your public key has been saved in /home/user/.ssh/default_key.pub
The key fingerprint is:
SHA256:mwy02fpKxjxEVvLC2EUJeaMIiNFOVS0GwWVjN9v0j4c user@example.com
The key's randomart image is:
+--[ED25519 256]--+
|o+ o+=O=*.. |
|o + .*+B== . |
| o ..oB+o.. . |
| . .+.= + |
| = S E o |
| + + o . |
| B + |
| o o |
| ... |
+----[SHA256]-----+Leaving out -t created an ed25519 key.
ssh-keygen -q -t ed25519 -f ~/.ssh/backup_key -N "" -C "backup@example.com"; echo rc=$?rc=0Adding -q hides everything, including the fingerprint and randomart, and leaves only the exit code. Use it when a script creates keys for several servers.
📌 Main options: results from this article
| Option | Task | Result in this article | |
|---|---|---|---|
| -t TYPE | Type of key to create | The first line of the output shows the type, such as ed25519 or rsa; if omitted, ed25519 | |
| -C COMMENT | Comment added to the end of the public key | user@example.com at the end of the public key shown with cat | |
| -f FILE | Key file path | A line saying it was saved to /home/user/.ssh/deploy_key; running again with the same name shows already exists and Overwrite (y/n)? | |
| -N PASSPHRASE | New passphrase; empty quotes mean no passphrase | Save location and fingerprint in one go, without a passphrase prompt | |
| -b BITS | Key length | Giving rsa 4096 puts RSA 4096 in the randomart header | |
| -q | Hides all output | Only the rc=0 line printed by echo remains | |
| -l | Prints the fingerprint of a key file | The same SHA256 fingerprint from the public and private key; for authorized_keys, one line per key | |
| -E HASH | Sets the hash type of the fingerprint | With -E md5, a fingerprint starting with MD5:00:7b:02:5e | |
| -v | With -l, also prints the randomart | The same randomart as when the key was created | |
| -y | Extracts and prints the public key from the private key | One public key line identical, character for character, to the .pub file | |
| -p | Changes the passphrase of a private key | A line saying it was saved with the new passphrase; the private key grows from 411 to 464 bytes | |
| -P PASSPHRASE | Old passphrase (the one currently set) | The right passphrase gives the public key; a wrong one gives incorrect passphrase supplied to decrypt private key | |
| -c | Changes the comment; the new comment goes with -C | Old comment: user@example.com and Comment 'user@laptop' applied | |
| -F HOST | Finds that host's line in known_hosts | # Host 192.0.2.10 found: line 1 and the line itself | |
| -R HOST | Removes that host's line from known_hosts | known_hosts updated and the original file known_hosts.old; an unknown host gives not found | |
| -H | Hashes host names and addresses in known_hosts | |1|-style lines, plus a WARNING that the .old file keeps the unhashed lines |
📎 Other options: per the official manual
| Option | Task | |
|---|---|---|
| -a ROUNDS | KDF (bcrypt_pbkdf) rounds used when saving the private key; more rounds make passphrase checks slower and brute-force harder; default 16 | |
| -A | Creates missing host keys for each default type (rsa, ecdsa, ed25519) in the default paths | |
| -B | Prints the bubblebabble digest of a key file | |
| -e | Converts an OpenSSH key to the -m format (default RFC4716) and prints it, for other SSH programs | |
| -i | Converts a passphrase-less key in the -m format (default RFC4716) to OpenSSH format and prints it | |
| -m FORMAT | Sets the key format (RFC4716, PKCS8, PEM); with -p, converts private keys between OpenSSH and PEM formats | |
| -r HOST | Prints the SSHFP DNS record for that host name from the public key | |
| -s CA_KEY | Signs a public key with a CA key to create a certificate | |
| -L | Prints the contents of a certificate | |
| -K | Downloads resident keys stored on a FIDO authenticator into the current folder | |
| -Y sign | Signs a file with an SSH key; the signature is saved with .sig added to the file name, and the namespace is given with -n |
3️⃣ ssh-keygen -l fingerprints and -y public key extraction
With several key files, it is hard to tell from the file names alone which public key pairs with which private key. Because the fingerprint is the same for the private and public key, comparing the two files with -l confirms the pair.
ssh-keygen -l -f ~/.ssh/id_ed25519.pub256 SHA256:c73KJh85pPijvy43kS1uaQo77fSsGlE4Wz+aMF+sTy8 user@example.com (ED25519)ssh-keygen -l -f ~/.ssh/id_ed25519256 SHA256:c73KJh85pPijvy43kS1uaQo77fSsGlE4Wz+aMF+sTy8 user@example.com (ED25519)The public key file and the private key file gave the same SHA256 fingerprint. The 256 in front is the key length, and the parentheses at the end show the key type.
ssh-keygen -l -E md5 -f ~/.ssh/id_ed25519.pub256 MD5:00:7b:02:5e:b8:ba:85:0c:32:d7:15:7e:72:45:f7:bc user@example.com (ED25519)Adding -E md5 switches to the older MD5 fingerprint format. Use it to compare when an old server or a control panel shows MD5 fingerprints.
ssh-keygen -lv -f ~/.ssh/id_ed25519.pub256 SHA256:c73KJh85pPijvy43kS1uaQo77fSsGlE4Wz+aMF+sTy8 user@example.com (ED25519)
+--[ED25519 256]--+
| |
| . |
| o o |
| = o . |
| = Soo . |
| = *+*.. . |
| o.B.o++ . |
| .=o*Eo.+ |
| o+=@OO= |
+----[SHA256]-----+Adding -v shows the randomart again. It was the same picture as the one printed when the key was created.
ssh-keygen -l -f ~/.ssh/authorized_keys256 SHA256:Rhk2/tgn59K8jtvDWJTOLiYnToM8t4gkdaMfu6TJ0oM deploy@example.com (ED25519)If you give a file with several public key lines, such as authorized_keys, you get one fingerprint line per key. This is how to check which keys are registered on a server.
ssh-keygen -y -f ~/.ssh/id_ed25519ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJVLiw04ugfXq15n2m8ZDW0OywkRoug8beXw/9VnVY/L user@example.com-y extracts the public key from the private key again. If you deleted the .pub file or moved only the private key to another computer, save this output to a file. The output was identical, character for character, to the original .pub file.
4️⃣ ssh-keygen -p passphrase change and -c comment change
With a passphrase on the private key, the file cannot be used without the passphrase even if it leaks. On the other hand, you have to type the passphrase every time you connect, so it suits keys people use by hand and not keys for scripts that run automatically, such as cron jobs.
ssh-keygen -p -f ~/.ssh/id_ed25519 -P "" -N "example-pass"Key has comment 'user@example.com'
Your identification has been saved with the new passphrase.-P is the old passphrase and -N the new one. The key had no passphrase, so -P got empty quotes, and two lines appeared saying it was saved again with the new passphrase. The manual says that with -p alone and no options, it asks for the old and new passphrase in turn.
ls -l ~/.ssh/id_ed25519-rw------- 1 user user 464 Oct 29 08:22 /home/user/.ssh/id_ed25519After the passphrase was set, the private key file grew from 411 to 464 bytes.
ssh-keygen -y -f ~/.ssh/id_ed25519 -P "example-pass"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJVLiw04ugfXq15n2m8ZDW0OywkRoug8beXw/9VnVY/L user@example.comssh-keygen -y -f ~/.ssh/id_ed25519 -P "wrongpass"Load key "/home/user/.ssh/id_ed25519": incorrect passphrase supplied to decrypt private keyFor a key with a passphrase, -y also needs the passphrase passed with -P. The right passphrase gave the same public key as before, and a wrong one ended with an incorrect passphrase error. The manual states that a lost passphrase cannot be recovered, so if you forget it, create a new key and register the public key again.
ssh-keygen -c -C "user@laptop" -f ~/.ssh/id_ed25519 -P "example-pass"Old comment: user@example.com
Comment 'user@laptop' appliedcat ~/.ssh/id_ed25519.pubssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJVLiw04ugfXq15n2m8ZDW0OywkRoug8beXw/9VnVY/L user@laptop-c changes the comment. The old and new comments appear on one line each, and the comment at the end of the public key file also changed to user@laptop. The comment is only a label to tell keys apart, so the key value itself stayed the same.
ssh-keygen -p -f ~/.ssh/id_ed25519 -P "example-pass" -N ""Key has comment 'user@laptop'
Your identification has been saved with the new passphrase.To remove the passphrase, give -N empty quotes. The same two lines appear, and after that -y ran again without -P.
5️⃣ ssh-keygen -R removing host keys from known_hosts
known_hosts collects the host key of every server you have connected to, one line each. If a server is reinstalled and its host key changes, ssh refuses to connect because the key differs from the stored one, and -R is the option that removes the old line. The known_hosts below is a file made of three lines with example addresses, given with -f.
cat ~/.ssh/known_hosts192.0.2.10 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKKphFOc7oiKNpIHFTzK/ippCS5U5D+lF3xmXM6dmYjk
server2.example.com,192.0.2.20 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3s6oYeTCz7KeYs0f/60/Ix0DhmK4hf1uHCC10TrXyR
[192.0.2.30]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC0WmcBD2HIsiSn+tERrbeFIyExAodSEUhE9j5WMpkRWEach line contains the host name or address, the key type and the key value, in that order. The second line, joined with a comma, lists the name and address of the same server, and the brackets and colon in the third line mean a port other than 22.
ssh-keygen -F 192.0.2.10 -f ~/.ssh/known_hosts# Host 192.0.2.10 found: line 1
192.0.2.10 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKKphFOc7oiKNpIHFTzK/ippCS5U5D+lF3xmXM6dmYjk-F prints that host's line together with its line number. For an unknown host it printed nothing and ended with exit code 1.
ssh-keygen -R 192.0.2.10 -f ~/.ssh/known_hosts# Host 192.0.2.10 found: line 1
/home/user/.ssh/known_hosts updated.
Original contents retained as /home/user/.ssh/known_hosts.oldcat ~/.ssh/known_hostsserver2.example.com,192.0.2.20 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3s6oYeTCz7KeYs0f/60/Ix0DhmK4hf1uHCC10TrXyR
[192.0.2.30]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC0WmcBD2HIsiSn+tERrbeFIyExAodSEUhE9j5WMpkRW-R removes that host's line and keeps the original file as known_hosts.old. After the removal, only two lines were left.
ssh-keygen -R 192.0.2.99 -f ~/.ssh/known_hostsHost 192.0.2.99 not found in /home/user/.ssh/known_hostsssh-keygen -R "[192.0.2.30]:2222" -f ~/.ssh/known_hosts# Host [192.0.2.30]:2222 found: line 2
/home/user/.ssh/known_hosts updated.
Original contents retained as /home/user/.ssh/known_hosts.oldGiving an unknown host printed just one not found line. For a server on a different port, you have to include the brackets and port exactly as written in known_hosts.
ssh-keygen -H -f ~/.ssh/known_hostsWARNING: /home/user/.ssh/known_hosts.old contains unhashed entries
Delete this file to ensure privacy of hostnames
/home/user/.ssh/known_hosts updated.
Original contents retained as /home/user/.ssh/known_hosts.oldcat ~/.ssh/known_hosts|1|JL/knNlx/BnCoOWp5rNeRo3r1rE=|OCPyeFd1rgG8P2QGzrju/V2Y8c4= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3s6oYeTCz7KeYs0f/60/Ix0DhmK4hf1uHCC10TrXyR
|1|AG9QJQvGaQmVsEusSdND1lM9cKI=|Ue0a9suJIzN0yxoJgV7ClLBgSYw= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3s6oYeTCz7KeYs0f/60/Ix0DhmK4hf1uHCC10TrXyR-H hashes host names and addresses. It keeps a leaked file from revealing which servers you connected to, and the name and address that were joined with a comma were split into two hashed lines. A warning that the .old file still holds the unhashed contents appeared as well.
ssh-keygen -F server2.example.com -f ~/.ssh/known_hosts# Host server2.example.com found: line 1
|1|JL/knNlx/BnCoOWp5rNeRo3r1rE=|OCPyeFd1rgG8P2QGzrju/V2Y8c4= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3s6oYeTCz7KeYs0f/60/Ix0DhmK4hf1uHCC10TrXyREven in the hashed file, -F found the line by name.
6️⃣ ssh-copy-id public key registration and authorized_keys
Creating a key is not enough: as long as the server does not know the public key, you still log in with a password. The place that tells the server about the public key is ~/.ssh/authorized_keys of the account you log in to, and ssh-copy-id is the command that adds the .pub line to this file. The private key stays on your computer and only the public key line goes over. Press the step buttons in the diagram below to see where the two files end up.
ssh-copy-id -n -i ~/.ssh/deploy_key.pub -o ConnectTimeout=3 user@192.0.2.10/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/deploy_key.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: ERROR: ssh: connect to host 192.0.2.10 port 22: Connection timed out-n is a dry run, so nothing is written to the server. Even so, ssh-copy-id first tries to log in with that key to filter out keys that are already registered, and because 192.0.2.10 is a documentation address, the connection failed and it ended with timed out. Options after -o are passed straight to ssh.
ssh-copy-id -n -f -i ~/.ssh/deploy_key.pub -o ConnectTimeout=3 user@192.0.2.10/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/deploy_key.pub"
=-=-=-=-=-=-=-=
Would have added the following key(s):
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKKphFOc7oiKNpIHFTzK/ippCS5U5D+lF3xmXM6dmYjk deploy@example.com
=-=-=-=-=-=-=-=-f is the force mode that skips this check. Together with -n, the public key line that would go to the server appeared under Would have added. It is the same line as the content of the .pub file.
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@192.0.2.10The actual registration is done with this command without -n. According to the ssh-copy-id script and manual, it asks for the account's password once, appends the public key to the end of ~/.ssh/authorized_keys on the server and then prints Number of key(s) added: 1. It creates the file and folder if they are missing and skips the key if the same one is already there.
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 user@192.0.2.10If the port is not 22, pass it with -p.
Where ssh-copy-id is not available, append the public key line to authorized_keys on the server yourself. Log in to the server with your password and follow the steps below. The output was checked by appending the .pub file on the same server; on a different server, copy the line from the .pub file and append it with echo.
mkdir -p ~/.sshchmod 700 ~/.sshIf the .ssh folder does not exist, create it and set its permissions to 700.
cat ~/.ssh/deploy_key.pub >> ~/.ssh/authorized_keyschmod 600 ~/.ssh/authorized_keyscat ~/.ssh/authorized_keysssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKKphFOc7oiKNpIHFTzK/ippCS5U5D+lF3xmXM6dmYjk deploy@example.comls -l ~/.ssh/authorized_keys-rw------- 1 user user 100 Oct 29 08:22 /home/user/.ssh/authorized_keysThe public key line went into authorized_keys with 600 permissions. The StrictModes entry in the sshd_config manual says that with the default settings sshd checks the permissions of the user's files first and refuses the login if they are writable by anyone.
ssh -i ~/.ssh/id_ed25519 user@192.0.2.10Once registration is done, connect with the private key. If the key has a default name such as id_ed25519, ssh finds it on its own; if it has another name such as deploy_key, specify the file with the ssh -i option.
🔲 ssh-keygen command simulator
In the simulator below you can type ssh-keygen yourself to create keys, check fingerprints, change the passphrase, clean up known_hosts and run ssh-copy-id. The fingerprints and public keys are the same example values as in the article, and ssh-copy-id only pretends to register the key on a virtual server.
🔢 FAQ & Recommended Content
Windows · Public key
How to Generate a Public Key in Xshell
Steps to create a public key with the key generation wizard of the Windows SSH client Xshell and register it on a server.
SSH · Server settings
SSH Configuration: sshd_config File Features and Settings
An article on setting items such as the port, root login and public key authentication in sshd_config on the receiving server.
Linux · Concepts
Linux SSH: Definition and Basic Setup
What SSH is and how it differs from Telnet, and how to turn on sshd on a server and check its basic settings.
ℹ️ Affiliate Disclosure
This site's content contains affiliate links. When a visitor buys a product or service through one of them, the site receives a commission from the seller. The amount the buyer pays(it goes down during event discounts ↓)does not go up. Posted prices, discounts, and stock reflect the time of writing and may differ, so confirm with the seller before buying. Products are chosen and reviewed by our own standards, and commissions do not affect the order or content of reviews.