SSH: reusing public keys and known-man-in-the-middle
Usually, people recommend to use a single private-public key pair everywhere (if we're not talking about a possibility of compromising the private key)
Note that there are two key pairs involved in an SSH connection:
- one to identify the server host (the server has the private key);
- one to identify the user (the client has the private key), if using public key authentication.
The recommendations you cite are about user keys. Having a single private key per user is a viable model, though I don't particularly recommend it (the cost of having separate key pairs per host or site isn't that high). Server keys, on the other hand, are never duplicated. They are generated afresh when the SSH server is installed.
On this topic, beyond the posts you've cited in your question, see also What is the difference between authorized_keys and known_hosts file for SSH?
In the attack you describe, you've mixed up the roles of the keys. These are the host keys, ssh_host_rsa
, and not user keys. This isn't directly relevant in how the attack you describe works, but may be part of your confusion.
The sticky point is at step 3.3:
PC1 accepts S1_id_rsa.pub's fingerprint (as it's known)
There are two cases.
If PC1 has connected to S2 before, then PC1 (or more precisely the account of the user on PC1) has memorized S2's host public key in its
known_hosts
file. So if S1 sends S2's host key, the SSH client on PC1 will detect that the purported host key does not match the recorded host key and abort the connection with a scary message:@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that the RSA host key has just been changed.
The attack is thus impossible in this scenario (unless the user goes out of his way to bypass the check — OpenSSH purposefully makes this difficult).
If PC1 has never connected to S2, then PC1 will happily accept whatever S1 is now telling it is S2's key. Since PC1 has no prior knowledge of any association between the name S2 and a cryptographic identity, nor any way to contact a trusted third party who knows such an association (i.e. a public-key infrastructure), there is no way to prevent this man-in-the-middle attack.
Finally, I think I got it.
First of all - I've mixed up ssh v1 and ssh v2 protocols in my description. The challenge-response authorization is from ssh v1.
In ssh v2 client simply signs a specific package with his private key and server checks it using stored public key. This package is described in rfc, here:
To perform actual authentication, the client MAY then send a
signature generated using the private key. The client MAY send the
signature directly without first verifying whether the key is
acceptable. The signature is sent using the following packet:
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string public key algorithm name
string public key to be used for authentication
string signature
The value of 'signature' is a signature by the corresponding private
key over the following data, in the following order:
string session identifier
byte SSH_MSG_USERAUTH_REQUEST
string user name
string service name
string "publickey"
boolean TRUE
string public key algorithm name
string public key to be used for authentication
So, the signed package contains "session identifier" which is calculated as follows:
H = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)
<…..>
mpint K, the shared secret
That is, signature does depend on shared secret, and shared secrets are different for P1<->S1 and S1<->S2 tunnels.