140

When connecting to a new/unknown server (with recent OpenSSH), for example:

ssh example.com

You get the fingerprint like below:

The authenticity of host 'example.org (192.0.2.42)' can't be established.
RSA key fingerprint is SHA256:7KMZvJiITZ+HbOyqjNPV5AeC5As2GSZES5baxy1NIe4.
Are you sure you want to continue connecting (yes/no)?

However, fingerprints are usually given for the server in this form:

f6:fc:1c:03:17:5f:67:4f:1f:0b:50:5a:9f:f9:30:e5

How can I check the authenticity (without bugging the server admins to provide an SHA256 fingerprint)?

1

6 Answers 6

174

Previously the fingerprint was given as a hexed md5 hash. Starting with OpenSSH 6.8 the fingerprint is now displayed as base64 SHA256 (by default). You can't compare these directly.

They also added a new configuration option FingerprintHash. You can put

FingerprintHash md5

in your ~/.ssh/config to revert to the old (less secure) default or just use this option for single use:

ssh -o FingerprintHash=md5 example.org

which would give the fingerprint as

MD5:f6:fc:1c:03:17:5f:67:4f:1f:0b:50:5a:9f:f9:30:e5

Hopefully server admins provide both types of fingerprints in the near future.

EDIT:

As given in the Arch Linux forums, there is also a third option:

Host example.org
    FingerprintHash md5

EDIT:

You can generate the hash of an arbitrary key as given on the OpenSSH Cookbook:

Retrieve the key:

  • download the key with ssh-keyscan example.org > key.pub
  • or: find the keys on the server in /etc/ssh

Generate the hash:

  • make sure you have only one line/type, so either delete all others in key.pub or run ssh-keyscan -t rsa example.org > key.pub
  • ssh-keygen -l -f key.pub (default hash, depending on OpenSSH version)
  • ssh-keygen -l -f key.pub -E md5 (md5 on current OpenSSH)
  • awk '{print $2}' ssh_host_rsa_key.pub | base64 -d | sha256sum -b | awk '{print $1}' | xxd -r -p | base64 (sha256 on old OpenSSH)
  • (You might need to start the line with awk '{print $3}' for newer versions of ssh-keyscan because the format changed)
8
  • 4
    do you happen to know how I can get the sha256 fingerprint on the server in case I have access there? ssh-keygen -lf on the server only gives me the md5 print, and for the life of me I can't find how to get the sha256 one either in the man pages or the www so far...
    – codeling
    Nov 12, 2015 at 21:09
  • 6
    I added a snipped from the OpenSSH Cookbook using sha256sum to the answer.
    – JonnyJD
    Nov 13, 2015 at 0:33
  • 2
    thank you, you're the only one to give an answer that has actual value. Apr 12, 2016 at 12:39
  • 4
    sha256sum -b | awk '{print $1}' | xxd -r -p can be replaced by openssl sha256 -binary not requiring vim installed. Resulting commandline would be: awk '{print $2}' ~/.ssh/id_rsa.pub | base64 -d | openssl sha256 -binary | base64 May 28, 2019 at 13:29
  • 2
    @JonathanCross Note that base64 utility would produce padded output (i.e. possibly ending with one or two equals signs), while openssh client does not pad base64 in it's log. May 28, 2019 at 13:31
32

Just created small bash script which will print table with fingerprints for all key ciphers allowed on server (according to /etc/ssh/sshd_config) in both SSH-256 and MD5 algo. Here is an example output:

