6

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

4 に答える 4

14

動機

問題のエラーを調査しているときに、この質問と回答に出くわしましたjava.io.IOException: The socket is EOF。私の場合、他のSSH Javaライブラリを使用するようにコードを変更することはすぐには不可能であり、@ a3.14_Infinityによる説明は私にとって十分に詳細ではなかったため、私の考えを追加したいと思います.

java.io.IOException: ソケットが EOF です - なぜですか?

この例外はあまり役に立たないので、最初にWiresharkを試して回線上で何が起こっているかを確認しましたが、役に立ちませんでした。そこで、sshd_config(OpenSSH 6.9)をログオンレベルに設定し、テストマシンDEBUG3のファイルで回答を得ました。鍵交換アルゴリズムを SSH クライアント (Java SSH ライブラリ) とネゴシエートしようとして/var/log/auth.logいるときに、致命的なエラーが発生しました。

SSH サーバーとクライアントが相互鍵交換アルゴリズムに同意できなかったため、OpenSSH サーバーはクライアントへの接続を終了します。その結果、Java SSH ライブラリ コードは例外をスローします。

しかし、なぜそれが起こるのですか?

( sshtools : j2ssh-core : 0.2.9sshtools.j2ssh )ライブラリ コードはかなり古く、廃止されました。OpenSSH 6.7 ( 2014年 10 月リリース) 以降、デフォルトの暗号と MAC が変更され、暗号を含む安全でないアルゴリズムが削除されました。また、OpenSSH 6.9 (2015 年 6 月リリース) では、1024 ビット鍵交換のサポートがデフォルトで無効になっています。blowfish-cbcdiffie-hellman-group1-sha1

新しい OpenSSH サーバーに接続する先史時代の SSH ツールj2sshライブラリ (神は禁じられています) を引き続き使用すると、説明されているエラーが発生します。ライブラリ コードは、diffie-hellman-group1-sha1デフォルトではサポートされていない OpenSSH サーバーに鍵交換アルゴリズムのみを提供します。したがって、安全な接続を確立できません。

コードを変更できませんか?

別の Java SSH ライブラリにすぐに移動できない場合 (私の場合) diffie-hellman-group1-sha1、OpenSSH のサーバー構成ファイルで無効になっている鍵交換アルゴリズムを再度有効にすることができますsshd_config。例えばこんな感じ。

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 は MavenCentral で 2016 年 9 月 3 日にリリースされまし)。または、 sshjまたはssh2j-maverickも検討することをお勧めします。

補遺: 移行

sshtools.j2ssh( sshtools : j2ssh-core : 0.2.9 ) ライブラリの移行作業を最小限に抑えるために、 SSHTOOLS (バージョン 1.7.1)の商用レガシー SSH クライアント ライブラリを調べました。これにより、既存のライブラリ統合コードを維持することができ、ライブラリ API と例外処理に関する小さな変更はほとんどありませんでした。したがって、最初からやり直すのが嫌なら、あきらめて SSHTOOLS に固執するのがおそらく最善の選択肢です。最後に、移行作業を評価するために、最初にライブラリを SSHTOOLS のオープン ソース ライブラリssh2j-maverickに置き換えました。これは、最新の商用バージョン (バージョン 1.7.1) とほぼ同じ API を備えています。

于 2016-04-25T21:21:07.290 に答える
1
于 2016-02-10T05:24:45.273 に答える
1

次のサンプル コードが役立つ場合があります。

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;
    }
}
于 2016-02-10T05:36:25.430 に答える