But muCommander didn't use public key authentication, and I have disabled password-based authentication on my home SSH server. So there was the opportunity to play around with J2SSH (which muCommander was using) and both implement key-based authentication in my current file manager and explore one of the libraries for my next project. OK, maybe not applicable for my work project, because J2SSH is GPL, but I can get to play, OK?
With license questions out of the way, we can get down to business. And it is not difficult at all, as the j2ssh/examples directory contains a PublicKeyConnect.java. You just need to instantiate a
com.sshtools.j2ssh.SshClient, call connect, passing the hostname as a String and authenticate using the method... which was it... ah yes authenticate, passing a com.sshtools.j2ssh.authentication.SshAuthenticationClient.Now getting this
SshAuthenticationClient is the fun part. In our case, we are using the com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient and we need several more steps to prep it up. After instantiating it, we need to call setUsername with a String (can't tell you what it stands for). Then we need to set the private key itself using the setKey method and we're What?
setKey expects a parameter? Well yes it does, and it's another of those j2ssh classes: com.sshtools.j2ssh.transport.publickey.SshPrivateKey. But we seem to be stuck here, as you can't instantiate SshPrivateKey- it's abstract. What does this mean? Argh, never mind, you can't instantiate it. Try it. See? Told ya so.So then we see there's this similarly named
com.sshtools.j2ssh.transport.publickey.SshPrivateKeyFile. Might it have something to do with our SshPrivateKey? Yes, as it turns out, it has a method toPrivateKey (takes a passphrase as a String- you do use passphrases, don't you?), and returns our most wanted SshPrivateKey. What's that you're saying? You can't instantiate SshPrivateKeyFile either? Fear not, because it has a static method parse, which overloaded to accept both a File and a byte array with the actual private key file or data.If you couldn't follow this convoluted line of thought (I couldn't), here's the code, as taken from the patch of muCommander:
PublicKeyAuthenticationClient pk = new PublicKeyAuthenticationClient();
pk.setUsername(credentials.getLogin());
SshPrivateKey key = null;
// Throw an AuthException if problems with private key file
try {
SshPrivateKeyFile file = SshPrivateKeyFile.parse(new File(privateKeyPath));
key = file.toPrivateKey(credentials.getPassword());
} catch (InvalidSshKeyException iske) {
throw new AuthException(realm, "Invalid private key file or passphrase"); // Todo: localize this entry
} catch (IOException ioe) {
throw new AuthException(realm, "Error reading private key file"); // Todo: localize this entry
}
pk.setKey(key);
Now if
sshClient.authenticate returns AuthenticationProtocolState.COMPLETE, you're ready to go. You can open a shell or an SftpClient. Mission accomplished.If you had the urge to download the muCommander source code and apply this patch, no need to: it's already implemented in CVS.