使用的Java FTPSClient和会话恢复是一个已知的问题。会话恢复是FTPS服务器为数据连接所需的一项安全功能。Apache不支持会话恢复,而FTPSClient API使构建自定义实现变得困难。有几个使用反射的解决方法,例如这个答案和这个公用网络错误条目。
我在JDK 11中使用了这样的解决方案(参见下面的片段),并在本地FileZilla服务器上测试了它。它适用于FileZilla服务器0.9.6,但不适用于FileZilla服务器1.2.0,后者是编写本报告时的最新版本。使用该版本时,当试图建立数据连接时,服务器响应如下:
425 Unable to build data connection: TLS session of data connection not resumed.
如前所述,FileZilla Server 0.9.6与我如何进行会话恢复有关,并确保激活了要求会话恢复的设置。
在FileZilla服务器1.2.0中,这些设置现在是隐式设置的,不能通过GUI进行更改,甚至根本不能更改。是否有一些服务器设置可以进行调整以使其工作?或者是我如何实现这个解决方案的问题?有没有人遇到过类似的问题?
这是我正在使用的解决方法:
public class FTPSClientWithSessionResumption extends FTPSClient {
static {
System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
System.setProperty("jdk.tls.client.enableSessionTicketExtension", "false");
}
@Override
protected void _connectAction_() throws IOException {
super._connectAction_();
execPBSZ(0);
execPROT("P");
}
@Override
protected void _prepareDataSocket_(Socket socket) throws IOException {
if (useSessionResumption && socket instanceof SSLSocket) {
// Control socket is SSL
final SSLSession session = ((SSLSocket)_socket_).getSession();
if (session.isValid()) {
final SSLSessionContext context = session.getSessionContext();
try {
final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache");
sessionHostPortCache.setAccessible(true);
final Object cache = sessionHostPortCache.get(context);
final Method putMethod = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
putMethod.setAccessible(true);
Method getHostMethod;
try {
getHostMethod = socket.getClass().getMethod("getPeerHost");
}
catch (NoSuchMethodException e) {
// Running in IKVM
getHostMethod = socket.getClass().getDeclaredMethod("getHost");
}
getHostMethod.setAccessible(true);
Object peerHost = getHostMethod.invoke(socket);
InetAddress iAddr = socket.getInetAddress();
int port = socket.getPort();
putMethod.invoke(cache, String.format("%s:%s", peerHost, port).toLowerCase(Locale.ROOT), session);
putMethod.invoke(cache, String.format("%s:%s", iAddr.getHostName(), port).toLowerCase(Locale.ROOT), session);
putMethod.invoke(cache, String.format("%s:%s", iAddr.getHostAddress(), port).toLowerCase(Locale.ROOT), session);
}
catch (Exception e) {
throw new IOException(e);
}
}
else {
throw new IOException("Invalid SSL Session");
}
}
}
}
套接字缓存的地址是使用getPeerHost
、getInetAddress().getHostName()
和getInetAddress().getHostAddress()
确定的。我尝试了几个组合,做或不做这三个,但我总是得到相同的结果。
编辑
下面是整个会话的服务器日志的屏幕截图:
发布于 2022-03-31 14:09:22
正如这篇StackOverflow文章中所述,可以告诉JVM只使用TLS1.2。
下面是对我有用的最初答案的链接:命令java只使用TLS1.2
您必须在JVM开始时添加一个命令行参数(本例中为:java -Djdk.tls.client.protocols=TLSv1.2 -jar ... <rest of command line here>
)。
这个简单的参数适用于我,现在我可以从运行FileZilla的FTP-服务器1.3.0中连接和传输数据
https://stackoverflow.com/questions/70903926
复制相似问题