0

With this command it is possible to generate an RSA public-private key pair:

ssh-keygen -f key

Now I would like to load these keys in Python using module cryptography. Example:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

with open("key.pub", "rb") as f:
    datapub = f.read()

public_key = serialization.load_ssh_public_key(datapub, backend=default_backend())

But now: How do you generate a fingerprint from this public key? With OpenSSH this can be done using ssh-keygen -lf key.pub. But how do you the same in Python?

Regis May
  • 3,070
  • 2
  • 30
  • 51
  • 1
    AFAIK no fingerprint is associated with an `RSAPublicKey` instance like `public_key`. A fingerprint is defined in the context of a format, e.g. _OpenSSH_. Here the [fingerprint](https://stackoverflow.com/a/9607373) is the SHA256 hash of the Base64 decoded public key. One way of its determination would be to serialize the `RSAPublicKey` instance into the _OpenSSH_ format, e.g. with `public_key.public_bytes(...)` (which of course results in `datapub ` for the posted example) and determine the corresponding hash, e.g. [with](https://stackoverflow.com/a/6682934) (but using SHA256). – Topaco Nov 08 '20 at 10:46

1 Answers1

0

First of all: Thank you very much Topaco, you provided the essential hint to me how to generate such a fingerprint. I crosschecked this with other sources on the WWW and can no provide code for anybody to use.

Let's assume we've loaded the key and stored it in public_key. Then a fingerprint can be generated as follows:

rawKeyData = public_key.public_bytes(
    encoding=serialization.Encoding.OpenSSH,
    format=serialization.PublicFormat.OpenSSH,
)

# prepare for hashing
m = re.match("ssh-rsa ([A-Za-z0-9+/=]+)", rawKeyData.decode("utf-8"))
assert m is not None
base64BinaryData = m.group(1).encode("utf-8")
rawBinaryData = base64.b64decode(base64BinaryData)

# now create the hash
hexDigest = hashlib.md5(rawBinaryData).hexdigest()

# formatting for output similar to "ssh-keygen -l -E md5 -f <pubkeyfile>"
chunks = [ hexDigest[i:i+2] for i in range(0, len(hexDigest), 2) ]
fingerprint = str(self.__public_key.key_size) + "MD5:" + ":".join(chunks) + " (RSA)"

This provides output like this:

2048 MD5:bd:5a:67:a3:4c:46:9d:2c:63:78:7e:68:bc:82:eb:23 (RSA)

The only difference to OpenSSH fingerprints: Here no email address is included in the output.

Some remarks:

  • regex
    • I use a regular expression here to parse the output. This is done for safety as this way I ensure that the output matches the expectations of data processing here.
  • base64
    • base64 might add padding to the data.
    • base64 is safe to use as padding is deterministic.
  • md5
    • Here the MD5 output is used.
    • You can safely replace MD5 by any other hash algorithm if you want - e.g. SHA256.
Regis May
  • 3,070
  • 2
  • 30
  • 51