【问题标题】:SSH connection using Java使用 Java 的 SSH 连接
【发布时间】:2016-05-20 08:15:12
【问题描述】:

我正在尝试通过我的 Java 代码建立 SSH 连接,但遇到异常。我通过 Putty/Winscp 工具测试了我的连接,它工作正常。问题出在我的 Java 代码上...

SEVERE: The Transport Protocol thread failed
java.io.IOException: The socket is EOF
    at com.sshtools.j2ssh.transport.TransportProtocolInputStream.readBufferedData(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolInputStream.readMessage(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.readMessage(Unknown Source)
    at com.sshtools.j2ssh.transport.kex.DhGroup1Sha1.performClientExchange(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolClient.performKeyExchange(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.beginKeyExchange(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.onMsgKexInit(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.startBinaryPacketProtocol(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

下面是我建立连接的一段Java代码

public class MySSHClient {

  static SshClient ssh = null;
  static SshConnectionProperties properties = null;
  SessionChannelClient session = null;

  private static void MySSHClient(String hostName, String userName, String passwd )
  {

    try
    {
      // Make a client connection
      ssh = new SshClient();
      properties = new SshConnectionProperties();
      properties.setHost("192.168.1.175");

      // Connect to the host
      ssh.connect(properties, new IgnoreHostKeyVerification());

      // Create a password authentication instance
      PasswordAuthenticationClient pwd = new PasswordAuthenticationClient();

      pwd.setUsername("root");
      pwd.setPassword("123456");

      // Try the authentication
      int result = ssh.authenticate(pwd);

      // Evaluate the result
      if (result==AuthenticationProtocolState.COMPLETE) {

        System.out.println("Connection Authenticated");
      }
    }
    catch(Exception e)
    {
      System.out.println("Exception : " + e.getMessage());
    }

  }//end of method.


  public String execCmd(String cmd)
  {
    String theOutput = "";
    try
    {
      // The connection is authenticated we can now do some real work!
      session = ssh.openSessionChannel();

      if ( session.executeCommand(cmd) )
      {
        IOStreamConnector output = new IOStreamConnector();
        java.io.ByteArrayOutputStream bos =  new
        java.io.ByteArrayOutputStream();
        output.connect(session.getInputStream(), bos );
        session.getState().waitForState(ChannelState.CHANNEL_CLOSED);
        theOutput = bos.toString();
      }
      //else
      //throw Exception("Failed to execute command : " + cmd);
      //System.out.println("Failed to execute command : " + cmd);
    }
    catch(Exception e)
    {
      System.out.println("Exception : " + e.getMessage());
    }

    return theOutput;
  }

  public static void main(String[] args){
      MySSHClient(null, null, null);
    }

【问题讨论】:

  • 尝试使用jcraft.com/jsch
  • 老实说,异常和贴出的代码好像不匹配,也没有使用execCmd
  • linux主机上ssh的版本是多少?
  • [root@centos-test ssh]# ssh -v localhost OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 2013 年 2 月 11 日 debug1:读取配置数据 /etc/ssh/ssh_config debug1:应用选项* debug1:连接到 localhost [::1] 端口 22。 debug1:已建立连接。 debug1:permanent_set_uid:0/0 debug1:身份文件/root/.ssh/身份类型-1 debug1:身份文件/root/.ssh/identity-cert类型-1 debug1:身份文件/root/.ssh/id_rsa类型- 1 debug1:远程协议版本2.0,远程软件版本
  • 您的问题在这里有几个答案——请接受一个(如果它回答了您的问题),这样您的问题就不会在问题搜索中显示为未回答

标签: java ssh ssh-keys j2ssh


【解决方案1】:

动机

我在调查问题java.io.IOException: The socket is EOF 的错误时偶然发现了这个问题和答案。因为在我的情况下无法立即更改代码以使用其他一些 SSH Java 库,而且@a3.14_Infinity 的说明对我来说不够详细,所以我想补充一下我的看法。

java.io.IOException: 套接字是 EOF - 为什么?

因为这个异常不是很有帮助,我首先尝试Wireshark 来查看网络上发生了什么,但无济于事。于是我将sshd_config(OpenSSH 6.9)配置为登录DEBUG3级别,并在我的测试机的/var/log/auth.log文件中得到了答案。它在尝试与 SSH 客户端(Java SSH 库)协商密钥交换算法时声明了一个致命错误

由于 SSH 服务器和客户端无法就相互密钥交换算法达成一致,OpenSSH 服务器终止与客户端的连接。因此,Java SSH 库代码会引发异常。

但为什么会这样呢?

sshtools.j2ssh (sshtools : j2ssh-core : 0.2.9) 库代码已经过时且已停止使用。从 OpenSSH 6.7(2014 年 10 月发布)开始,默认密码和 MAC 已被更改以删除不安全的算法,其中包括 blowfish-cbc 密码。 OpenSSH 6.9(2015 年 6 月发布)默认禁用对 1024 位 diffie-hellman-group1-sha1 密钥交换的支持。

当您仍然使用史前 SSH 工具 j2ssh 库(上帝保佑)连接到较新的 OpenSSH 服务器时,您将收到所描述的错误。库代码仅向 OpenSSH 服务器提供 diffie-hellman-group1-sha1 密钥交换算法,默认情况下它不支持。因此,无法建立安全连接。

无法更改代码?

如果不能立即迁移到另一个 Java SSH 库(我的情况),那么您可以在 OpenSSH 的服务器配置文件 sshd_config 中重新启用禁用的 diffie-hellman-group1-sha1 密钥交换算法。比如这样。

Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,blowfish-cbc

KexAlgorithms diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1

但是让我明确一点。 diffie-hellman-group1-sha1 密钥交换算法和blowfish-cbc 密码默认关闭,因为它们不安全。重新启用它们应该只是一种临时措施,直到您可以替换这个过时的 Java SSH 库。

最后,我想指出的是,其他答案中建议的 Java Secure Channel (JSch) 库已停止使用。因此,您可能需要考虑使用sshj 甚至ssh2j-maverick

编辑:我错了Java Secure Channel JSch 库还活着(JSCH 0.1.54 于 2016-09-03 on MavenCentral 发布)当然值得您考虑。或者,您也可以考虑sshjssh2j-maverick

附录:迁移

为了尽量减少 sshtools.j2ssh (sshtools : j2ssh-core : 0.2.9) 库的迁移工作量,我查看了来自 SSHTOOLS(1.7 版)的商业旧版 SSH 客户端库.1)。这允许保留现有的库集成代码,只需对库 API 和异常处理进行少量更改。因此,如果您不想从头开始重新启动,那么硬着头皮坚持使用 SSHTOOLS 可能是您的最佳选择。最后,为了衡量迁移工作,我首先用 SSHTOOLS 的开源库 ssh2j-maverick 替换了该库,该库几乎具有与其最新商业版本(版本 1.7.1)相同的 API。

【讨论】:

    【解决方案2】:

    当 j2ssh.jar 文件与当前 SSH 版本的 SFTP 服务器不兼容时,会发生此错误(“传输协议线程失败。java.io.IOException: The socket is EOF”)。

    您可以尝试使用来自here 的 Java 安全通道 (JSch)。

    礼貌:http://techydiary.com/the-transport-protocol-thread-failed-java-io-ioexception-the-socket-is-eof/

    【讨论】:

    • 感谢 Infinity,我会尝试使用 JSCH。
    • @KarthickSambanghi:很高兴它有帮助。稍后您可以接受这个答案:)
    【解决方案3】:

    以下示例代码可能会对您有所帮助,

    import java.io.InputStream;
    import org.apache.commons.io.IOUtils;
    
    
    import com.jcraft.jsch.Channel;
    import com.jcraft.jsch.ChannelExec;
    import com.jcraft.jsch.JSch;
    import com.jcraft.jsch.Session;
    
    
    public class SSHClient {
    
        /**
         * Constant EXCUTE_CHANNEL
         */
        public static final String EXCUTE_CHANNEL = "exec";
    
        /**
         * Constant STRICT_KEY_CHECKING
         */
        public static final String STRICT_KEY_CHECKING = "StrictHostKeyChecking";
    
    
        /** Name/ip of the remote machine/device **/ 
        private String host;
        private String userName;
        private String password;
    
        /**
         * This method used to initilze user and host
         * 
         * @param userName
         * @param password
         * @param host
         */
        public SSHClient(String userName,String password, String host) {
            super();
            this.userName = userName;
            this.password = password;
            this.host = host;
        }
    
        /**
         * This method used to execute commands remotly by using SSHV2
         * 
         * @param host
         * @param username
         * @param password
         * @param command
         * @return
         */
        public String executeCommand(String command) { 
            StringBuilder log = new StringBuilder();
            String response = null;
            Channel channel = null;
            Session session = null;
    
            try {
                JSch jsch = new JSch();
                JSch.setConfig(STRICT_KEY_CHECKING, Constants.NO);
    
                session = jsch.getSession(userName, host, 22);
    
    
                // If two machines have SSH passwordless logins setup, the following
                // line is not needed:
                session.setPassword(password);
                session.connect();
    
                channel = session.openChannel(EXCUTE_CHANNEL);
                ((ChannelExec) channel).setCommand(command);
    
                // channel.setInputStream(System.in);
                channel.setInputStream(null);
    
                ((ChannelExec) channel).setErrStream(System.err);
    
                InputStream in = channel.getInputStream();
    
                channel.connect();
    
                response = IOUtils.toString(in);
    
            } catch (Exception ex) {
                    //handle exception
            } finally {
                try {
                    if (session != null) {
                        session.disconnect();
                    }
                } catch (Exception ex) {
                    //handle exception
                }
                try {
                    if (channel != null) {
                        channel.disconnect();
                    }
                } catch (Exception ex) {
                    //handle exception
                }
    
            }
            System.ou.println( "Response received :"+  response));
            return response;
        }
    }
    

    【讨论】:

      【解决方案4】:

      这是从一些谷歌源重用的工作代码 -

      import ch.ethz.ssh2.Connection;
      import ch.ethz.ssh2.StreamGobbler;
      
      Connection conn = new Connection(server);
      conn.connect();
      boolean isAuthenticated = conn.authenticateWithPassword(user_id, password);
      
      System.out.println("Is server - " + server + " Authenticated? " + isAuthenticated);
      
      if (isAuthenticated == false)
          throw new IOException("Authentication failed.");
      
      ch.ethz.ssh2.Session sess = conn.openSession();
      String new_commands = "";
      
      for (int i = 0; i < commands.size(); i++) {
          new_commands = new_commands + commands.get(i) + "\n";
      }
      
      System.out.println("The command executed is: " + new_commands);
      
      sess.requestDumbPTY();
      sess.execCommand(new_commands);
      InputStream stdout = new StreamGobbler(sess.getStdout());
      BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
      InputStream errStrm = new StreamGobbler(sess.getStderr());
      BufferedReader stderrRdr = new BufferedReader(new InputStreamReader(errStrm));
      sess.getStdin().write("EXIT\n".getBytes());
      
      System.out.println("the output of the command is");
      
      while (true) {
          String line_out = br.readLine();
          if (line_out == null) {
              break;
          } else {
              System.out.println(line_out);
              output_logs.add(line_out);
          }
      }
      
      while (true) {
          String line_error = stderrRdr.readLine();
          if (line_error == null) {
              break;
          } else {
              System.out.println(line_error);
              output_logs.add(line_error);
          }
      }
      
      output_logs.add("Exit Code:" + sess.getExitStatus());
      System.out.println("ExitCode: " + sess.getExitSignal());
      
      sess.close();
      conn.close();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-05
        • 2016-06-27
        • 2014-10-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多