dodgy_coder
dodgy_coder

Reputation: 13033

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.

Upvotes: 8

Views: 3237

Answers (6)

First add this BouncyCastle library in app/build.gradle file:

implementation 'org.bouncycastle:bcpkix-jdk15on:1.64'

Then in your activity file, add a static block to remove the default BouncyCastle provider found in Android with our version:

    static {
        Security.removeProvider("BC");//first remove default os provider
        Security.insertProviderAt(new BouncyCastleProvider(), 1);//add new provider
    }

This will resolve the algorithm implementation not found issue.

Upvotes: 4

Joe
Joe

Reputation: 1342

Based off of LeeDavidPainter's solution,

/**
 * Creates a new SSH client stub
 */
public SSH(final String host, final int port)
{
    SecurityUtils.setSecurityProvider(SecurityUtils.BOUNCY_CASTLE); //<-- Here
    Security.insertProviderAt(new BouncyCastleProvider(), 1); //<-- Here
    this.ssh.addHostKeyVerifier(new PromiscuousVerifier());
    this.shell = new SSHShellSession();
    this.ssh = new SSHClient();
    this.connected = false;
    this.initiated = false;
    this.host = host;
    this.port = port;
}

The two commented areas above //<-- here are the solution.

Upvotes: 0

gregoiregentil
gregoiregentil

Reputation: 1889

Downgrade to sshj 0.9.0 here: http://mvnrepository.com/artifact/net.schmizz/sshj/0.9.0

The problem seems to have been introduced in 0.10.x. Also, I have tried the other JCE provider but got into the same trouble.

Upvotes: 2

dodgy_coder
dodgy_coder

Reputation: 13033

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.

Upvotes: 0

LeeDavidPainter
LeeDavidPainter

Reputation: 126

Jsch most likely worked because it does not support the Elliptic Curve algorithms for SSH AFAIK. If you don't need Elliptic Curve algorithms then that's your answer.

Upvotes: 0

LeeDavidPainter
LeeDavidPainter

Reputation: 126

Android ships with a cut down version of BouncyCastle which does not include the ECDSA algorithms. So even though you include the full version in your class path, the Android runtime version will be picked up and used.

You may want to look at http://rtyley.github.io/spongycastle/ which was created to get around this, its a repackaged version of Bouncycastle that can be installed as a separate JCE provider in Android. Just install it as the default JCE provider before you try to connect with SSHJ (untested).

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

Upvotes: 5

Related Questions