Reputation: 694
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
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
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
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