在Android应用程序中,我正在尝试使用JSch库连接到SSH服务器。 远程服务器由用户指定,所以我事先不知道远程指纹。 同时,我不想像我在很多例子中看到的那样将StrictHostKeyChecking
设置为否。
我想获取远程服务器指纹,将其显示给用户以供接受。 这是否可能使用JSch或常规Java,也许使用套接字?
下面是您可以尝试的示例,只需将其粘贴到 Android 活动的onCreate
中即可:
new Thread(new Runnable() {
@Override
public void run() {
com.jcraft.jsch.Session session;
JSch jsch;
try {
jsch = new JSch();
jsch.setLogger(new MyLogger());
session = jsch.getSession("git", "github.com", 22);
session.setPassword("hunter2");
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "yes");
session.setConfig(prop);
//**Get a host key and show it to the user**
session.connect(); // reject HostKey: github.com
}
catch (Exception e){
LOG.error("Could not JSCH", e);
}
}
}).start();
好的,我已经找到了一种方法。 这可能不是最好的方法,但这是一种方法。 使用UserInfo.promptYesNo
需要循环,代价是在等待用户响应时牺牲 CPU 或执行器/未来任务/阻塞队列的开销。 相反,执行连接的异步线程(因为网络任务不能在 UI 线程上发生)更有利于执行此操作两次 - 一次是"中断"并让用户接受,第二次是成功。 我想这就是"安卓方式"。 为此,主机密钥需要存储在某个地方。 假设我将其存储在Android的PpreferencesManager中,然后从那里开始获取密钥,如果不可用,则默认为空。
String keystring = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("target_hostkey","");
if(!Strings.isNullOrEmpty(keystring)){
byte[] key = Base64.decode ( keystring, Base64.DEFAULT );
jsch.getHostKeyRepository().add(new HostKey("github.com", key ), null);
}
接下来,像往常一样继续连接到服务器
session = jsch.getSession("git", "github.com", 22);
session.setPassword("hunter2");
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "yes");
session.setConfig(prop);
session.connect();
但这一次,赶上JSchException
. 在那里,会话有一个可用的主机密钥。
catch(final JSchException jex){
LOG.debug(session.getHostKey().getKey());
final com.jcraft.jsch.Session finalSession = session;
runOnUiThread(new Runnable() {
@Override
public void run() {
new MaterialDialog.Builder(MyActivity.this)
.title("Accept this host with fingerprint?")
.negativeText(R.string.cancel)
.positiveText(R.string.ok)
.content(finalSession.getHostKey().getFingerPrint(jsch))
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit().putString("target_hostkey", finalSession.getHostKey().getKey()).apply();
}
}).show();
}
});
}
在此之后,需要重新调用 Thread 或 AsyncTask,但这次主机密钥被添加到 JSch 的主机密钥存储库中。
两种可能性:
-
当
StrictHostKeyChecking
设置为ask
时,JSch 会调用UserInfo.promptYesNo
并显示确认提示。实现UserInfo
接口以向用户显示确认。缺点是您无法以任何方式自定义消息(当然,除非您尝试解析它,依靠硬编码模板)。消息如下:
警告:远程主机标识已更改!
有可能有人在做一些讨厌的事情!
现在可能有人在偷听你(中间人攻击)!
也可能是 -key_type- 主机密钥刚刚更改。
远程主机发送的 -key_type- 密钥的指纹是
key_fprint-
请联系您的系统管理员。
在 -file- 中添加正确的主机密钥以删除此消息。有关示例实现,请参阅官方 JSch
KnownHosts.java
示例。 -
甚至在上述之前,JSch 调用
HostKeyRepository.check
,传递主机名和密钥。您可以实现该接口/方法,以执行您喜欢的任何提示。
检查Session.checkHost
实现。