Linux: ssh-keygen to check whether ssh private key and public cert are keypair

When using a private key on the client to ssh into a remote server with the matching public certificate in ~/.ssh/authorized_keys, a common failure message from the client is:

Permission denied (publickey)

The most common reasons for this is private key permissions issues (chmod 600), a misconfiguration of authorized_keys, or trying to send the wrong identity file.

But after that, you should look into confirming whether the local ssh private key is a valid keypair match for the public cert on the server-side.

Also take note that the existence of a local ‘<privateKey>.pub’ file sitting in the same directory as the local private key can also cause this error message if it is not a matching keypair.

Example ssh keypair

So we can test our examples, let’s generate a sample ssh keypair (private key and its .pub certificate).  Then we create one more keypair so we can also test a mismatch later.

sshkeyfile="id_rsa"
sshpubfile="${sshkeyfile}.pub"

# generate rsa keypair
ssh-keygen -t rsa -b 4096 -f $sshkeyfile -C test -N "" -q
# if you instead wanted an elliptical curve key
# ssh-keygen -t ecdsa -b 521 -f $sshkeyfile -C test -N "" -q

# check existence and permissions
ls -l $sshkeyfile
ls -l $sshpubfile

# generate another keypair so we can see mismatch later
ssh-keygen -t rsa -b 4096 -f failcase -C "for mismatch" -N "" -q
ls -l failcase*

Fingerprint of local private key

Get the fingerprint of the local ssh private key.  This is what the ssh client passes as the identity file to the remote server for authentication.

# calculate the fingerprint of the private key
$ ssh-keygen -l -f $sshkeyfile
4096 SHA256:hiF9EnXATcOdbuSBzztSokQlfsuQvj6clwcpPKkoaoY test (RSA)

We are purposely not using the “-e” switch, because that causes keygen to simply echo the .pub file (if it exists).

Fingerprint of remote public cert

For your testing, you will want to grab the public certificate loaded on the remote host at “~/.ssh/authorized_keys” of the user.

# calculate the fingeprint of the public cert
$ ssh-keygen -l -f $sshpubfile
4096 SHA256:hiF9EnXATcOdbuSBzztSokQlfsuQvj6clwcpPKkoaoY test (RSA)

Notice that it matches the fingerprint of the private key.  This is the same logic used to allow authentication into the remote system.

Whereas the fingerprint of the other keypair is a completely different SHA value.

# calculate the fingeprint of the different keypair
$ ssh-keygen -l -f failcase.pub
4096 SHA256:SO6fnjfxZbx+z7d2Pp8KoRL5uPqDUZNLXEGh6ncDgRw for mismatch (RSA)

Determining match

If the SHA values match, that is proof that the private key and public certificate are keypairs.

# match if no differences and exit code of 0
diff <(ssh-keygen -l -f $sshkeyfile) <(ssh-keygen -l -f $sshpubfile)

The comments field can be different, that does not matter, only the SHA values must be equivalent.

Fingerprint of local .pub used by ssh client

But there is one more caveat that you need to be aware of, your ssh client will act differently if there exists a “<keyName>.pub” file sitting in the same directory as the private key.  It will send the public cert as a validation.

# with verbosity, can see that ".pub" also gets sent by client (not just private key)
$ ssh -vvv -i <privateKey> <user>@<host>
..
debug1: Offering public key: RSA SHA256:hiF9EnXATcOdb....
...

The ssh client will skip this step if a “.pub” file does not exist.  So if the “.pub” file exists, it MUST be a matching keypair with the private key or else you will get the error.

<user>@<host>: Permission denied (publickey).

This “.pub” file can be checked just like shown above, and needs to match the fingerprint of the private key.

Github script

I’m providing ‘ssh-keygen_check_ssh_key.sh‘ on my public github repo to test for all the conditions above.

wget https://raw.githubusercontent.com/fabianlee/blogcode/master/bash/ssh-keygen_check_ssh_key.sh
chmod +x ssh-keygen_check_ssh_key.sh

# looks at private ssh key and its '.pub' certificate side
./ssh-keygen_check_ssh_key.sh myprivatekey

 

REFERENCES

man ssh-keygen

stackoverflow, how to check if RSA public private keypair match

fabianlee, script for validating match of private key and public TLS cert using openssl

serverfault.com – ssh-keygen generates in RFC4716 while openssl reads PEM

 

NOTES

Alternate way of showing private key sha for RSA private key

$ ssh-keygen -y -f $sshkeyfile | ssh-keygen -lf -
4096 SHA256:hiF9EnXATcOdbuSBzztSokQlfsuQvj6clwcpPKkoaoY test (RSA)

convert RSA private key in RFC4716 format to PEM that openssl can read

# change 'BEGIN OPENSSH PRIVATE KEY' to 'BEGIN RSA PRIVATE KEY'
# empty password phrase with -N
ssh-keygen -p -m PEM -f $sshkeyfile -N ""

# 'BEGIN RSA PRIVATE KEY' readable by openssl
# 'BEGIN OPENSSH PRIVATE KEY' is NOT readable by openssl
openssl rsa -in $sshkeyfile -text

ads