使用 Java 的 SSH 连接

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35307393/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 23:59:34  来源:igfitidea点击:

SSH connection using Java

javasshssh-keysj2ssh

提问by Karthick Sambanghi

I am trying to establish an SSH connection through my Java code, but getting below exception .. I tested my connection through Putty/Winscp tools and it works fine. The problem is with my Java code...

我试图通过我的 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)

Below is my piece of Java code to establish the connection

下面是我建立连接的一段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);
    }

回答by Ivo Mori

Motivation

动机

I stumbled across this question and answers while investigating the error in question java.io.IOException: The socket is EOF. Because changing the code to use some other SSH Java library is not immediately possible in my case and the stated explanation by @a3.14_Infinity was not detailed enough for me, I'd like to add my take on it.

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

java.io.IOException: The socket is EOF - Why?

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

Because this exception is not very helpful, I first tried Wiresharkto see what's going on over the wire, but to no avail. So I configured the sshd_config(OpenSSH 6.9) to log on DEBUG3level and got the answer in the /var/log/auth.logfile of my test machine. It stated a fatal errorwhile trying to negotiate the key exchange algorithm with the SSH client (the Java SSH library).

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

Because the SSH server and client could not agree on a mutual key exchange algorithm the OpenSSH server terminates the connection to the client. In consequence, the Java SSH library code throws the exception.

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

But why does it happen?

但为什么会发生呢?

The sshtools.j2ssh(sshtools : j2ssh-core : 0.2.9) library code is pretty old and discontinued. Starting with OpenSSH 6.7(released October, 2014) default ciphers and MAC have been altered to remove unsafe algorithms which includes the blowfish-cbccipher. And with OpenSSH 6.9(released June, 2015) the support for the 1024-bit diffie-hellman-group1-sha1key exchange is disabled by default.

sshtools.j2sshsshtools:J2SSH核心:0.2.9)库中的代码是很老和停产。从OpenSSH 6.7(2014 年 10 月发布)开始,默认密码和 MAC 已被更改,以删除包括blowfish-cbc密码在内的不安全算法。对于OpenSSH 6.9(2015 年 6 月发布)diffie-hellman-group1-sha1,默认情况下禁用了对 1024 位密钥交换的支持。

When you still use the prehistoric SSH Tools j2sshlibrary (God forbid) connecting to a newer OpenSSH server you will get the described error. The library code only offers the diffie-hellman-group1-sha1key exchange algorithm to the OpenSSH server which it does not support by default. Thus, a secure connection cannot be established.

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

Cannot change the code?

不能改代码?

If moving to another Java SSH library is not immediately possible (my case) then you can re-enable the disabled diffie-hellman-group1-sha1key exchange algorithm in the OpenSSH's server config file sshd_config. For example like this.

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

Ciphers aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],[email protected],blowfish-cbc

KexAlgorithms diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,[email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1

But let me be clear on this. The diffie-hellman-group1-sha1key exchange algorithm as well as the blowfish-cbccipher are turned off by default because they are insecure. Reenabling them should only be a temporary measure until you can replace this obsolete Java SSH library.

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

Finally, I like to point out that the suggested Java Secure Channel (JSch)library in other answers is discontinued. So, you might want to consider sshjor even ssh2j-maverickinstead.

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

Edit: I was wrong, the Java Secure Channel JSchlibrary is alive (JSCH 0.1.54 was released on 2016-09-03 on MavenCentral) and certainly worth your consideration. Alternatively, you may want to consider also sshjor ssh2j-maverick.

编辑:我错了Java 安全通道JSch库还活着(JSCH 0.1.54 于 2016 年 9月 3日在 MavenCentral 上发布),当然值得您考虑。或者,您可能还想考虑sshjssh2j-maverick

Addendum: Migration

附录:迁移

To keep the migration effort for the sshtools.j2ssh(sshtools : j2ssh-core : 0.2.9) library minimal I looked at the commerciallegacy SSH client library from SSHTOOLS(version 1.7.1). This allowed to keep the existing library integration code with few minor changes regarding library API and exception handling. Thus, if you do not want to restart from scratch then biting the bullet and sticking with SSHTOOLS is probably your best option. Finally, to gauge the migration effort I first replaced the library with SSHTOOLS' open source library ssh2j-maverickwhich almost has the same API as its latest commercial version (version 1.7.1).

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

回答by a3.14_Infinity

This error ("The Transport Protocol thread failed. java.io.IOException: The socket is EOF”) occurs when j2ssh.jar file is not compatible with current SSH version of SFTP server.

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

You can try using Java Secure Channel (JSch) from here.

您可以从这里尝试使用 Java 安全通道 (JSch) 。

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

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

回答by Kajal

The following sample Code may help you,

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

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;
    }
}

回答by masvel

Here is the working code reused from some google source -

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

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();