我正在尝试从我的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连接到您的RPi,用户名和密码验证。这很好,所以看起来它不是网络问题。
答案 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
}
这将解决算法实现未发现的问题。