SSH public key verification with FingerprintHash

Whenever I'm connecting to a new remote server via SSH, I tend to verify the fingerprint to make sure that I'm actually connecting to my own machine. Usually it's not that big a deal as I'm simply comparing two strings, but what if those two strings are created with two different hashing algorithms?

This is what I saw today when I connected to a new server for the first time.

notebook $ ssh remoteserver.name
The authenticity of host 'remoteserver.name' can't be established.
ECDSA key fingerprint is SHA256:2weq/LEmJ77j6sJUV6krRPnA9KUqgoojH+uM0hOcOJY.
Are you sure you want to continue connecting (yes/no)?

Ok, instead of just typing yes, let's verify that real quick. To do that, I ssh'd into the hypervisor and connected to the server via direct console. Now there are three ways I know of, how to verify the key, all of which boil down to string comparison. The question is, how to generate the string.

The easiest way is to ssh into localhost

server $ ssh localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is 9b:1f:c9:df:e6:58:59:45:5d:5b:c0:3a:e4:e0:11:9c.
Are you sure you want to continue connecting (yes/no)?

Shoot, that's a problem. My notebook generated an SHA256+Base64 fingerprint, which is the default nowadays, while the ssh client on the server (all versions prior to 6.8) uses the old MD5 algorithm.

The reason for this is the difference in the ssh-client versions.

notebook $ ssh -V
OpenSSH_6.9p1 Ubuntu-2, OpenSSL 1.0.2d 9 Jul 2015

server $ ssh -V
OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.3, OpenSSL 1.0.1f 6 Jan 2014

Both machines have all updates installed, so the versions are up to date on their respective distros. One way to bypass this is by generating the fingerprint with ssh-keygen

server $ ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key
256 9b:1f:c9:df:e6:58:59:45:5d:5b:c0:3a:e4:e0:11:9c  root@remoteserver.name (ECDSA)

Same result, let's try specifying the FingerprintHash option, as suggested in this book.

server $ ssh -o FingerprintHash=sha256 localhost
command-line: line 0: Bad configuration option: fingerprinthash

Hm, the ssh client version on the server doesn't seem to support it. How about the -E option for ssh-keygen?

$ ssh-keygen -E sha256 -lf /etc/ssh/ssh_host_ecdsa_key.pub
unknown option -- E

Ok, so that didn't work either, not even on my notebook. Luckily there is a way to generate the key fingerprint manually.

server $ awk '{print $2}' /etc/ssh/ssh_host_ecdsa_key.pub | base64 -d | sha256sum -b | sed 's/ .*$//' | xxd -r -p | base64
2weq/LEmJ77j6sJUV6krRPnA9KUqgoojH+uM0hOcOJY=

notebook $ ssh remoteserver.name
ECDSA key fingerprint is SHA256:2weq/LEmJ77j6sJUV6krRPnA9KUqgoojH+uM0hOcOJY.

Looks like the same, however that's not really a convenient way to do things. There is one other way I can think of. Remember the FingerprintHash function I tried earlier? We can use it on the newer client to downgrade the fingerprinting to md5 and compare that with the servers key-gen result.

notebook $ ssh -o FingerprintHash=md5 remoteserver.name
ECDSA key fingerprint is MD5:9b:1f:c9:df:e6:58:59:45:5d:5b:c0:3a:e4:e0:11:9c.

server $ ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub 
256 9b:1f:c9:df:e6:58:59:45:5d:5b:c0:3a:e4:e0:11:9c  root@remoteserver.name (ECDSA)

That's more like it, even though I'd prefer to use the newer SHA256+base64. If you don't want to compare strings character by character, then you might like the following alternative way.

$ ssh-keyscan remoteserver.name
remoteserver.name ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD4T41P6Byl4Qj2d/pzTJbiYs6Ld23KiSbjjGnZ1VUCQaIKFRa1dYGbxHe7v5JKLnfzmkVdTDxwHfbZWIomOFs6qFCoCXU4XsNwiwfDKjy2MZYN0+OJ2lq5hGqCDv66X4vG8PGgx4Vf0/iGIsILSoib8rPdX0jcjgtu4IeaPDt/sqgc09mVJzwVKnp37rlSTGSz2tRvyAo9koIWB+DOTUtq3QGbeQvyaY8pJIapi4BmvMznTt2vtXo15G3MFrT3dA9HIurtE8uX9ohVXL52GhAMm35GSoIwGY5mvp3QbNdG15bm2Rwn/4689gC6dZ2lkb+puOMV5OTTfURsj3q9Mjdd
remoteserver.name ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG51NabehVOdZ/5G18erFMrtV54OQ8T+R6dShAINsfQPI7cOVaGxTBiOGWojfQfnOOgY32UzBxosBu3vKtLfxnM=
remoteserver.name ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG/XXHwyejGBMxCqVyiWR6Z4KbwqOlj+p9SEuxrqXOkp

Copy the output into a file on the remote server and run the following command to generate a fingerprint image.

$ ssh-keygen -lvf remoteserver_keys.pub 
2048 a9:8b:d6:46:cd:44:7b:36:ba:b7:0a:4c:c4:6a:a7:75 remoteserver.name (RSA)
+--[ RSA 2048]----+
|                 |
|     .  .        |
|      o. .       |
|     o  o.+      |
|    o ++E+ .     |
|   . *.o+        |
|    .o+  .       |
|    ..oo. .      |
|   .... .o..     |
+-----------------+
256 9b:1f:c9:df:e6:58:59:45:5d:5b:c0:3a:e4:e0:11:9c remoteserver.name (ECDSA)
+--[ECDSA  256]---+
|         ..o ...*|
|          E . ..+|
|         . = . ..|
|          . +   .|
|        S    .  .|
|         + .   o |
|        o +   o  |
|         . o +.  |
|          . ooo  |
+-----------------+
256 9e:a9:07:82:ad:4a:12:4b:b8:f6:fd:de:4a:05:80:d5 remoteserver.name (ED25519)
+--[ED25519  256--+
|    oo.          |
|   .  .E         |
|       .         |
|.       .        |
|o. o    S.       |
|.+. o ...o       |
|+o . . o+        |
|+ o . ..o        |
|.. . .+=..       |
+-----------------+

Then run the same key-gen command on the servers public key and compare the result with the images above. One should match.

$ ssh-keygen -lvf /etc/ssh/ssh_host_ecdsa_key.pub 
256 9b:1f:c9:df:e6:58:59:45:5d:5b:c0:3a:e4:e0:11:9c  root@remoteserver.name (ECDSA)
+--[ECDSA  256]---+
|         ..o ...*|
|          E . ..+|
|         . = . ..|
|          . +   .|
|        S    .  .|
|         + .   o |
|        o +   o  |
|         . o +.  |
|          . ooo  |
+-----------------+

While this is more work than the -o FingerprintHash=md5 solution, it's a bit easier to compare the results.