❯ ssh-fingerprints
 +---------+------------------------------------------------------+
 | Cipher  | Algo and Fingerprint                                 |
 +---------+------------------------------------------------------+
 | RSA     | MD5:65:cc:63:49:ac:d6:a6:a8:5c:ab:58:12:f6:84:a4:75  |
 | RSA     | SHA-256:jlDPKCCRr1TkufVsZJf02ejXNQ7RB/vg09uGwKeSwnU  |
 +---------+------------------------------------------------------+
 | ECDSA   | MD5:fc:fa:b1:4a:6a:4f:4e:15:24:a0:28:14:d8:13:f2:58  |
 | ECDSA   | SHA-256:XqtbaJcdqem6s/R+T9NpXA7QKCTyPfzxC3f+2O/vfmY  |
 +---------+------------------------------------------------------+
 | ED25519 | MD5:03:1e:8e:0e:a8:4a:08:7a:49:35:af:2f:99:b8:9c:5b  |
 | ED25519 | SHA-256:HV5r6SFytqauiUrWcXd3zDGMzCYHj6RR6tKj0S0UhFI  |
 +---------+------------------------------------------------------+

 +---[RSA 3072]----+ +---[RSA 3072]----+ +---[ECDSA 256]---+ +---[ECDSA 256]---+ +--[ED25519 256]--+ +--[ED25519 256]--+
 |         ..      | |....     .   .o. | |o+E.  ... .      | |      . . .      | |                 | |     .Eo+.+      |
 |         +..     | |.....   . o + .o.| |o=o. .   o       | |       o =   o   | |                 | |    . .  * * o   |
 |  o E    oO      | |..oo ..o o + oo.o| |o o..     .      | |        . = + .  | |    o o          | |     .  + + O +  |
 | + o    o+o.     | | o..o.=+. .. .++o| | .     .   .     | |           + =   | | . . * o         | |       = + * = o |
 |. + .  oSo       | |.. .o .+S ..E.+oo| |        S .      | |        S . = + o| |o o . + S        | |    . + S O * +  |
 | . +  o .        | |.    o.+ . o.+ oo| |         o       | |       . o * +.Bo| |=. + .   .       | |   . o = B * o   |
 |  ..oo .         | |      . + o . ..o| |        + o      | |        = B . +++| |+.o.Eo           | |  o . .   o .    |
 |   +o .          | |         . .   . | |      .* . o     | |       o.*  ...+E| |oo.o+.           | | o   . . o       |
 |  . ..           | |                 | |     ...=oo      | |        *B+. .o*%| |. =o ..          | |  ... ooo        |
 +------[MD5]------+ +----[SHA256]-----+ +------[MD5]------+ +----[SHA256]-----+ +------[MD5]------+ +----[SHA256]-----+

Script will run also on servers with SSH version below 6.8 (before -E md5 option was added).

Edit: Updated versions for even more recent versions of SSH which switched default ciphers now with ASCII images support.

Edit: Merged Algo and Fingerprint to one column so it is easier to Copy&Paste verification available in recent SSH clients.

You can get recent version of this script at: https://github.com/kepi/ssh-fingerprints

#! /bin/bash
#
# ssh-fingerprints
#
# Source: https://github.com/kepi/ssh-fingerprints
#
# MIT License
# Copyright 2018 Ondra Kudlík (Kepi) <kepi@igloonet.cz>
#
set -eu

# standard sshd config path
readonly SSHD_CONFIG=/etc/ssh/sshd_config

readonly LINE=" +---------+------------------------------------------------------+"


# Warning: don't use in a pipe, because a pipe runs in a
# subshell and thus would throw away changes to global variables
function parse_fp {
    local algo=$1 n=0 filter line

    if [ "${2:-}" = old ] ; then
        # Older OpenSSH versions don't include the hash algorithm prefix
        filter="s/^\\([^ =]*\\).*/\\1/"
    else
        # Newer OpenSSH versions do include the hash algorithm prefix; remove
        filter="s/^${algo^^}:\\([^ =]*\\).*/\\1/"
    fi

    while read -r line
    do
        n=$((n + 1))
        if [[ $n -eq 1 ]] ; then
            ALGOS[$algo]=$(echo "$line" | sed "$filter")
        else
            # Alter old versions' ASCII art box to include algorithm
            if [ "$line" = "+-----------------+" ] ; then
                case $algo in
                    md5)    line="+------[MD5]------+" ;;
                    sha256) line="+----[SHA256]-----+" ;;
                esac
            fi

            ASCII[$n]="${ASCII[$n]:-} ${line}"
        fi
    done
}        


# *** MAINLINE ***
# header
echo "$LINE"
printf " | %-7s | %-52s |\n" "Cipher" "Algo and Fingerprint"
echo "$LINE"

