JSch发送SSH_MSG_KEXINIT后无进展,SFTP连接超时求助
针对你遇到的JSch(0.1.55)连接Serv-U 15.3.2 SFTP服务器时,发送SSH_MSG_KEXINIT后停滞超时的问题,结合其他工具可正常连接、此前代码可用的背景,提供以下排查和解决思路:
确认JSch实际运行版本:日志显示本地版本为
JSCH-0.1.54,但你声明使用的是0.1.55,可能存在依赖冲突(比如项目中引入了旧版本JSch包)。先确保运行环境加载的是0.1.55版本,或直接升级到较新的稳定版(如0.1.59),旧版本可能对Serv-U新增的密钥交换算法支持不足。强制指定Serv-U兼容的密钥交换(KEX)算法:
先通过WinSCP查看会话日志,找到成功连接时使用的KEX算法(比如WinSCP日志会显示Key exchange: diffie-hellman-group14-sha256),然后在JSch配置中优先指定该算法:config.put("kex", "diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,ecdh-sha2-nistp256");若Serv-U禁用了ECDH系列算法,可尝试只保留DH系列:
config.put("kex", "diffie-hellman-group14-sha256,diffie-hellman-group14-sha1");同步加密/消息认证(MAC)算法配置:
Serv-U可能对加密算法有特定偏好,参考WinSCP使用的加密和MAC算法,补充到JSch配置中:config.put("cipher.s2c", "aes256-gcm,aes128-gcm,aes256-ctr,aes128-ctr"); config.put("cipher.c2s", "aes256-gcm,aes128-gcm,aes256-ctr,aes128-ctr"); config.put("mac.s2c", "hmac-sha2-256,hmac-sha1"); config.put("mac.c2s", "hmac-sha2-256,hmac-sha1");开启JSch详细调试日志:
默认日志仅显示阶段信息,需自定义Logger打印完整SSH交互细节,确认Serv-U是否回复了SSH_MSG_KEXINIT,或是否存在算法协商失败的情况:session.setLogger(new com.jcraft.jsch.Logger() { @Override public boolean isEnabled(int level) { return true; } @Override public void log(int level, String message) { System.out.println("JSch DEBUG: " + message); } });排查服务器端变更:
既然此前代码可用,近一月出现问题,大概率是Serv-U服务器或网络层(防火墙/IPS)做了变更:- 联系服务器管理员确认是否禁用了旧的KEX/加密算法;
- 在Docker容器中使用
tcpdump抓取JSch和sftp命令的SSH交互数据包,对比差异,排查是否存在数据包被拦截或协议格式不兼容的情况。
调整StrictHostKeyChecking设置:
虽然当前问题出在KEX阶段,但可尝试修改该配置(比如设为ask或删除该配置项),排除主机密钥验证环节的隐性影响。
修改后的测试代码示例:
JSch jsch = new JSch(); java.util.Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); config.put("PreferredAuthentication", "password,keyboard-interactive"); // 新增KEX和加密算法配置 config.put("kex", "diffie-hellman-group14-sha256,diffie-hellman-group14-sha1"); config.put("cipher.s2c", "aes256-gcm,aes128-gcm,aes256-ctr,aes128-ctr"); config.put("cipher.c2s", "aes256-gcm,aes128-gcm,aes256-ctr,aes128-ctr"); try { Session session = jsch.getSession(user, host, 22); session.setPassword(password); session.setConfig(config); // 开启详细调试日志 session.setLogger(new com.jcraft.jsch.Logger() { @Override public boolean isEnabled(int level) { return true; } @Override public void log(int level, String message) { System.out.println("JSch DEBUG: " + message); } }); session.connect(30 * 1000); session.disconnect(); } catch (Exception e) { e.printStackTrace(); }
内容的提问来源于stack exchange,提问作者Darren Mossman

