SHA256 ssh fingerprint given by the client but only md5 fingerprint known for server

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)?


Solution 1:

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)

Solution 2:

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:

 +---------+---------+-----------------------------------------------------+
 | Cipher  | Algo    | Fingerprint                                         |
 +---------+---------+-----------------------------------------------------+
 | RSA     | MD5     | MD5:15:66:80:fd:79:d8:c0:92:e8:39:4a:bc:4e:c4:00:15 |
 | RSA     | SHA-256 | SHA256:G+rKuLGk+8Z1oxUV3cox0baNsH0qGQWm/saWPr4qZMM  |
 +---------+---------+-----------------------------------------------------+
 | ECDSA   | MD5     | MD5:f5:90:5c:03:2e:38:1b:c9:86:bd:86:47:5d:22:79:17 |
 | ECDSA   | SHA-256 | SHA256:GGmuOzsG4EGeRV9KD1WK7tRf3nIc40k/5jRgbTZDpTo  |
 +---------+---------+-----------------------------------------------------+
 | ED25519 | MD5     | MD5:d1:5a:04:56:37:f4:75:19:22:e6:e5:d7:41:fd:79:fa |
 | ED25519 | SHA-256 | SHA256:QVdqYeVzvsP4n5yjuN3D2fu8hDhskOxQCQAV5f9QK7w  |
 +---------+---------+-----------------------------------------------------+

 +---[RSA 2048]----+ +---[RSA 2048]----+ +---[ECDSA 256]---+ +---[ECDSA 256]---+ +--[ED25519 256]--+ +--[ED25519 256]--+
 |.oE.   +.++      | |        .o+=     | |      ... Eoo    | |  .. o.o..      .| |        ooo++o.+*| |   .o++o. +oo    |
 |  .   o +oo.     | |        .o= =    | |     +.=.=.o .   | | . .o *..      ..| |       . oo.+o.o=| |      ...o.+     |
 |   + . . o.=     | |       ... X +   | |    . X.o.*      | |o o  ++o      .. | |        . o. ...+| |       ..oo.o .  |
 |    = +  .+ o    | |      ..  = + o  | |     + = o o     | |.+  .o.o     .o  | |         +    ..o| |        =oo .+   |
 |   o o .S  .     | |     . .S  o o   | |    . = S   .    | |...  oo.S  .E* * | |        S      ..| |       .SO .  .  |
 |    +            | |    . E. =o..    | |     o           | | .. o.  . o *.O o| |              .  | |        o B .o.. |
 |   o             | | .o. *.o. *.     | |                 | |   ...   . o * * | |               . | |         E *..=.o|
 |    .            | | oo=...  +.      | |                 | |    +.      o + o| |                E| |          . +.+B+|
 |                 | |o+=.o....o+o     | |                 | |   .o+         . | |                 | |           o.ooOX|
 +------[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.

#!/bin/bash
# server_ssh_fingerprints
#
# Version 0.2
#
# 2016 Kepi <[email protected]
# MIT License
#
# Print fingerprint matrix for all allowed Host Keys
# with all allowed ciphers and MD5 and SHA-256 algos
#
# Changelog:
#
# 2018-03-11 (0.2):
# - Support for newer versions of OpenSSH
# - Added visual keys for all algos too - only in recent OpenSSH versions

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

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

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

declare -A ALGOS
declare -a ASCII

# fingerprints
while read -r host_key; do
    cipher=$(echo "$host_key" | sed -r 's/^.*ssh_host_([^_]+)_key\.pub$/\1/'| tr 'a-z' 'A-Z')
    if [[ -f "$host_key" ]]; then
        if ssh-keygen -E md5 -l -f "$host_key" &>/dev/null; then
        IFS=$'\n'

        for algo in md5 sha256; do
            n=0
            for line in $(ssh-keygen -E $algo -lv -f "$host_key"); do
                n=$(( n + 1))
                if [[ $n -eq 1 ]]; then
                    ALGOS[$algo]=$(echo "$line" | awk '{print $2}')
                else
                    ASCII[$n]="${ASCII[$n]} ${line}"
                fi
            done
        done
        else
            ALGOS[md5]=$(ssh-keygen -l -f "$host_key" | awk '{print $2}')
            ALGOS[sha256]=$(awk '{print $2}' "$host_key" | base64 -d | sha256sum -b | awk '{print $1}' | xxd -r -p | base64)
        fi

        echo "$cipher" MD5 "${ALGOS[md5]}" | tablize
        echo "$cipher" SHA-256 "${ALGOS[sha256]}" | tablize
        echo "$LINE"
    fi
 done < <(awk '/^HostKey/ {sub(/^HostKey\s+/,"");print $0".pub"};' $SSHD_CONFIG)

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

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

Solution 3:

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.

Solution 4:

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

Solution 5:

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)