49

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,127
  • 10
  • 25
childno͡.de
  • 631
  • 1
  • 5
  • 14
  • 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](http://unix.stackexchange.com/questions/343695) but the best I could come up with is covered by [this answer](http://unix.stackexchange.com/a/343713). – starfry Feb 10 '17 at 08: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 '22 at 13:37

7 Answers7

57

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
  • 11,337
  • 6
  • 36
  • 47
  • 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,507
  • 1
  • 15
  • 28
akavel
  • 347
  • 3
  • 10
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,127
  • 10
  • 25
  • 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
3
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)
tripleee
  • 1,324
  • 3
  • 14
  • 24
  • 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
  • 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
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,324
  • 3
  • 14
  • 24
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