50

Is there a simple way to get a list of all fingerprints entered in the .ssh/authorized_keys || .ssh/authorized_keys2 file?

ssh-keygen -l -f .ssh/authorized_keys 

will only return fingerprint of first line / entry / publickey

hack with awk:

awk 'BEGIN { 
    while (getline < ".ssh/authorized_keys") {
        if ($1!~"ssh-(r|d)sa") {continue}
        print "Fingerprint for "$3
        system("echo " "\""$0"\"> /tmp/authorizedPublicKey.scan; \
            ssh-keygen -l -f /tmp/authorizedPublicKey.scan; \
            rm /tmp/authorizedPublicKey.scan"
        )
    }
}'

but is there an easier way or ssh command I didn't find?

2
  • To do this reliably you have to consider the options field in the authorized_keys file, which the ssh-keygen baulks at. I looked for a reliable way to parse it but the best I could come up with is covered by this answer.
    – starfry
    Feb 10, 2017 at 8:51
  • Please consider updating this question. Your command, ssh-keygen -l -f .ssh/authorized_keys, now seems to fingerprint all keys in the file, at least with my version (OpenSSH_8.4p1). No need for hacky oneliners anymore!
    – Wilmer
    May 16, 2022 at 13:37

7 Answers 7

58

Here's another hack using plain bash without temporary files:

