sshj icon indicating copy to clipboard operation
sshj copied to clipboard

Public RSA-key of host is not verified

Open mgattinger opened this issue 4 years ago • 10 comments

I'm new to sshj and I try to solve the following problem of verifying the identity of the host to connect to. The host is an SSH server that only provides its public key using the ssh-rsa host key algorithm (and not the ssh-ed25519 algorithm, too).

I use the following code to do the verification:

PublicKey publicKey = new Buffer.PlainBuffer(Base64.decode("...")).readPublicKey(); sshClient.addHostKeyVerifier(SecurityUtils.getFingerprint(publicKey));

whereas "..." contains the public key that can be listed requested from the host using the command ssh-keyscan -t rsa .

The SSHClient uses the default configuration.

If I run this code on my machine (in this case the target host is the localhost), I get the following exception: ERROR n.s.s.t.KeyExchanger - Disconnecting because none of the configured Host key verifiers ([net.schmizz.sshj.transport.verification.FingerprintVerifier$1@73f0d46f]) could verify 'ssh-ed25519' host key with fingerprint ca:5f:e9:2b:79:58:5c:0e:a5:b3:28:b5:51:9a:54:d2 for localhost:22 ERROR n.s.s.t.TransportImpl - Dying because - Could not verify ssh-ed25519 host key with fingerprint ... for localhost on port 22 net.schmizz.sshj.transport.TransportException: Could not verify ssh-ed25519 host key with fingerprint ... for localhost on port 22 at net.schmizz.sshj.transport.KeyExchanger.verifyHost(KeyExchanger.java:211) at net.schmizz.sshj.transport.KeyExchanger.handle(KeyExchanger.java:365) at net.schmizz.sshj.transport.TransportImpl.handle(TransportImpl.java:503) at net.schmizz.sshj.transport.Decoder.decodeMte(Decoder.java:159) at net.schmizz.sshj.transport.Decoder.decode(Decoder.java:79) at net.schmizz.sshj.transport.Decoder.received(Decoder.java:231) at net.schmizz.sshj.transport.Reader.run(Reader.java:59) ERROR n.s.c.Promise - <> woke to: net.schmizz.sshj.transport.TransportException: [HOST_KEY_NOT_VERIFIABLE] Could not verify ssh-ed25519 host key with fingerprint ... for localhost on port 22

I wonder why sshj still wants to use ssh-ed25519 for verification as the method KeyType.fromKey(publicKey) prints ssh-rsa.

I know that I can use sshClient.addHostKeyVerifier(new PromiscuousVerifier()); to circumvent the problem, but that is not the solution I'm looking for.

So the question is how I can verify the host key if I only have an RSA ciphered public key of the host?

I found out, that if I use an explicit DefaultConfiguation like this: DefaultConfig defaultConfig = new DefaultConfig(); defaultConfig.setSignatureFactories(new SignatureRSA.Factory()); and pass this to the constructor of the SSHClient the exception disappears.

I do not understand why the default configuration which consists of several preregistered signature factories (and the factory above is already present) will not work as expected. I suppose that all registered signature factories are used to verify the hosts public key. To me this looks like an error.

BTW: I'm using version 0.27.0 of sshj.

Kind regards, Marcus

mgattinger avatar Aug 06 '19 12:08 mgattinger

You can run:

ssh-keyscan -t ed25519 localhost

You can add that line to your ~/.ssh/known_hosts file.

corporate-gadfly avatar Oct 24 '19 13:10 corporate-gadfly

As @mgattinger, I'm very interested that sshj uses RSA when ed25519 fingerprint is not found in known hosts. It could be a fallback: if ed25519 fingerprint is not found, then sshj looks for RSA fingerprint, and if not found, DSA fingerprint.

That would be very helpful. In my known_hosts, I have only RSA fingerprints. And if I remove the fingerprint, and do a new ssh connexion (using putty), rsa fingerprint is added (not ed25519) to know_hosts file.

Finally, if I do "ssh-keyscan -t ed25519 localhost", I get the following error: unknown key type ed25519

So please, consider to fix this issue.

fbaligand avatar Aug 29 '20 21:08 fbaligand

Same issue still exists with 0.30.0

aemyers avatar Dec 10 '20 20:12 aemyers

