8

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.

Community
  • 1
  • 1
dodgy_coder
  • 12,407
  • 10
  • 54
  • 67

6 Answers6

5

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);
  • Thanks for the suggestion - I gave it a go and am now getting a different exception - java.security.spec.InvalidKeySpecException: key spec not recognised. It happens at the same spot - on the ssh.connect() call. I've updated the question above with the details. – dodgy_coder Oct 31 '14 at 11:38
  • I have the same problem and introducing this new JCE gives the same problem as the comment just above... – gregoiregentil Feb 03 '15 at 02:41
  • 1
    On Android 4.4.4, I had to call `Security.removeProvider("BC")` **before** calling `Security.insertProviderAt(new BouncyCastleProvider(), Security.getProviders().length+1)`. Otherwise, the `BouncyCastleProvider` was never getting added to the list of providers. – EricRobertBrewer Mar 27 '17 at 19:33
  • Yes it worked, and it wasn't even on android I was trying to do this for. – Joe Apr 04 '19 at 16:01
4

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.

  • 3
    It's working solution. However please use below code if you are using any other provider also for different operation. For Ex. Android Keystore **`Security.removeProvider("BC");//first remove default os provider Security.addProvider(new BouncyCastleProvider());//add new provider`** It will add new provider at the end of the list. – Tejas Mehta Aug 13 '20 at 10:51
2

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.

gregoiregentil
  • 1,793
  • 1
  • 26
  • 56
0

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.

dodgy_coder
  • 12,407
  • 10
  • 54
  • 67
  • How can I execute the following command like following ,where there is no username or password or any authentication `ssh -p 4000 -R test:1777:localhost:1337 test.mydomain.com` – Mithun Sarker Shuvro Jul 01 '20 at 12:36
0

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.

0

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.

Joe
  • 1,316
  • 9
  • 17