简体   繁体   English

如何建立到 FileZilla Server 1.2.0 的 FTPS 数据连接

[英]How to establish a FTPS data connection to a FileZilla Server 1.2.0

It is a known problem to use the Java FTPSClient of Apache commons.net with session resumption.使用 Apache commons.net 的 Java FTPSClient 和 session 恢复是一个已知问题。 Session resumption is a security feature which a FTPS server can require for data connections. Session 恢复是 FTPS 服务器可能需要的数据连接安全功能。 The Apache FTPSClient does not support session resumption, and the JDK APIs make it hard to build a custom implementation. Apache FTPSClient 不支持 session 恢复,并且 JDK API 很难构建自定义实现。 There are a couple of workarounds using reflection, see eg this answer and this commons.net bug entry .有几个使用反射的变通方法,例如参见这个答案和这个commons.net 错误条目

I use such a workaround (see snipped below) in JDK 11 and tested it against a local FileZilla Server.我在 JDK 11 中使用了这样的解决方法(请参阅下面的片段)并针对本地 FileZilla Server 对其进行了测试。 It works with FileZilla Server 0.9.6, but it doesn't with FileZilla Server 1.2.0, which is the latest version at the time of writing.它适用于 FileZilla Server 0.9.6,但不适用于 FileZilla Server 1.2.0,后者是撰写本文时的最新版本。 With that version, when trying to establish a data connection, the server responds with:使用该版本,当尝试建立数据连接时,服务器会响应:

425 Unable to build data connection: TLS session of data connection not resumed.

As I said, FileZilla Server 0.9.6 is fine with how I do session resumption, and I made sure that the setting for requiring session resumption is activated.正如我所说,FileZilla Server 0.9.6 与我执行 session 恢复的方式没问题,并且我确保激活了要求 session 恢复的设置。

In FileZilla Server 1.2.0, such settings are now set implicitly and cannot be changed via the GUI, maybe not at all.在 FileZilla Server 1.2.0 中,此类设置现在是隐式设置的,无法通过 GUI 更改,也许根本无法更改。 Are there some server settings that I can tweak for this to work?我可以调整一些服务器设置以使其工作吗? Or is it an issue with how I implemented the workaround?还是我如何实施解决方法的问题? Does anyone experience similar issues?有没有人遇到类似的问题?

This is the workaround I am using:这是我正在使用的解决方法:

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

The address under which the socket is cached is determined using getPeerHost , get.netAddress().getHostName() , and get.netAddress().getHostAddress() .缓存套接字的地址是使用getPeerHostget.netAddress().getHostName()get.netAddress().getHostAddress()确定的。 I tried several combinations of doing or not doing these three, but I always get the same result.我尝试了做或不做这三个的几种组合,但我总是得到相同的结果。

Edit :编辑

Here is a screenshot of the server logs of the full session:这是完整 session 的服务器日志的屏幕截图:

FileZilla Server 1.2.0 日志截图

As stated in this StackOverflow post it is possible to tell the JVM that only TLS 1.2 should be used.正如这篇 StackOverflow 帖子中所述,可以告诉 JVM 只应使用 TLS 1.2。
Here is the link to the original answer which worked for me: command for java to use TLS1.2 only这是对我有用的原始答案的链接:命令 java 仅使用 TLS1.2

You have to add a command line parameter at the start of the JVM in this case this is: java -Djdk.tls.client.protocols=TLSv1.2 -jar... <rest of command line here>在这种情况下,您必须在 JVM 的开头添加一个命令行参数: java -Djdk.tls.client.protocols=TLSv1.2 -jar... <rest of command line here>

This simple parameter worked for me, now I can connect and transfer data from a FTP-Server wich runs FileZilla FTP-Server 1.3.0这个简单的参数对我有用,现在我可以从运行 FileZilla FTP-Server 1.3.0 的 FTP-Server 连接和传输数据

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM