54

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?

Will
  • 1,157
  • 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 '17 at 08:51
  • 1
    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 '22 at 13:37

7 Answers7

60

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
raphink
  • 12,457
  • 1
    nice @Raphink , thank you. added http://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.?!

    – childno͡.de Aug 01 '12 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 01 '12 at 12:34
  • Doesn't work on Mac? – Will Jul 28 '14 at 01:13
  • 2
    This does not work if the keys are prefixed with options. – starfry Feb 09 '17 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. – 0xC0000022L Aug 09 '17 at 12:24
  • does not work with zsh – thomas Mar 17 '20 at 11:59
  • this works for me with prefixed options: grep -o 'ssh-.*' ~/.ssh/authorized_keys | xargs -n1 -I% sh -c 'echo "%" | ssh-keygen -l -f /dev/stdin' – thomas Mar 17 '20 at 12:08
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 <<<"%"'
Law29
  • 3,577
akavel
  • 357
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  x@x.local (RSA)
bash $ 
Will
  • 1,157
  • 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 ;) – childno͡.de Jul 29 '14 at 05: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 02 '14 at 02: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 '15 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. – 0xC0000022L Aug 09 '17 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 '21 at 09:47
5

Since recent versions of OpenSSH, this command already produces a nice listing for you:

ssh-keygen -l -f authorized_keys

Example:

# ssh-keygen -l -f authorized_keys
2048 SHA256:GzZ7.................................RqTEag foo (RSA)
2048 SHA256:/y0.......................................4 bar (RSA)
2048 SHA256:p.........................................k bleech (RSA)

So consider just upgrading your OpenSSH client.

  • that is already shown in the hoghest count answer? did you not read it? – djdomi Dec 31 '21 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 '22 at 18:33
  • 1
    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 '22 at 13:34
  • It seems to me this should be the accepted answer nowadays – Valerio Bozz Feb 28 '24 at 10:17
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 -}
Elephantik
  • 111
  • 2
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
tripleee
  • 1,483
emaV
  • 101
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
}
  • /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 '21 at 09:52