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):

  1. Best Practice: “One per-user ssh key” or “multiple per-host ssh keys”
  2. Reusing Private/Public Keys
  3. https://serverfault.com/questions/80478/ssh-do-you-use-one-private-public-key-pair-for-each-remote-machine-or-a-single/80483#80483

    It seems that this would result in a vulnerability when using client certificate authentication over SSH. Since it is so popular to suggest, I suspect the "algorithm" below won't work. But I just don't get what exactly is wrong with it.

I've tried to make my description as detailed as possible, to minimize possible discrepancies, so, please excuse me for the length...


  • PC1 has both (S1_id_rsa.pub and S2_id_rsa.pub) fingerprints in his known hosts.
  • Server1 somehow knows about existence of PC1's account on Server2.
  • Keys:
    • PC1 : has P1_id_rsa , S1_id_rsa.pub, S2_id_rsa.pub.
    • Server1, the attacker : has S1_id_rsa, P1_id_rsa.pub, S2_id_rsa.pub.
    • Server2: has S2_id_rsa, P1_id_rsa.pub


This is something like a well-known man-in-the-middle attack, but a bit different.

  1. PC1 sends "Hi" to Server1
  2. Server1 sends "Hi" to Server2
  3. Server shares it's public key

    1. Server2 sends Server1 S2_id_rsa.pub
    2. Server1 sends PC1 S1_id_rsa.pub (instead of S2_id_rsa.pub)
    3. PC1 accepts S1_id_rsa.pub's fingerprint (as it's known)
  4. Two separate shared-secret tunnels are generated using Diffie-Hellman:

    1. "Server1--Server2"

      1. Server2 generates DH1.a, and sends DH1.A,signed with S2_id_rsa, to Server1
      2. Server1 generates DH1.b and sends DH1.B to Server2
      3. Tunnel established
    2. "PC1--Server1"
      1. Server1 generates DH2.a, and sends DH1.A, signed with S1_id_rsa, to PC1
      2. PC1 generates DH2.b and sends DH2.B to Server1
      3. Tunnel is established.
  5. Client Authentication (Server2 now wants to be sure he is talking to PC1)

    1. PC1 sends P1_id_rsa.pub to Server1
    2. Server1 sends P1_id_rsa.pub to Server2
    3. Server2 generates a challenge, which could be solved only with P1_id_rsa and sends it to Server1
    4. Server1 just tunnels challenge to PC1
    5. PC1 solves the challenge and sends answer to Server1
    6. Server1 tunnels answer to Server2
    7. Done.
  6. Done

P.S. I've looked through public-key cryptography and man-in-the-middle attack on Wikipedia, and this quite detailed answer (my view of the whole process is largely based on it), but I've not found the answer..

I wasn't able to find a readable "complete ssh authentication and encryption processes for dummies"...

I've already asked the same question on Server Fault, but was suggested to re-post it here.

  • Note that if you login to a rouge server with SSH agent forwarding enabled, and the SSH key available in your agent without verification, the rouge server can use this to log in into a different server, using a similar scheme as you described. Aug 23, 2016 at 17:21

4 Answers 4


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:

    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.

  • 1) >Note that there are two key pairs involved in an SSH connection There might be a slight confusion in the naming scheme: I've just named 'Server2\etc\ssh\ssh_host_rsa_key.pub' "S2_id_rsa.pub", and "S1_id_rsa.pub" is defined in the same manner. 2) >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 Em... The thing is, in my algorithm , Server1 provides PC1 with S1_id_rsa.pub, not S2<..>.pub
    – Igor
    Jan 21, 2013 at 22:23

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:

  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
  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.


The problem is that the client would know they are talking to server 1 because of the use of server 1's certificate. If server1 has a valid certificate saying they are server2, then the client would be unaware if they had not connected to server2 before. Most SSH clients however track the fingerprint of the certificate that they connect to for a given server. When the client goes to connect and ends up with a connection to server 1, even if the cert is signed, the user will likely get a warning that the fingerprint has changed.

As for a known connection to S1 trying to claim to be s2, if s2 accurately knows p1,s private key, then if 01 wants to talk to s2 through s1 then it should encrypt for s2 and s2 would then encrypt for p1. When p1 doesn't get a key exchange from s2 it will know there is a problem since s1 can't sign an exchange with p1 as s2.

  • >the client would know they are talking to server 1 The thing is, client intentionally wants to connect to Server1, that's why I've named the question "known-man-in-the-middle
    – Igor
    Jan 21, 2013 at 22:29
  • @Igor ah, OK. Updated my answer to address your question better. Jan 23, 2013 at 1:27
  • Em.... As far as I understood, you're still saying that P1 wants to connect to S2. But it wants to connect to S1 instead. The thing is, as far as I understood, only ssh server needs to sign his part of DH key with his private key for server to get authenticated (maybe this is required for keyboard-interactive authentication to be possible). So, S1 doesn't need to "fake" any private key. He may use his own key. It's known and trusted. Client authenthication works a bit different. And again, S1 can fool S2 by allowing P1 to solve the "challenge".
    – Igor
    Jan 23, 2013 at 16:40
  • @Igor - S2 knows it is talking to S1 though because the challenge should be formulated so that only P1 can read it. If P1 gets a challenge from S2 while talking to S1, it shouldn't answer it in a way that S1 can read. P1 should always know reliably which server they are talking to and if S1 should not be able to impersonate a tunnel to P2 without actually tunneling so long as either P1 knows S2 or vice versa. Jan 23, 2013 at 18:25
  • as far as I understood, the challenge could only be SOLVED by P1, because he's the only one with P1's private key. Anyone could receive the challenge, but only the one with an appropriate private key can solve it. That's the idea of the challenge,isn't it? The thing is, after receiving the challenge from S2, S1 may just pass it to P1 , for P1 to solve it. At least, I don't understand, why he's unable to do so. This is the main idea of the whole question...
    – Igor
    Jan 23, 2013 at 23:15

Actually that is a known vulnerability with cracked certificates in the trusted list, and why revocation lists are necessary.

For this attack to work there need to be 2 holes:

  1. PC1 must think it is communicating with server2 while it is actually communicating with server1.
  2. PC1 must have S1_id_rsa.pub as a trusted certificate and it points to server2's address (other wise PC1 will know it is talking to server1 instead of server2 in step #4.3).

#1 can happen on an untrusted network, while #2 can only happen when a previous breach has happened that allowed server1 to insert itself as a trusted party.

  • >>1 But what if PC1 intentionally wants to connect to Server1? (and S1_id_rsa.pub is trusted in this situation, yes)
    – Igor
    Jan 21, 2013 at 22:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .