199

我正在尝试使用Jsch在 Java 中建立 SSH 连接。我的代码产生以下异常:

com.jcraft.jsch.JSchException: UnknownHostKey: mywebsite.com. 
RSA key fingerprint is 22:fb:ee:fe:18:cd:aa:9a:9c:78:89:9f:b4:78:75:b4

我在 Jsch 文档中找不到如何验证主机密钥。我在下面包含了我的代码。

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

public class ssh {
    public static void main(String[] arg) {

        try {
            JSch jsch = new JSch();

            //create SSH connection
            String host = "mywebsite.com";
            String user = "username";
            String password = "123456";

            Session session = jsch.getSession(user, host, 22);
            session.setPassword(password);
            session.connect();

        } catch(Exception e) {
            System.out.println(e);
        } 
    }
}
4

13 回答 13

252

我会:

  1. 尝试ssh从命令行并接受公钥(主机将被添加到~/.ssh/known_hosts,然后一切都应该从 Jsch 正常工作)-或-
  2. 使用以下代码将 JSch 配置为不使用“StrictHostKeyChecking”(这会引入不安全性,仅应用于测试目的):

    java.util.Properties config = new java.util.Properties(); 
    config.put("StrictHostKeyChecking", "no");
    session.setConfig(config);
    

选项#1(将主机添加到~/.ssh/known_hosts文件)有我的偏好。

于 2010-01-05T00:57:12.840 回答
65

虽然这个问题已经得到了普遍的回答,但我发现自己有一种情况,即使现有的known_hosts条目也无济于事。当 SSH 服务器发送 ECDSA 指纹时会发生这种情况,因此,您将拥有如下条目:

|1|+HASH=|HASH= ecdsa-sha2-nistp256 FINGERPRINT=

问题是 JSch更喜欢SHA_RSA 并且在连接时它会尝试比较 SHA-RSA 指纹,这将导致有关“未知主机”的错误。

要解决此问题,只需运行:

$ ssh-keyscan -H -t rsa example.org >> known_hosts

或者向Jcraft抱怨更喜欢 SHA_RSA 而不是使用本地HostKeyAlgorithms设置,尽管他们似乎不太急于修复他们的错误

于 2017-06-27T09:53:32.300 回答
44

避免主机密钥检查存在安全风险。

JSch 使用 HostKeyRepository 接口及其默认实现 KnownHosts 类来管理它。您可以通过实现 HostKeyRepository 来提供允许特定密钥的替代实现。或者您可以将要允许的密钥保留在known_hosts 格式的文件中并调用

jsch.setKnownHosts(knownHostsFileName);

或者使用如下的公钥字符串。

String knownHostPublicKey = "mysite.com ecdsa-sha2-nistp256 AAAAE............/3vplY";
jsch.setKnownHosts(new ByteArrayInputStream(knownHostPublicKey.getBytes()));

有关更多详细信息,请参阅Javadoc

这将是一个更安全的解决方案。

Jsch 是开源的,您可以从这里下载源代码。在示例文件夹中,查找 KnownHosts.java 以了解更多详细信息。

于 2012-12-24T11:13:51.543 回答
17

根据您用于 ssh 的程序,获取正确密钥的方式可能会有所不同。Putty(在 Windows 中很流行)使用自己的 ssh 密钥格式。对于我见过的大多数 Linux 和 BSD 变体,您只需查看~/.ssh/known_hosts. 我通常从 Linux 机器 ssh,然后将此文件复制到 Windows 机器。然后我使用类似的东西

jsch.setKnownHosts("C:\\Users\\cabbott\\known_hosts");

假设我已将文件C:\Users\cabbott放在我的 Windows 机器上。如果您无法访问 Linux 机器,请尝试http://www.cygwin.com/

也许其他人可以建议另一种 Windows 替代方案。我发现 putty 处理 SSH 密钥的方式是通过以非标准格式将它们存储在注册表中,这样提取起来很麻烦。

于 2012-12-24T18:41:10.453 回答
10

提供主机的公共 rsa 密钥:-

String knownHostPublicKey = "mywebsite.com ssh-rsa AAAAB3NzaC1.....XL4Jpmp/";

session.setKnownHosts(new ByteArrayInputStream(knownHostPublicKey.getBytes()));
于 2015-09-04T12:16:00.817 回答
8

你也可以简单地做

session.setConfig("StrictHostKeyChecking", "no");

它不安全,而且它是一种不适合实时环境的解决方法,因为它会禁用全球已知的主机密钥检查。

于 2014-06-20T09:40:09.653 回答
6

您还可以执行以下代码。它已经过测试并且可以正常工作。

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UIKeyboardInteractive;
import com.jcraft.jsch.UserInfo;

public class SFTPTest {

    public static void main(String[] args) {
        JSch jsch = new JSch();
        Session session = null;
        try {
            session = jsch.getSession("username", "mywebsite.com", 22); //default port is 22
            UserInfo ui = new MyUserInfo();
            session.setUserInfo(ui);
            session.setPassword("123456".getBytes());
            session.connect();
            Channel channel = session.openChannel("sftp");
            channel.connect();
            System.out.println("Connected");
        } catch (JSchException e) {
            e.printStackTrace(System.out);
        } catch (Exception e){
            e.printStackTrace(System.out);
        } finally{
            session.disconnect();
            System.out.println("Disconnected");
        }
    }

    public static class MyUserInfo implements UserInfo, UIKeyboardInteractive {

