Scott
Scott

Reputation: 694

How can I get an MD5 fingerprint (signature) of an SSH public key from a string

Is there a way to get an MD5 fingerprint (signature) of a public key if I have a string of that key rather than a file?

ssh-keygen -l -E md5 -f "path/to/file"

This command will give me (among other things) the MD5 fingerprint (signature) of the key. I have read the man page for the ssh-keygen command and experimented in my shell, but I have not been able to get it to work on a string rather than a file. I can found no argument that takes a string, and I have tried piping in the string and also using STDIN to supply the string to ssh-keygen - all without success.

I have tried using Ruby and Digest::MD5.hexdigest on the string, but either I am not supplying the correct string or I need to be using some other hash mechanism because I've had no luck with that either. (I've tried various substrings of the key)

I could, of course, write the string to a temp file and then use ssh-keygen on that file, but it just seems like I shouldn't have to.

I would ultimately like to solve this problem in Ruby, but I can start with a unix utility or bash solution. If need be I can always execute the shell command from Ruby.

If it matters, I am running a bash (GNU bash, version 3.2.57(1)) shell on macOS Mojave (10.14.6) and Ruby 2.6.4

Edit: I changed the term from signature to fingerprint but left signature in parentheses. I have seen both terms used, but I believe fingerprint is the more common.

Upvotes: 1

Views: 14613

Answers (3)

Matt
Matt

Reputation: 49

Using Python:

import codecs, hashlib
pubkey = "AAAA...."  #Put you public key string here
fingerprint = hashlib.md5(codecs.decode(bytes(pubkey,'utf-8'), 'base64')).hexdigest()
print(fingerprint)

If you need a colon separated fingerprint, try:

cs_fingerprint = ":".join([ fingerprint[pair:pair+2] for pair in range(0, len(fingerprint)-1, 2) ])
print(cs_fingerprint)

Upvotes: 0

Léa Gris
Léa Gris

Reputation: 19545

Use - as filename to read stdin as file. Then feed stdin with a Bash's <<<"herestring"

With Bash:

#!/usr/bin/env bash

# Fill a keystring variable for demo purpose
IFS= read -r -d '' keystring <"$HOME/.ssh/id_rsa.pub"


# Get ssh-keygen to read from the keystring rather than a file
key_md5="$(ssh-keygen -l -E md5 -f - <<<"$keystring" | cut -d ' ' -f2)" 

# Isolate and reformat the md5
# Remove the MD5: prefix
key_md5="${key_md5##MD5:}"

# Remove the in-between :
key_md5="${key_md5//:}"

# Print the md5 for testing
echo "$key_md5"

Or with a POSIX shell:

#!/usr/bin/env sh

# Fill a keystring variable for demo purpose
IFS= read -r keystring <"$HOME/.ssh/id_rsa.pub"

# Get ssh-keygen to read from the keystring rather than a file
# and reformat the md5 sum with sed POSIX Extended Regex
# sed commands:
# s/://g deletes all colon characters
# s/^[[:digit:]]\+[[:space:]]\+MD5\([[:xdigit:]]\{32\}\).*/\1/
# starting with digits followed by spaces, followed by MD5
# capture the group of 32 hexadecimal digits from the md5 sum
key_md5="$(
  echo "$keystring" \
    | ssh-keygen -l -E md5 -f - \
    | sed 's/://g;s/^[[:digit:]]\+[[:space:]]\+MD5\([[:xdigit:]]\{32\}\).*/\1/'
)"

# Print the md5 for testing
echo "$key_md5"

Alternatively, you can use the base64 command to decode the key, and the md5sum command to compute the sum in shell like this:

#!/usr/bin/env sh

# Fill a keystring variable for demo purpose
IFS= read -r keystring <"$HOME/.ssh/id_rsa.pub"

# Inject this sub-shell commands result into the arguments
set -- "$(

  # Pipe in the ssh key string
  echo "$keystring" |

  # Get the second field containing the base64 encoded key
  cut -d ' ' -f2 |

  # Decode the key from its base64
  base64 -d |

  # Compute the md5 sum of the key
  md5sum |

  # Get the first field containing the md5
  cut -d ' ' -f1
)"

# The key's md5 has been returned in the first argument
key_md5="$1"

# Print the md5 for testing
echo "$key_md5"

Upvotes: 3

Scott
Scott

Reputation: 694

I decided to search for a Ruby Gem that solves the problem. I found this one: https://github.com/bensie/sshkey. Digging around the source code, I discovered that I need to Base64 decode the key part of the string, and then do a Digest::MD5.hexdigest on that to get the fingerprint of the key.

string = "ssh-rsa aabbccddqq== comment goes here" # not a real key
key = string.split(" ")[1]
fingerprint = Digest::MD5.hexdigest(Base64.decode64(key))

I used the term "signature" in the original question, I have edited that question to add "fingerprint" as an alternate term

Upvotes: 4

Related Questions