k4200’s notes and thoughts

Programmer side of k4200

SSH for Java (and Scala)

I needed an SSH implementation in Java (or Scala) for my personal project.

I did a brief search and JSch came up, so I tried it. The result was bad. It has no documentation and poorly written sample programs, which didn't help much.

sshj rocks

I did another small research and found this thread on stackoverflow. As one of the guys recommended, I tried sshj and found it pretty good and easy to use.

It's got a lot of example code, so you don't really need any blog entries for beginners or some such.

Required libraries

As README says, it requires bouncycastle for using some of the crypto algorithms and JZlib for zlib compression.

My sbt project file is like the following:

protected class CoreProject(info: ProjectInfo) extends DefaultProject(info) {
 // (snip)
  override def libraryDependencies = Set(
    // (snip)
    "net.schmizz" % "sshj" % "0.4.1",
    "org.bouncycastle" % "bcprov-jdk16" % "1.46", 
    "com.jcraft" % "jzlib" % "1.0.7"
  ) ++ super.libraryDependencies
  override def compileOptions = super.compileOptions ++ Seq(Unchecked)
}
troubleshooting

Before you connect to a server via SSH, the host's fingerprint should be included in your known_hosts. Otherwise you'll get an error like the following:

10:50:53.554 [20627169@qtp-3551336-4] INFO  n.schmizz.sshj.common.SecurityUtils - BouncyCastle not registered, using the default JCE provider
10:50:54.234 [20627169@qtp-3551336-4] INFO  n.s.sshj.transport.TransportImpl - Client identity string: SSH-2.0-SSHJ_0_4_1
10:50:54.491 [20627169@qtp-3551336-4] INFO  n.s.sshj.transport.TransportImpl - Server identity string: SSH-2.0-OpenSSH_5.4p1 FreeBSD-20100308
10:50:54.491 [20627169@qtp-3551336-4] DEBUG net.schmizz.concurrent.Promise - Setting <<kex done>> to `null`
10:50:54.491 [20627169@qtp-3551336-4] INFO  n.s.sshj.transport.KeyExchanger - Sending SSH_MSG_KEXINIT
10:50:54.495 [20627169@qtp-3551336-4] DEBUG net.schmizz.concurrent.Promise - Setting <<kexinit sent>> to `SOME`
10:50:54.495 [20627169@qtp-3551336-4] DEBUG net.schmizz.concurrent.Promise - Awaiting <<kex done>>
10:50:54.690 [reader] INFO  n.s.sshj.transport.KeyExchanger - Received SSH_MSG_KEXINIT
10:50:54.691 [reader] DEBUG n.s.sshj.transport.KeyExchanger - Negotiated algorithms: [ kex=diffie-hellman-group1-sha1; sig=ssh-rsa; c2sCipher=aes128-ctr; s2cCipher=aes128-ctr; c2sMAC=hmac-sha1; s2cMAC=hmac-sha1; c2sComp=none; s2cComp=none ]
10:50:54.711 [reader] INFO  net.schmizz.sshj.transport.kex.DHG1 - Sending SSH_MSG_KEXDH_INIT
10:50:55.001 [reader] INFO  n.s.sshj.transport.KeyExchanger - Received kex followup data
10:50:55.001 [reader] INFO  net.schmizz.sshj.transport.kex.DHG1 - Received SSH_MSG_KEXDH_REPLY
10:50:55.021 [reader] DEBUG n.s.sshj.transport.KeyExchanger - Trying to verify host key with net.schmizz.sshj.transport.verification.OpenSSHKnownHosts@1cc5af0
10:50:55.025 [reader] DEBUG n.s.sshj.transport.KeyExchanger - Trying to verify host key with net.schmizz.sshj.transport.verification.OpenSSHKnownHosts@17d51a6
10:50:55.026 [reader] ERROR n.s.sshj.transport.TransportImpl - Dying because - net.schmizz.sshj.transport.TransportException: [HOST_KEY_NOT_VERIFIABLE] Could not verify `ssh-rsa` host key with fingerprint `xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx` for `foo.example.com` on port 22
10:50:55.027 [reader] DEBUG n.s.sshj.transport.TransportImpl - Default disconnect listener - HOST_KEY_NOT_VERIFIABLE
10:50:55.027 [reader] DEBUG n.s.sshj.transport.KeyExchanger - Got notified of net.schmizz.sshj.transport.TransportException: [HOST_KEY_NOT_VERIFIABLE] Could not verify `ssh-rsa` host key with fingerprint `xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx` for `foo.example.com` on port 22
10:50:55.027 [20627169@qtp-3551336-4] ERROR net.schmizz.concurrent.Promise - <<kex done>> woke to: net.schmizz.sshj.transport.TransportException: [HOST_KEY_NOT_VERIFIABLE] Could not verify `ssh-rsa` host key with fingerprint `xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx` for `foo.example.com` on port 22
10:50:55.028 [reader] DEBUG n.s.s.t.TransportImpl$NullService - Notified of net.schmizz.sshj.transport.TransportException: [HOST_KEY_NOT_VERIFIABLE] Could not verify `ssh-rsa` host key with fingerprint `xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx` for `foo.example.com` on port 22
10:50:55.028 [reader] INFO  n.s.sshj.transport.TransportImpl - Setting active service to null-service
10:50:55.029 [reader] DEBUG n.s.sshj.transport.TransportImpl - Sending SSH_MSG_DISCONNECT: reason=[HOST_KEY_NOT_VERIFIABLE], msg=[Could not verify `ssh-rsa` host key with fingerprint `xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx` for `foo.example.com` on port 22]

If the necessary libraries are not found in your class path, an error will occur.

Without bouncycastle, the error should be like the following:

14:02:14.245 [10184846@qtp-13399097-0] DEBUG net.schmizz.sshj.SSHClient - Attempting to load key from: /home/foo/.ssh/id_rsa
14:02:14.261 [10184846@qtp-13399097-0] WARN  net.schmizz.sshj.SSHClient - Could not load keys due to: {}
net.schmizz.sshj.common.SSHException: No provider available for OpenSSH key file
        at net.schmizz.sshj.SSHClient.loadKeys(SSHClient.java:485) [sshj-0.4.1.jar:na]
        at net.schmizz.sshj.SSHClient.loadKeys(SSHClient.java:441) [sshj-0.4.1.jar:na]
        at net.schmizz.sshj.SSHClient.authPublickey(SSHClient.java:351) [sshj-0.4.1.jar:na]
        at net.schmizz.sshj.SSHClient.authPublickey(SSHClient.java:285) [sshj-0.4.1.jar:na]
        at net.lifthub.lib.SbtHelper$$anonfun$deploy$1.apply(ProjectHelper.scala:207) [classes/:na]

Conclusion

sshj rocks. It's pretty easy. Have fun.