即使服务器的主机密钥存在于 known_hosts 文件中,JSch 连接也会因 UnknownHostKey 而失败

JSch connection fails with UnknownHostKey even when the server's hostkey is present in the known_hosts file

我正在使用 JSch 在多个 VPS 上部署各种文件。我能够在 StrictHostKeyChecking 关闭的情况下得到一个工作原型。我现在想重新启用主机密钥检查,这样我就不会受到 MITM 攻击。当前,客户端是连接到 VPS 运行 Debian 的 Windows 机器。这是我到目前为止所做的:

  1. 使用 "ssh-keyscan -t rsa <serverIp> >> ~/.ssh/known_hosts"
  2. 在我的本地计算机(Windows 客户端)上添加了远程 IP 地址
  3. 在我的应用程序中将 known_hosts 文件的路径传递给 JSch.setKnownHosts

尝试建立连接时,结果是

com.jcraft.jsch.JSchException: UnknownHostKey: . RSA key fingerprint is

这显然是因为我对主机密钥的工作原理或密码学缺乏了解。根据我的基本理解,known_hosts 文件包含一个密钥。该密钥用于确保我们连接到的远程 IP 是他们所说的那个人,因此防止任何人试图将自己“欺骗”为服务器。

我的 known_hosts 文件看起来像

184.154.70.174 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNv4m+tOZUipp8UDGrd+kbtsM5R+tu3ZYZi3p7OTRWUX/Wqy74pONlLqI+/WGu77EHnOfdssJfclgo37vKLRFKneXZNAMXE7FUu5yUNOHlpPwzmvYUT/sp1k9CeNrtJAbkm05pOBIDqDQQGfQ+IAw9zqqo/sqJ6c8NKiVFAt4Ud0msvedb559dhYgcjwb52ABbsJ0mZ8FnU7LKG1592/ZTtYxam+M3qMhtJacrh5gpfZjjx2lGhqpOgvM+xwWeK6DQVn0QyIJd474G3gcm4M43ErRfzXOum3p/0wOw+hL1ora9eWSz2Wf9WuDXf86xkbZPD7Gy6ER5LBhquy331p7X

我的代码是(SSH 是 JSch 的一个实例):

try {
    SSH.setKnownHosts(new FileInputStream("C:/Users/nvulc/.ssh/known_hosts"));
    SSH.addIdentity(keyPath, keyPass);
    Session session = SSH.getSession("root", "184.154.70.174", 22);
    session.connect();
} catch (Exception e) {
    e.printStackTrace();
}

这导致:

    com.jcraft.jsch.JSchException: UnknownHostKey: 184.154.70.174. RSA key fingerprint is b4:79:5a:58:d3:15:ad:a9:c7:af:cc:d7:09:f5:40:62
    at com.jcraft.jsch.Session.checkHost(Session.java:805)
    at com.jcraft.jsch.Session.connect(Session.java:345)
    at com.jcraft.jsch.Session.connect(Session.java:183)
    at Main.main(Main.java:40)

您的方法是正确的,如果操作正确,应该会奏效。尽管仅供参考,它并不能完全防止 MitM -- ssh-keyscan 本身使用未经验证的连接并且容易受到 MitM 的攻击,尽管 if 该连接是合法的,但检查密钥可以防止伪造在以后 个连接上。这是 SSH 通用 'ToFU'(首次使用信任)安全模型的 form/variant。

From my basic understanding, the known_hosts file contains a key. ...

通常,known_hosts 包含从主机身份(名称 and/or IP 地址)到密钥的 映射。但是如果你的文件是按照你显示的那样创建的,它只有一个映射条目,只包含一个键。

  1. 确保将正确的路径传递给 setKnownHosts 运行 作为正确的(同一)用户。如果您指定的路径无法打开(不存在或不允许访问),Jsch 不会抛出任何错误,它只是 returns 而不加载任何内容。您可以使用 new FileInputStream(pathstring) 自己打开文件并传递给 setKnownHosts(InputStream) 重载,因此如果打开失败,您会得到一个异常。

  2. 确保您使用的是同一主机身份。如果例如一个主机有多个名称(就像云服务器经常做的那样),你在 ssh-keyscan 中使用一个,但在 JSch 中使用不同的名称,那么即使这实际上是相同的主机和密钥,它也不会匹配。但是,如果您实际上在两个地方都使用 IP 地址,至少如果您指的是 IPv4,则这种可能性较小,因为如今很少有机器具有多个 public IPv4 地址。 (过去这更常见,称为多宿主。) IPv6 更有可能;大多数 IPv6 机器都有临时和永久 public 地址(以及 local/private 地址),并且通常有多个临时地址。

您的代码对我有用 – https://www.browxy.com/#ALIEN_137442 – 它以 "JSchException: Auth fail" 结尾 – 这意味着它通过了主机密钥验证。

确保 known_hosts 文件采用纯 ASCII 编码(或 UTF-8 BOM,对于此类内容,它应该与 ASCII 相同)。但不是UTF-8 with BOM,让只有UTF-16甚至更糟。

也试试 Unix 行结尾。虽然行尾应该不是问题。