        @Override
        public String getPassphrase() {
            return null;
        }
        @Override
        public String getPassword() {
            return null;
        }
        @Override
        public boolean promptPassphrase(String arg0) {
            return false;
        }
        @Override
        public boolean promptPassword(String arg0) {
            return false;
        }
        @Override
        public boolean promptYesNo(String arg0) {
            return false;
        }
        @Override
        public void showMessage(String arg0) {
        }
        @Override
        public String[] promptKeyboardInteractive(String arg0, String arg1,
                String arg2, String[] arg3, boolean[] arg4) {
            return null;
        }
    }
}

请替换适当的值。

于 2013-01-22T11:45:32.967 回答
2

我在这个愚蠢的问题上浪费了很多时间,而且我认为消息是非常正确的“我正在访问的文件中没有主机”但是您的系统上可以有多个 know_host 文件(例如我'正在使用 mobaXterm 并将其保留在从该根目录安装主目录的安装目录中)。

如果您遇到:它是从命令行工作但不是从应用程序尝试使用 ssh 访问您的远程服务器并使用详细 -v 选项检查当前使用哪个文件,示例如下:

 ssh -v git@gitlab.com
 OpenSSH_6.2p2, OpenSSL 1.0.1g 7 Apr 2014
 debug1: Reading configuration data /etc/ssh_config
 debug1: Connecting to gitlab.com [104.210.2.228] port 22.
 debug1: Connection established.
 debug1: identity file /home/mobaxterm/.ssh/id_rsa type 1
 debug1: identity file /home/mobaxterm/.ssh/id_rsa-cert type -1
 debug1: identity file /home/mobaxterm/.ssh/id_dsa type -1
 debug1: identity file /home/mobaxterm/.ssh/id_dsa-cert type -1
 debug1: identity file /home/mobaxterm/.ssh/id_ecdsa type -1
 debug1: identity file /home/mobaxterm/.ssh/id_ecdsa-cert type -1
 debug1: Enabling compatibility mode for protocol 2.0
 debug1: Local version string SSH-2.0-OpenSSH_6.2
 debug1: Remote protocol version 2.0, remote software version OpenSSH_7.2p2      Ubuntu-4ubuntu2.1
 debug1: match: OpenSSH_7.2p2 Ubuntu-4ubuntu2.1 pat OpenSSH*
 debug1: SSH2_MSG_KEXINIT sent
 debug1: SSH2_MSG_KEXINIT received
 debug1: kex: server->client aes128-ctr hmac-sha1-etm@openssh.com zlib@openssh.com
 debug1: kex: client->server aes128-ctr hmac-sha1-etm@openssh.com zlib@openssh.com
 debug1: sending SSH2_MSG_KEX_ECDH_INIT
 debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
 debug1: Server host key: RSA b6:03:0e:39:97:9e:d0:e7:24:ce:a3:77:3e:01:42:09
 debug1: Host 'gitlab.com' is known and matches the RSA host key.
 debug1: Found key in /home/mobaxterm/.ssh/known_hosts:19
 debug1: ssh_rsa_verify: signature correct

如您所见,密钥位于:

debug1: Found key in /home/mobaxterm/.ssh/known_hosts:19

而不是在 C:\Users\ my_local_user \.ssh 下的 Windows 主页中,我只是将它们合并并对齐以解决问题。

希望这对将来的人有所帮助

于 2016-09-28T09:46:16.407 回答
1

设置已知主机优于设置指纹值。

当您设置已知主机时,尝试从应用程序运行的框中手动 ssh(第一次,在应用程序运行之前)。

于 2013-02-02T01:34:33.507 回答
1

只需替换“用户”、“通过”、“SSHD_IP”。并使用服务器的 ~/.ssh/known_hosts 的内容创建一个名为 known_hosts.txt 的文件。你会得到一个贝壳。

public class Known_Hosts {
public static void main(String[] arg) {
    try {
        JSch jsch = new JSch();
        jsch.setKnownHosts("known_hosts.txt");
        Session session = jsch.getSession("user", "SSHD_IP", 22);
        session.setPassword("pass");
        session.connect();
        Channel channel = session.openChannel("shell");
        channel.setInputStream(System.in);
        channel.setOutputStream(System.out);
        channel.connect();
    } catch (Exception e) {
        System.out.println(e);
    }
  }
}
于 2013-06-13T13:33:15.167 回答
0

有没有人能够解决这个问题?我正在使用 Jscp 使用公钥身份验证来 scp 文件(我不想使用密码身份验证)。帮助将不胜感激!!!

这个stackoverflow条目是关于主机密钥检查的,与公钥认证无关。

至于公钥身份验证,请使用您的普通(非加密)私钥尝试以下示例,

于 2012-12-25T15:19:46.387 回答
0

我在@Jairo Martínez所写的已删除答案之一中找到了解决方案

首先,服务器需要将主机存储为散列 rsa

ssh-keyscan -H -t rsa SERVER_IP_ADDRESS >> known_hosts_file

另外,需要告诉 Jsch 主机密钥是散列的

session.setConfig("HashKnownHosts", "是");

于 2021-11-22T14:28:41.830 回答
-1
JSch jsch = new JSch();
Session session = null;
try {
   session = jsch.getSession("user", "hostname", 22); // default
   UserInfo ui = new MyUserInfo();
   session.setUserInfo(ui);
   session.setPassword("password".getBytes());
   java.util.Properties config = new java.util.Properties();
   config.put("StrictHostKeyChecking", "no");
   session.setConfig(config);
   session.connect();
   Channel channel = session.openChannel("sftp");
   channel.connect();
   System.out.println("Connected");
} catch (JSchException e) {
   e.printStackTrace(System.out);
} catch (Exception e) {
   e.printStackTrace(System.out);
} finally {
   session.disconnect();
   System.out.println("Disconnected");
}
于 2013-12-20T06:43:36.490 回答