Michael Gorsuch
Michael Gorsuch

Reputation: 847

Deriving an SSH Fingerprint from a Public Key in Python

I'm trying to understand the steps to take an OpenSSH public key like so:

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqmEmDTNBC6O8HGCdu0MZ9zLCivDsYSttrrmlq87/YsEBpvwUTiF3UEQuFLaq5Gm+dtgxJewg/UwsZrDFxzpQhCHB6VmqrbKN2hEIkk/HJvCnAmR1ehXv8n2BWw3Jlw7Z+VgWwXAH50f2HWYqTaE4qP4Dxc4RlElxgNmlDPGXw/dYBvChYBG/RvIiTz1L+pYzPD4JR54IMmTOwjcGIJl7nk1VjKvl3D8Wgp6qejv4MfZ7Htdc99SUKcKWAeHYsjPXosSk3GlwKiS/sZi51Yca394GE7T4hZu6HTaXeZoD8+IZ7AijYn89H7EPjuu0iCAa/cjVzBsFHGszQYG+U5KfIw==

And then to convert it into an standard fingerprint like so:

2048 49:d3:cb:f6:00:d2:93:43:a6:27:07:ca:12:fd:5d:98 id_rsa.pub (RSA)

I have attempted to dive into the OpenSSH source to understand this, but it is over my head. My first guess was to do a simple MD5 on the key text, but the result does not match the above output.

Upvotes: 22

Views: 13051

Answers (3)

micahbf
micahbf

Reputation: 627

Here's a solution if you have a public key object using the cryptography library, that gives a SHA256 fingerprint (the new preferred choice.)

It took a lot of troubleshooting to find the right set of bytes to fingerprint to match the fingerprint given by ssh-keygen -l. You have to first format the public key in OpenSSH style, then decode the base 64 encoded bytes from that formatted style, and hash those - just hashing the raw bytes won't work.

import base64

from cryptography.hazmat.primitives import hashes, serialization


def get_public_key_fingerprint(public_key):
    formatted = public_key.public_bytes(
        encoding=serialization.Encoding.OpenSSH,
        format=serialization.PublicFormat.OpenSSH,
    )
    parts = formatted.split(b" ")
    key_bytes = base64.b64decode(parts[1])

    digest = hashes.Hash(hashes.SHA256())
    digest.update(key_bytes)
    fingerprint = base64.b64encode(digest.finalize()).rstrip(b"=").decode("utf-8")

    return f"SHA256:{fingerprint}"

Upvotes: 0

Mihai Ftr.
Mihai Ftr.

Reputation: 51

https://github.com/ojarva/sshpubkeys

pip install sshpubkeys

Usage:

import sshpubkeys
key = sshpubkeys.SSHKey("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqmEmDTNBC6O8H" +
    "GCdu0MZ9zLCivDsYSttrrmlq87/YsEBpvwUTiF3UEQuFLaq5Gm+dtgxJewg/UwsZrDFxz" +
    "pQhCHB6VmqrbKN2hEIkk/HJvCnAmR1ehXv8n2BWw3Jlw7Z+VgWwXAH50f2HWYqTaE4qP4" +
    "Dxc4RlElxgNmlDPGXw/dYBvChYBG/RvIiTz1L+pYzPD4JR54IMmTOwjcGIJl7nk1VjKvl" +
    "3D8Wgp6qejv4MfZ7Htdc99SUKcKWAeHYsjPXosSk3GlwKiS/sZi51Yca394GE7T4hZu6H" +
    "TaXeZoD8+IZ7AijYn89H7EPjuu0iCAa/cjVzBsFHGszQYG+U5KfIw== user@host")
print(key.bits)   # 2048
print(key.hash()) # '49:d3:cb:f6:00:d2:93:43:a6:27:07:ca:12:fd:5d:98'

Upvotes: 5

phihag
phihag

Reputation: 287735

It is the MD5 sum of the base64-encoded key:

import base64
import hashlib

def lineToFingerprint(line):
    key = base64.b64decode(line.strip().split()[1].encode('ascii'))
    fp_plain = hashlib.md5(key).hexdigest()
    return ':'.join(a+b for a,b in zip(fp_plain[::2], fp_plain[1::2]))

Upvotes: 49

Related Questions