7

我正在尝试从我的 Android 应用程序打开一个 SSH 客户端会话。尝试连接到本地网络上的设备(Raspberry Pi)。我正在使用SSHJ库版本 0.10.0。它在ssh.connect()通话中失败,TransportException最终由 a 引起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 连接到您的 RPi,并使用用户名和密码进行身份验证。这工作正常,所以它似乎不是网络问题。

4

6 回答 6

5

Android 附带了不包含 ECDSA 算法的精简版 BouncyCastle。因此,即使您在类路径中包含完整版本,Android 运行时版本也会被拾取并使用。

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

Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
于 2014-10-31T00:25:14.153 回答
3

首先在 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
    }

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

于 2019-12-16T06:35:30.470 回答
2

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

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

于 2015-02-03T02:50:56.377 回答
0

在 SSHJ 中无法解决此问题,因此决定尝试提供相同功能的JSch 。它也可以作为 maven repo 使用——我使用了jsch 版本 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 秒。

于 2014-10-31T13:39:48.880 回答
0

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

于 2014-11-01T12:48:14.327 回答
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;
}

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

于 2019-04-04T16:03:08.063 回答