declare -A ALGOS
declare -a ASCII

# fingerprints
hostkey_files=$(awk '/^HostKey/ { print $2 ".pub" }' $SSHD_CONFIG)
if [ -z "$hostkey_files" ] ; then
    # If HostKey not set in $SSHD_CONFIG, use the default
    hostkey_files='/etc/ssh/ssh_host_rsa_key.pub
                   /etc/ssh/ssh_host_ecdsa_key.pub
                   /etc/ssh/ssh_host_ed25519_key.pub'
fi
# (Fake piping into while loop by using a redirect, because a pipe runs in a
# subshell and thus would throw away changes to global variables)
while read -r host_key
do
    cipher=$(echo "$host_key" |
               sed -r 's/^.*ssh_host_([^_]+)_key\.pub$/\1/' |
               tr '[:lower:]' '[:upper:]')
    if [ "$cipher" = DSA ] ; then
        continue
    fi

    if [[ -f "$host_key" ]] ; then
        if ssh-keygen -E md5 -l -f "$host_key" &>/dev/null
        then
            for algo in md5 sha256 ; do
                parse_fp $algo < <(ssh-keygen -E $algo -lv -f "$host_key" |
                                       sed '/^[0-9]/ s/^[^ ]* \([^ ]*\).*/\1/')
            done
        else
            parse_fp md5 old < <(ssh-keygen -lv -f "$host_key" |
                                     sed '/^[0-9]/ s/^[^ ]* \([^ ]*\).*/\1/')
            # Note: no ASCII art
            # TO-DO: Use https://github.com/atoponce/keyart Python script
            parse_fp sha256 old < <(cut -f2 -d' ' "$host_key" |
                                        base64 -d |
                                        openssl dgst -sha256 -binary |
                                        base64)
        fi

        printf " | %-7s | %-52s |\n" "$cipher" "MD5:${ALGOS[md5]}"
        printf " | %-7s | %-52s |\n" "$cipher" "SHA256:${ALGOS[sha256]}"
        echo "$LINE"
    fi
done < <(echo "$hostkey_files")

echo
for line in "${ASCII[@]}"; do
    echo "$line"
done

This is just pretty print using information from JonnyJD's answer. Thanks.

3
  • 3
    I made it more robust and improved functionality slightly; version 0.3 (with changelog) here: github.com/unixnut/scripts/blob/master/ssh_fprint Thanks for writing this, it is awesome! Apr 29, 2018 at 14:57
  • @kepi thanks for your script, I used it a couple of times in the past but it's not working anymore so I did my own version and I posted it as a new answer (check it out, probably below...).
    – loco.loop
    Mar 14, 2020 at 22:02
  • @loco.loop updated version should work without any problem on both older and new system. If not, it would be great if you can open new issue at github.com/kepi/ssh-fingerprints with info about your system, server ssh_config and public keys.
    – Kepi
    Sep 10 at 17:41
9

it turns out ssh-keygen (sometime after version 6.6; presumably 6.8) has a -E md5 option which will cause it to print out the fingerprint as an md5 fingerprint. So, if you can independently grab the public key file of the server, you can feed it to ssh-keygen -E md5 -l -f ssh_host_rsa_key.pub and get your familiar fingerprint.

1
  • 2
    all of that is already in the previous answer well formatted.
    – Jakuje
    Jan 8, 2016 at 16:25
3

The following one-liner works (at least) on Ubuntu 16.04,18.04 / Centos >= 7

(Tested with servers: openssh 3.9 - openssh 7.4)

ssh-keygen -l -E md5 -f <(ssh-keyscan -t rsa 192.168.2.1) \
| awk '{print $2}' | cut -d":" -f 2-

Output:

# 192.168.2.1:22 SSH-2.0-OpenSSH_5.3
65:2c:c4:03:03:2f:6d:c0:c0:09:79:e7:33:c4:f1:78
2

I liked Kepi's idea but unfortunately that's not working for me anymore so I reviews the Script and made it work, also I simplified it and removed the ASCII art as I don't need that.

To run my script directly run:

curl -sS https://raw.githubusercontent.com/caballerofelipe/scripts/master/ssh/ssh_fingerprint.sh | bash

