JSch - 如何让用户确认主机指纹?



在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 的主机密钥存储库中。

两种可能性:

  1. StrictHostKeyChecking设置为ask时,JSch 会调用UserInfo.promptYesNo并显示确认提示。实现UserInfo接口以向用户显示确认。缺点是您无法以任何方式自定义消息(当然,除非您尝试解析它,依靠硬编码模板)。

    消息如下:

    警告:远程主机标识已更改!
    有可能有人在做一些讨厌的事情!
    现在可能有人在偷听你(中间人攻击)!
    也可能是 -key_type- 主机密钥刚刚更改。
    远程主机发送的 -key_type- 密钥的指纹是
    key_fprint-
    请联系您的系统管理员。
    在 -file- 中添加正确的主机密钥以删除此消息。

    有关示例实现,请参阅官方 JSchKnownHosts.java示例。

  2. 甚至在上述之前,JSch 调用HostKeyRepository.check,传递主机名和密钥。

    您可以实现该接口/方法,以执行您喜欢的任何提示。


检查Session.checkHost实现。

最新更新