while read l; do
  [[ -n $l && ${l###} = $l ]] && ssh-keygen -l -f /dev/stdin <<<$l;
done < .ssh/authorized_keys

You can easily make it a function in your .bashrc:

function fingerprints() {
  local file="${1:-$HOME/.ssh/authorized_keys}"
  while read l; do
    [[ -n $l && ${l###} = $l ]] && ssh-keygen -l -f /dev/stdin <<<$l
  done < "${file}"
}

and call it with:

$ fingerprints .ssh/authorized_keys
7
  • 1
    nice @Raphink , thank you. added code.childno.de/marcel/changeset/afdce0dd ;) One note: ssh-keygen -l -f /dev/stdin seems not to work on a mac.. nevertheless not relevant for servers but gnaa apple or is it a BSD "problem" getting /dev/stdin is not a public key file.?! Aug 1, 2012 at 12:16
  • 1
    Reading from /dev/stdin is not a great idea in general, it's better to use -, but for some reason ssh-keygen doesn't know about -...
    – raphink
    Aug 1, 2012 at 12:34
  • Doesn't work on Mac?
    – Will
    Jul 28, 2014 at 1:13
  • 2
    This does not work if the keys are prefixed with options.
    – starfry
    Feb 9, 2017 at 11:22
  • 1
    @ℝaphink: I'd go for local file="${1:-$HOME/.ssh/authorized_keys}" to allow for it to work without any arguments and default to the usual ~/.ssh/authorized_keys file and quote the < "$file" used as input to the while loop. Aug 9, 2017 at 12:24
11

A one-liner based on the /dev/stdin trick from ℝaphink's answer and man xargs → EXAMPLES:

egrep '^[^#]' ~/.ssh/authorized_keys | xargs -n1 -I% bash -c 'ssh-keygen -l -f /dev/stdin <<<"%"'
3
  • This works great, and given it's a one-liner, can easily be used to run the command via SSH. Thanks! Jan 11, 2018 at 13:24
  • does not work with options field present in authorized_keys
    – thomas
    Mar 17, 2020 at 12:00
  • this works for me: grep -o 'ssh-.*' ~/.ssh/authorized_keys | xargs -n1 -I% sh -c 'echo "%" | ssh-keygen -l -f /dev/stdin'
    – thomas
    Mar 17, 2020 at 12:04
8

Here's a portable way to show all key fingerprints for a given file, tested on Mac and Linux:

#!/bin/bash

fingerprint_keys()
{
    if (( $# != 1 )); then
        echo "Usage: ${FUNCNAME} <authorized keys file>" >&2
        return 1
    fi

    local file="$1"
    if [ ! -r "$file" ]; then
        echo "${FUNCNAME}: File '${file}' does not exist or isn't readable." >&2
        return 1
    fi

    # Must be declared /before/ assignment, because of bash weirdness, in
    # order to get exit code in $?.
    local TMPFILE

    TEMPFILE=$(mktemp -q -t "$0.XXXXXXXXXX")
    if (( $? != 0 )); then
        echo "${FUNCNAME}: Can't create temporary file." >&2
        return 1
    fi

    while read line; do
        # Make sure lone isn't a comment or blank.
        if [[ -n "$line" ]] && [ "${line###}" == "$line" ]; then
            # Insert key into temporary file (ignoring noclobber).
            echo "$line" >| "$TEMPFILE"

            # Fingerprint time.
            ssh-keygen -l -f "$TEMPFILE"

            # OVerwrite the file ASAP (ignoring noclobber) to not leave keys
            # sitting in temp files.
            >| "$TEMPFILE"
        fi
    done < "$file"

    rm -f "$TEMPFILE"
    if (( $? != 0 )); then
        echo "${FUNCNAME}: Failed to remove temporary file." >&2
        return 1
    fi
}

Example Usage:

bash $ fingerprint_keys ~/.ssh/authorized_keys
2048 xx:xx:xx:xx:xx:xx:xx:xx:bb:xx:xx:xx:xx:xx:xx:xx  [email protected] (RSA)
bash $ 
5
  • sorry to say that but that's neither "simplier", nor "smaller" not even "smarter" and doesn't take another approach than listed above. just a script using more error handlers ;) Jul 29, 2014 at 5:40
  • 3
    Which makes it safer, right? You're welcome to make edits but why downvote? I didn't propose that it was any kind of better solution than yours... I feel a secure tempfile is better, and that more safety is needed for scripting purposes. Also, the version above is noclobber-safe.
    – Will
    Aug 2, 2014 at 2:05
  • For a FreeBSD system (which does not use bash by default), I made the following changes: Assuming bash is installed from ports, change the first line to #!/usr/local/bin/bash. I then called the function by adding this as the last line: fingerprint_keys $@. I saved the script as fingerprints.bash, marking it executable with chmod u+x ./fingerprints.bash. Additionally, I added a comment to the file with the link to this answer, like so, near the top # solution from "Will" on SO http://serverfault.com/a/615892/126742. Call it like so ./fingerprints.bash ~/.ssh/authorized_keys.
    – derekv
    Aug 26, 2015 at 13:44
  • 1
    @derekv: the more portable method is to use the following hashbang: #!/usr/bin/env bash, because the path for env is very portable and it tells env to execute the Bash it knows about. Aug 9, 2017 at 12:21
  • The temporary file and all the machinations around it seem useless here; like most modern commands, ssh-keygen -l -f - reads from standard input just fine. Perhaps there was a time when it didn't, but then Bash provides /dev/stdin for those cases.
    – tripleee
    Dec 30, 2021 at 9:47
4
ssh-keygen -l -f - <authorized_keys

produces a nice listing for you:

# ssh-keygen -l -f - <authorized_keys
2048 SHA256:GzZ7.................................RqTEag foo (RSA)
2048 SHA256:/y0.......................................4 bar (RSA)
2048 SHA256:p.........................................k bleech (RSA)
3
  • that is already shown in the hoghest count answer? did you not read it?
    – djdomi
    Dec 31, 2021 at 21:23
  • Its a little different @djdomi in that is using stdin (the -) arg, which I was able to use like this cat ~/.ssh/authorized_keys | ssh-keygen -lf -.
    – BeeZee
    May 13, 2022 at 18:33
  • It seems that there's no need to do something different anymore though. While the OP says that command will fingerprint the first key in the file only, I've just run it now and got fingerprints for all of them: wilmer@fiona:~$ ssh-keygen -l -f .ssh/authorized_keys 2048 SHA256:DEtyZ1E+22Gsx9g/k8Wdz2vMk3JyJ9ZgCxytb4aMYSc yubikey-ws-wilmer (RSA) 2048 SHA256:oIePQpcOShOhFlCIXZZv6wUCp96iX7Pg8cdFtTSb8Co yubikey-lap-wilmer (RSA) .....
    – Wilmer
    May 16, 2022 at 13:34
1

And should anyone need to do it on Windows / in PowerShell:

gc authorized_keys | foreach {$_ |ssh-keygen -l -f -}

or the fully-blown version without aliases looking for your user profile directory:

(Get-Content ((Get-Content env:/userprofile)+"/.ssh/authorized_keys")) | foreach {$_ |ssh-keygen -l -f -}
0

This is a version of ℝaphink's answer that will work on all sh-compatible shells.

fingerprints() {
    _fingerprints_file="${1:-$HOME/.ssh/authorized_keys}"
    while read -r l; do
        [ -n "$l" ] && [ "${l###}" = "$l" ] && printf '%s\n' "$l" | ssh-keygen -l -f /dev/stdin
    done < "${_fingerprints_file}"
    unset _fingerprints_file
}
1
  • /dev/stdin is a Bashism too; it is not guaranteed to work in non-Bash shells. Some OS platforms provide it, but not all.
    – tripleee
    Dec 30, 2021 at 9:52
0

If and when reading from /dev/stdin it's an issue you may want to use process redirection without temporary files:

while read -r l; do
  [[ -n "$l" && ${l###} = "$l" ]] && ssh-keygen -l -f <(echo "$l");
done < .ssh/authorized_keys

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .