How to check an SSH key fingerprint, and what to compare it with
SSH asks you to confirm a fingerprint the first time you connect to a server, GitHub shows one beside every key on your account, and a colleague asks you to read yours out before they add it to a server. Checking one takes a single command. The part people skip is knowing what to compare it against, which is the only part that makes the check mean anything.
On a machine with OpenSSH, ssh-keygen -lf key.pub prints a key's fingerprint. Without a terminal, paste the public key into KEYPRINT and it shows the same SHA256 fingerprint, plus the older MD5 form, the key's real type and size, and anything wrong with it. Then compare it with a fingerprint you got some other way: the server's console, your hosting provider's dashboard, the service's published list, or the person reading it to you. A fingerprint compared with nothing proves nothing.
What a fingerprint is
A public key is a few hundred characters of base64 nobody compares by eye. A fingerprint is a hash of it: short enough to read out loud, and different for any other key. Current OpenSSH prints the SHA-256 of the key's binary form, in base64 with the padding left off:
256 SHA256:JUx/zUVydtlju2loQOMShn3jvVTk7nBhqDCVEixgHWA user@laptop (ED25519)
The number at the front is the key size in bits, then the fingerprint, the comment, and the type. Older systems and some control panels still show the MD5 form instead, as sixteen colon-separated hex pairs, like MD5:3b:5c:.... OpenSSH switched its default from MD5 to SHA256 in version 6.8, in 2015. Both identify the same key, but you cannot compare one form with the other: get both sides into the same form first. ssh-keygen -E md5 -lf key.pub prints the MD5 one.
The fingerprint is computed from the key data, not from the label. The ssh-ed25519 at the start of a key line and the comment at the end are just text and can say anything. KEYPRINT reads the key's own wire format for its type and size, and flags a line whose label and data disagree.
The commands
| You want | Command |
|---|---|
| Your own key's fingerprint | ssh-keygen -lf ~/.ssh/id_ed25519.pub |
| The same, in the old MD5 form | ssh-keygen -E md5 -lf ~/.ssh/id_ed25519.pub |
| A server's host key, run on the server | ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub |
| A server's host key, from outside | ssh-keyscan host.example.com | ssh-keygen -lf - |
| Every key in an authorized_keys file | ssh-keygen -lf ~/.ssh/authorized_keys |
| What your known_hosts has for a host | ssh-keygen -F host.example.com |
Reading from standard input with -lf - needs OpenSSH 7.2 or later. Note that ssh-keyscan asks the server over the network, so it tells you what the server is presenting now, which is exactly the thing you are trying to verify. It is useful for comparing, never as the trusted side of the comparison.
What to compare it with
The check only works if the fingerprint you compare against reached you by a different route from the key itself. Good sources, roughly from best to acceptable:
- The machine itself, through a route that is not SSH: the cloud provider's serial or web console, a keyboard and screen in the room, or the setup log where the provider printed the host keys when the server was built.
- The service's own published list, fetched over HTTPS. GitHub, for one, publishes its SSH key fingerprints in its documentation.
- A person you know, reading it to you over a phone call or a message on a channel you already trust.
- DNS, if the domain publishes SSHFP records and is signed with DNSSEC; OpenSSH checks them when
VerifyHostKeyDNSis on.
Comparing the first and last few characters is a much weaker check than it feels. Someone who wants a key that looks like yours can generate keys until a handful of characters at each end line up, which takes computing time, not cleverness. Matching all 43 characters is out of reach. Compare the whole string, or paste both into a text search and see whether it finds a match.
The two prompts SSH shows, and what each means
The first time you connect, SSH says The authenticity of host ... can't be established, prints the server's fingerprint, and asks whether to continue. This is not an error. It is SSH telling you it has never seen this server and cannot tell whether the key belongs to it. Typing yes without comparing is trusting the network you happen to be on at that moment. Compare it with one of the sources above, then answer.
The warning in capitals, REMOTE HOST IDENTIFICATION HAS CHANGED, means the server presented a different key from the one stored in your known_hosts. The honest reasons are that the server was rebuilt, reinstalled, or its keys were rotated. The dishonest one is that something between you and it is pretending to be it. Find out which before doing anything else: check the new fingerprint against a trusted source, and only then remove the old entry with ssh-keygen -R host.example.com and connect again.
Auditing an authorized_keys file
An authorized_keys file is the list of every key that can log in as that account, and on a long-lived server it collects keys nobody can put a name to. Paste the whole file into KEYPRINT and each line is read with its options, such as from=, command= and restrict. It flags:
- DSA keys, which OpenSSH stopped accepting by default in version 7.0, and which recent releases no longer support at all. One that still logs in means an old server, or one where DSA was switched back on.
- RSA keys under 2048 bits.
- The same key twice, often under two different comments. That usually means a key that was meant to be removed was relabelled instead.
- A line whose type label and key data disagree.
Then go through the list and ask, for each fingerprint, whose it is. A key nobody can name is a key that should go.
Is it safe to paste a key into a website?
A public key is meant to be shared: it is what you give servers and services so they can recognise you. The fingerprint is public too. What must never be pasted anywhere is the private key, the file without .pub on the end, which begins -----BEGIN OPENSSH PRIVATE KEY-----. KEYPRINT reads everything in the tab and sends nothing, and if a private key is pasted by mistake it recognises it, does not read it, and clears it from the box.
Questions people ask about How to check an SSH key fingerprint, and what to compare it with
How do I find my SSH key's fingerprint?
Run ssh-keygen -lf ~/.ssh/id_ed25519.pub, using the name of your .pub file. Without a terminal, paste the public key into KEYPRINT, which shows the same SHA256 fingerprint ssh-keygen prints and the MD5 form too.
What is the difference between SHA256 and MD5 SSH fingerprints?
They are two different hashes of the same key. OpenSSH has printed SHA256, in base64, by default since version 6.8 in 2015. Before that it printed MD5 as colon-separated hex, which some systems still show. They cannot be compared with each other; use ssh-keygen -E md5 -lf key.pub to get the MD5 form.
How do I verify a server's host key fingerprint?
Get the fingerprint from a route other than the SSH connection itself: the provider's web console, the server's own setup log, the service's published list, or someone you trust reading it to you. Compare the whole string with what SSH shows on first connection before typing yes.
What does REMOTE HOST IDENTIFICATION HAS CHANGED mean?
The server presented a different host key from the one saved in your known_hosts. It happens legitimately when a server is rebuilt or its keys are rotated, and it is also what an interception looks like. Check the new fingerprint against a trusted source before removing the old entry with ssh-keygen -R hostname.
Is it safe to share my SSH public key fingerprint?
Yes. The fingerprint and the public key are both meant to be shared. Only the private key, the file without .pub, must stay secret.
Why is my DSA key rejected?
OpenSSH disabled DSA keys by default in version 7.0, in 2015. Make a new key with ssh-keygen -t ed25519, add the new public key where the old one was, and remove the old one.
Related tools
- KEYPRINTCheck an SSH key's fingerprint, type and size, and audit authorized_keys
- CERTLENSTake a certificate or a chain apart and check it
- KEYRINGMake an age key pair and encrypt files to one
- SENTINELFind keys, passwords and card numbers before you share
- SWEEPRead an Nmap scan, and see what changed since the last one
- CHECKSUMVerify a download was not tampered with