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.