Search code examples
javaandroidsshraspberry-pisshj

Android SSHJ exception upon connect() - "KeyFactory ECDSA implementation not found"


I'm trying to open an SSH client session from my Android app. Trying to connect to a device on the local network (a Raspberry Pi). I'm using the SSHJ library version 0.10.0. It fails on the ssh.connect() call, with a TransportException which is ultimately caused by a NoSuchAlgorithmException. Refer exception tree below.

SSHClient ssh = new SSHClient(new AndroidConfig());
Session session = null;

try {    
    //ssh.loadKnownHosts();

    // Exception thrown on this line
    ssh.connect("192.168.1.109", 22);

    // Doesn't reach below
    ssh.authPassword("user", "password");
    session = ssh.startSession();
}
catch (net.schmizz.sshj.transport.TransportException ex) {
    ;
}

Exception tree:

net.schmizz.sshj.transport.TransportException
 net.schmizz.sshj.common.SSHException
  net.schmizz.sshj.common.SSHRuntimeException
   java.security.GeneralSecurityException: java.security.NoSuchAlgorithmException: KeyFactory ECDSA implementation not found
    java.security.NoSuchAlgorithmException: KeyFactory ECDSA implementation not found

Other system info:

SSHJ library   : v0.10.0
Android device : Galaxy Note 3 running Android 4.4.2

I used the maven dependency support in Android Studio to bring in the SSHJ JAR and it pulled in the following three libraries in addition to the SSHJ v0.10.0 jar...

bouncy castle...
  bcpkix-jdk15on-1.50.jar
  bcprov-jdk15on-1.50.jar
logging....
  slf4j-api-1.7.7.jar

Don't have a clue where to start with this exception ... any suggestions appreciated! Thanks.

UPDATE: 31-Oct-2014

As suggested by LeeDavidPainter, I included the SpongyCastle 1.51.0 JAR and added this line at the top:

Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);

I'm now getting a different exception on the same line:

net.schmizz.sshj.transport.TransportException
 net.schmizz.sshj.common.SSHException
  net.schmizz.sshj.common.SSHRuntimeException
   java.security.GeneralSecurityException: java.security.spec.InvalidKeySpecException: key spec not recognised
    java.security.spec.InvalidKeySpecException: key spec not recognised

Also note I tried the following line as well, with the same result:

Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());

I have another app on my phone which is basically doing exactly what I want to achieve - its called RaspberryPiController - it connects to your RPi over SSH with username and password auth. This works fine, so it would seem its not a network issue.


Solution

  • Couldn't get anywhere with this issue in SSHJ, so decided to give JSch a try which offers the same functionality. Its available as a maven repo as well - I used jsch version 0.1.51 ('com.jcraft:jsch:0.1.51').

    It worked first time with this code fragment;

    import com.jcraft.jsch.ChannelExec;
    import com.jcraft.jsch.JSch;
    import com.jcraft.jsch.JSchException;
    
    import java.io.ByteArrayOutputStream;
    import java.util.Properties;
    
    JSch jsch = new JSch();
    com.jcraft.jsch.Session session = null;
    String result = "";
    
    try {    
      session = jsch.getSession("user", "192.168.1.109", 22);
      session.setPassword("password");
    
      // Avoid asking for key confirmation
      Properties prop = new Properties();
      prop.put("StrictHostKeyChecking", "no");
      session.setConfig(prop);
      session.connect();
    
      // SSH Channel
      ChannelExec channel = (ChannelExec)session.openChannel("exec");
      ByteArrayOutputStream stream = new ByteArrayOutputStream();
      channel.setOutputStream(stream);
    
      // Execute command
      channel.setCommand("ls -ltr");
      channel.connect(1000);
      java.lang.Thread.sleep(500);   // this kludge seemed to be required.
      channel.disconnect();
    
      result = stream.toString();
    }
    catch (JSchException ex) {
      String s = ex.toString();
      System.out.println(s);
    }
    catch (InterruptedException ex) {
      String s = ex.toString();
      System.out.println(s);
    }
    finally {
      if (session != null)
        session.disconnect();
    }
    

    It feels like a more robust implementation when using it compared to SSHJ - or this impression might be caused by them selecting quite conservative timeouts. For example, if the target device is switched off, the session.connect() call will, by default, keep trying to connect for something like 20 seconds before giving up.