connect()时的Android SSHJ异常 - "未找到KeyFactory ECDSA实现"

时间:2014-10-30 12:47:12

标签: java android ssh raspberry-pi sshj

我正在尝试从我的Android应用中打开SSH客户端会话。尝试连接到本地网络上的设备(Raspberry Pi)。我正在使用SSHJ库版本0.10.0。它在ssh.connect()来电时失败,TransportException最终由NoSuchAlgorithmException引起。请参阅下面的例外树。

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) {
    ;
}

异常树:

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

其他系统信息:

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

我使用Android Studio中的maven依赖支持来引入SSHJ JAR,除了SSHJ v0.10.0 jar之外,还引入了以下三个库...

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

没有线索从这个例外开始...任何建议表示赞赏!感谢。

更新时间:2014年10月31日

根据LeeDavidPainter的建议,我加入了SpongyCastle 1.51.0 JAR并在顶部添加了这一行:

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

我现在在同一行上得到一个不同的例外:

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

另请注意,我也尝试了以下行,结果相同:

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

我的手机上有另一个应用程序基本上正在完成我想要实现的目标 - 名为RaspberryPiController - 它通过SSH连接到您的RP​​i,用户名和密码验证。这很好,所以看起来它不是网络问题。

6 个答案:

答案 0 :(得分:5)

Android附带了BouncyCastle的缩减版本,其中不包含ECDSA算法。因此,即使您在类路径中包含完整版本,也会选择并使用Android运行时版本。

您可能希望查看为了解决此问题而创建的http://rtyley.github.io/spongycastle/,它是Bouncycastle的重新打包版本,可以作为Android中的单独JCE提供程序安装。在尝试连接SSHJ(未经测试)之前,只需将其安装为默认JCE提供程序。

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

答案 1 :(得分:2)

在此处降级为sshj 0.9.0:http://mvnrepository.com/artifact/net.schmizz/sshj/0.9.0

问题似乎已在0.10.x中引入。此外,我已经尝试过其他JCE提供商,但遇到了同样的麻烦。

答案 2 :(得分:0)

在SSHJ中无法解决此问题,因此决定尝试提供相同功能的JSch。它也可以作为maven repo使用 - 我使用jsch version 0.1.51(' com.jcraft:jsch:0.1.51')。

第一次使用此代码片段;

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();
}

与SSHJ相比,使用它时感觉就像一个更强大的实现 - 或者这种印象可能是由于他们选择了相当保守的超时。例如,如果目标设备已关闭,则默认情况下,session.connect()调用将在放弃之前继续尝试连接20秒。

答案 3 :(得分:0)

Jsch最有可能工作,因为它不支持SSH AFAIK的椭圆曲线算法。如果您不需要椭圆曲线算法,那么这就是您的答案。

答案 4 :(得分:0)

基于LeeDavidPainter的解决方案,

/**
 * 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;
}

// <-以上两个注释区域是解决方案。

答案 5 :(得分:0)

首先在app / build.gradle文件中添加此BouncyCastle库:

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

然后在您的活动文件中,添加一个静态块以删除在我们的版本中存在于Android中的默认BouncyCastle提供程序:

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

这将解决算法实现未发现的问题。