Here's my script (might change in the future in GitHub):

#!/bin/bash

# To run this file directly from a computer without downloading run this:
# curl -sS https://raw.githubusercontent.com/caballerofelipe/scripts/master/ssh/ssh_fingerprint.sh | bash

# Used to show SSH fingerprints
# Inspired by this post: http://superuser.com/a/1030779/369045

# standard sshd config path
SSH_DIR=/etc/ssh/

# Helper functions
function tablize {
    printf "| %-7s | %-7s | %-47s |\n" $1 $2 $3
}
LINE="+---------+---------+-------------------------------------------------+"

# Header
echo $LINE
tablize "Cipher" "Algo" "Fingerprint"
echo $LINE

# Fingerprints
for i in $(ls $SSH_DIR/*.pub); do
    md5_result=$(ssh-keygen -l -f $i -E md5)
    sha256_result=$(ssh-keygen -l -f $i -E sha256)
    cipher=$(echo $md5_result | sed 's/.*(//' | sed 's/)[^)]*//')
    md5=$(echo $md5_result | awk '{print $2}' | sed 's/^[^:]*://g')
    sha256=$(echo $sha256_result | awk '{print $2}' | sed 's/^[^:]*://g')
    tablize $cipher MD5 $md5
    tablize $cipher SHA-256 $sha256
    echo $LINE
done
1

tried to modify to get also random art into table:

# standard sshd config path
SSHD_CONFIG=/etc/ssh/sshd_config

# helper functions
function tablize {
        awk '{printf("| %-7s | %-51s |\n", $1, $3)}'
}
LINE="+---------+-----------------------------------------------------+"

# header
echo $LINE
echo "Cipher" "Fingerprint" "Fingerprint" | tablize
echo $LINE

# fingerprints
for host_key in $(awk '/^HostKey/ {sub(/^HostKey\s+/,"");print $0".pub"};' $SSHD_CONFIG); do
        cipher=$(echo $host_key | sed -r 's/^.*ssh_host_([^_]+)_key\.pub$/\1/'| tr '[a-z]' '[A-Z]')
        if [[ -f "$host_key" ]]; then
                md5=$(ssh-keygen -l -f $host_key -E md5 | awk '{print $2}')
                sha256=$(ssh-keygen -l -f $host_key | awk '{print $2}')
                art_sha256=$(ssh-keygen -lv -f $host_key | grep -v -w "256 SHA256" | grep -v -w "1024 SHA256" | grep -v -w "2048 SHA256")
                art_md5=$(ssh-keygen -lv -f $host_key -E md5 | grep -v "256 MD5" | grep -v "1024 MD5" | grep -v "2048 MD5")

                echo $cipher MD5 $md5 | tablize
                echo $cipher SHA-256 $sha256 | tablize
                echo $art_sha256 | tablize
                echo $art_md5 | tablize
                echo $LINE
        fi
done

... but I'm not a programmer actually and the script does not work as expected. Would appreciate if anyone can help to fix (also cleanup). There would be nice to have sha256 and md5 random art images side by side to use space more efficiently. I modified also md5 and sha256 commands as the original ones did not work for me (probably too new system) - only sha256 appreared into table with "=" sign in the end which was not part of actual fingerprint and could not remove it.

(sorry I could not comment as I registered recently)

3
  • If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. Jul 3, 2017 at 0:18
  • Well - as you see - it is a not new question but actually admitting that proposed script did not work as expected and a new and still not yet complete version has been proposed. I could modify existing script so it works as expected but I tried to add the random art and this part id not complete. Actually there is one proposal how to compare certain public key and offered key but this variant is not MITM-proof: ssh-keyscan -t ecdsa xxx.xxx.xxx.xxx | ssh-keygen -lv -f - && ssh -X -o VisualHostKey=yes -i ~/.ssh/key user@xxx.xxx.xxx.xxx
    – zeroconf
    Jul 3, 2017 at 17:36
  • I didn't see your comments but today got same idea, so my answer is now updated with working version for more recent OpenSSH versions, ASCII art included.
    – Kepi
    Mar 11, 2018 at 19:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.