@fbaligand : Just to clarify, when you issue:

ssh-keyscan -t ed25519 localhost

is localhost the target running ssh software?

corporate-gadfly avatar Dec 10 '20 20:12 corporate-gadfly

Hi,

"localhost" is not my target running ssh software, but just an example host. That said, if I do ssh-keyscan -t ed25519 my-remote-ssh-server (that is a VM), I get the same error. Though I do this test on a quite "classic" linux OS: RHEL.

fbaligand avatar Dec 10 '20 20:12 fbaligand

when initiating a ssh connection, the host key is only sent once. so the client says, i want to verify using key types (algorithms) A, B, C. server then looks at its keys and uses the key that matches the leftmost proposal of the client. e.g. if it has key types A and B, it will always choose A.

the problem with sshj is that it always sends the whole list of algorithms, starting with Ed25519, regardless of what keys it has. so if the client only knows server's RSA key and the server has both RSA and Ed25519 keys, sshj will fail to connect. is this the issue?

unknown key type ed25519

i think this is a problem with your ssh-keyscan, perhaps it's too old? try running it from another device maybe

oakkitten avatar Dec 11 '20 06:12 oakkitten

Well, after some tests, on RHEL 6, ed25519 key type is not recognized, but it works fine on RHEL 7. That said, the point of this issue is that sshj works in all cases and recognizes RSA key types. So it would be great if sshj searches for ed25519 key type in known_hosts file, and if not found, searches for rsa key type, and ideally if still not found, searches for dsa key type

fbaligand avatar Dec 11 '20 13:12 fbaligand

Thank you @oakkitten for the explanations. It allowed me to limit to RSA and now it works.

    DefaultConfig conf = new DefaultConfig();
    conf.setSignatureFactories(new SignatureRSA.Factory());
    SSHClient ssh = new SSHClient(conf);

nicofabre avatar Feb 25 '21 18:02 nicofabre

This is makes it very hard to imagine using SSHJ in production. It seems the above solution is OBE, there is no DefaultConfig.setSignatureFactories now.

By looping threw making each type of key the first choice, I am able to connect to all the hosts I have tried so far. You have to do extra work to keep SSHJ from killing your thread.

This is in Scala, I'm sure you can figure out how to do the same in java.

Logging is LOUD! There are a lot of messages generated doing this, so logback.xml is worth while, even then some are ERRORs.

 val algs: List[Factory.Named[KeyAlgorithm]] = List(
    KeyAlgorithms.ECDSASHANistp256(),
    KeyAlgorithms.ECDSASHANistp521(),
    KeyAlgorithms.ECDSASHANistp384(),
    KeyAlgorithms.SSHRSA(),
    KeyAlgorithms.RSASHA256(),
    KeyAlgorithms.RSASHA512(),
    KeyAlgorithms.EdDSA25519(),
    KeyAlgorithms.SSHRSACertV01(),
    KeyAlgorithms.SSHDSSCertV01(),
    KeyAlgorithms.SSHDSA()
  )

  /**
   *
   * @param hostname -- host to connect to
   * @return -- The authenticated SSHClient or None if no authentication succeeds
   */
  def makeConnection(hostname: String, user: String): Option[SSHClient] = {
    var result: Option[SSHClient] = None
    for {alg <- algs if result.isEmpty} {
      val test = new Thread(() => {
        try {
          val conf = new DefaultConfig
          conf.setKeyAlgorithms(List(alg).asJava)
          val ssh = new SSHClient(conf)
          ssh.loadKnownHosts()
          ssh.connect(hostname)
          ssh.authPublickey(user)
          if (ssh.isAuthenticated) {
            result = Some(ssh)
          }
        } catch {
          case tx: TransportException if tx.getDisconnectReason != DisconnectReason.HOST_KEY_NOT_VERIFIABLE =>
            throw tx
        }
      })
      test.start()
      test.join()
    }
    result
  }

WorkDayHeyHey avatar Aug 09 '21 21:08 WorkDayHeyHey

The same error here with me, but found this: https://github.com/hierynomus/sshj/issues/635

Then I've added this before connecting:

ssh.addHostKeyVerifier(new PromiscuousVerifier());

It was enough to solve!

eduveks avatar Dec 05 '23 18:12 eduveks