SSH соединение с использованием Java
Я пытаюсь установить SSH-соединение через мой Java-код, но получаю исключение ниже. Я проверил свое соединение с помощью инструментов 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);
}
5 ответов
мотивация
Я наткнулся на этот вопрос и ответы во время расследования рассматриваемой ошибки 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 г.), шифры по умолчанию и MAC были изменены для удаления небезопасных алгоритмов, которые включают blowfish-cbc
шифр. А с OpenSSH 6.9 (выпущена в июне 2015 г.) поддержка 1024-битной diffie-hellman-group1-sha1
Обмен ключами по умолчанию отключен.
Когда вы все еще используете доисторические инструменты SSH j2ssh
библиотека (не дай бог) при подключении к более новому серверу OpenSSH вы получите описанную ошибку. Код библиотеки предлагает только 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 был выпущен в 2016-09-03 на MavenCentral) и, безусловно, стоит вашего рассмотрения. В качестве альтернативы вы можете рассмотреть также sshj или ssh2j-maverick.
Приложение: Миграция
Чтобы сохранить усилия по миграции для sshtools.j2ssh
( sshtools: j2ssh-core: 0.2.9) минимальная библиотека Я посмотрел коммерческую устаревшую клиентскую библиотеку SSH из SSHTOOLS (версия 1.7.1). Это позволило сохранить существующий код интеграции библиотеки с небольшими изменениями, касающимися библиотечного API и обработки исключений. Таким образом, если вы не хотите перезапускаться с нуля, то, вероятно, лучшим вариантом будет прикус пули и использование SSHTOOLS. Наконец, для оценки процесса миграции я сначала заменил библиотеку SSHTOOLS с открытой исходной библиотекой ssh2j-maverick, которая имеет почти тот же API, что и ее последняя коммерческая версия (версия 1.7.1).
Эта ошибка ("Поток транспортного протокола не выполнен. Java.io.IOException: сокет EOF") возникает, когда файл j2ssh.jar несовместим с текущей версией SSH сервера SFTP.
Вы можете попробовать использовать Java Secure Channel (JSch) здесь.
Предоставлено: http://techydiary.com/the-transport-protocol-thread-failed-java-io-ioexception-the-socket-is-eof/
Следующий пример кода может помочь вам,
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;
}
}
нашел простое решение на ОС: закомментировать строку Cipher в /etc/ssh/sshd_config и запустить сервис sshd restart
Вот рабочий код, повторно использованный из какого-то источника Google -
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();