I am currently writing a tool which will be used to determine whether any of the servers that I manage currently trust any SSH private keys which are known to be compromised.
I don't want to store the compromised SSH private keys on the hosts that will do this scanning.
Is there a way to determine whether a remote SSH server accepts a private key, based only on its public key?
When using ssh -vvv
, there is some information that hints towards it being possible, however I'm not sure how:
debug1: Next authentication method: publickey
debug1: Offering public key: /Users/opera_user/.ssh/id_rsa RSA SHA256:bZ2lxtUybH426ogDCGzZ3/HzbYaIsZ3rC69jgXBa3Ig
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 60
debug1: Server accepts key: /Users/opera_user/.ssh/id_rsa RSA SHA256:bZ2lxtUybH426ogDCGzZ3/HzbYaIsZ3rC69jgXBa3Ig
Is there a programmatic way to do this?
The protocol allows this, but OpenSSH does not. In order to authenticate with a public key, the client first sends the public key over the connection and asks if the server would accept it. If it does, then the client signs the appropriate data with the private key, including the session hash (which is generated from the key agreement), and sends the signature.
The reason this occurs is because one can have multiple public keys, which may or may not be encrypted or on a security key, and it's beneficial to avoid needing to prompt for multiple passphrases or PINs.
However, OpenSSH doesn't offer a way to scan to see if the key is valid. You would need a library which can do this. Paramiko is a library for Python, Go has an SSH library, and libssh is available for anything that can interface with C. I don't know, however, if they offer this functionality on the client side (I know libssh does for the server side), but it should be relatively easy to see if they can in fact perform this based on the API in question.