Abstract
这篇文章会介绍一些常见的使用JSch中的一些问题. 都是在实际客户运行环境中发现的问题.
JSch是一个用Java实现的与SSH服务器交互的库. 但是这个库本身已经很久没有更新了.
一般的测试代码:
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Logger;
import com.jcraft.jsch.Session;
import java.io.InputStream;
import java.util.Date;
public class TestJSch {
public static String host = "",
user = "",
pass = "";
static int port = 22;
public static void main(String[] args) throws Exception {
if (args.length < 3) {
System.out.println("Missing param. Run with [host, user, pass]");
return;
} else {
host = args[0];
user = args[1];
pass = args[2];
}
JSch.setLogger(new Logger() {
@Override
public boolean isEnabled(int i) {
return true;
}
@Override
public void log(int i, String s) {
System.out.println(new Date() + " " + s);
}
});
System.out.println("Connect " + host);
try {
String command = "cat /proc/diskstats";
String command_output = runCommand(command);
System.out.println(command_output);
}
catch (Exception e) {
e.printStackTrace();
}
}
static String runCommand(String input_command) throws Exception {
Session session = null;
try {
// instantiate JSCH object.
JSch jsch = new JSch();
// create session.
session = jsch.getSession(user, host, port);
session.setPassword(pass);
// given we are running non-interactively, we will automatically accept new host keys.
session.setConfig("StrictHostKeyChecking", "no");
// is host configured with a user & password?
// connect
session.connect();
// execute command.
ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(input_command);
// collect command output.
InputStream commandOutput = channel.getInputStream();
channel.connect();
StringBuffer s = new StringBuffer();
byte[] buf = new byte[256];
int n = -1;
while ((n = commandOutput.read(buf)) > 0) {
s.append(new String(buf, 0, n));
}
// disconnect
channel.disconnect();
return s.toString();
}
catch (Exception e) {
e.printStackTrace();
return "";
}
// ensure we disconnect the session.
finally {
if (session != null) {
session.disconnect();
}
}
}
}
Case 1 认证方式导致的失败
在连接某些客户端时,发现连接被一直卡住了. 并且没有任何输出. 系统自带ssh可以连接.
这个一般是因为jsch的认证方法有很多种, 而其中很多是需要UI交互操作的 而这在后台程序中就会出现问题.
默认的顺序:
session.setConfig("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password");
其中: gssapi-with-mic,keyboard-interactive 都是需要交互的.
所以可以按照如下设置:
session.setConfig("PreferredAuthentications", "password,gssapi-with-mic,publickey,keyboard-interactive");
Case 2: jsch 升级导致的不工作
从JSch0.1.53 升级到 0.1.54的过程中导致的某些Cisco 设备不工作.
报错是:
java.io.IOException: failed to initialize the channel.
at com.jcraft.jsch.Channel$1.init(Channel.java:242)
at com.jcraft.jsch.Channel$1.write(Channel.java:253)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)
at java.io.BufferedWriter.flush(BufferedWriter.java:254)
查看Changelog 发现:
Changes since version 0.1.53:
- bugfix: fixed CVS-2016-5725
Refer to following links,
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2016-5725
https://github.com/tintinweb/pub/tree/master/pocs/cve-2016-5725
Thanks a lot for tintinweb's contributions.
- bugfix: sftp-put may send the garbage data in some rare case.
- bugfix: fixed a deadlock bug in KnownHosts#getHostKey().
- bugfix: SftpProgressMonitor#init() was not invoked in sftp-put
by using the output-stream.
- change: KnownHosts#setKnownHosts() should accept the non-existing file.
- change: excluding the user interaction time from the timeout value.
- change: addressing SFTP slow file transfer speed with Titan FTP.
- change: updating copyright messages; 2015 -> 2016
并没有看到哪里改了. 实际对比代码发现是因为packet size改变了 导致在老的设备上无法工作:
这个是我格式化后的代码(存储在我的github: https://github.com/gaoxingliang/JSch)
可以看到这个最大mac 长度改变了 导致对老设备的兼容性上出现了.
解决办法1: 使用我改过后的基于0.1.54 修改的版本 (https://github.com/gaoxingliang/JSch/releases)
2. 在我看了0.1.53和0.1.54之间的修改过后, 主要fix 了一个CVE问题, 然后我在0.1.53上也Fix了这个问题. 也可以在这里下载.