How to Connect to Ftps Server with Data Connection Using Same Tls Session

How to connect to FTPS server with data connection using same TLS session?

Indeed some FTP(S) servers do require that the TLS/SSL session is reused for the data connection. This is a security measure by which the server can verify that the data connection is used by the same client as the control connection.

Some references for common FTP servers:

  • vsftpd: https://scarybeastsecurity.blogspot.com/2009/02/vsftpd-210-released.html
  • FileZilla server: https://svn.filezilla-project.org/filezilla?view=revision&revision=6661
  • ProFTPD: http://www.proftpd.org/docs/contrib/mod_tls.html#TLSOptions (NoSessionReuseRequired directive)

What may help you with the implementation is that Cyberduck FTP(S) client does support TLS/SSL session reuse and it uses Apache Commons Net library:

  • https://github.com/iterate-ch/cyberduck/issues/5087 – Reuse Session key on data connection

  • See its FTPClient.java code (extends Commons Net FTPSClient), particularly its override of _prepareDataSocket_ method:

     @Override
    protected void _prepareDataSocket_(final Socket socket) {
    if(preferences.getBoolean("ftp.tls.session.requirereuse")) {
    if(socket instanceof SSLSocket) {
    // Control socket is SSL
    final SSLSession session = ((SSLSocket) _socket_).getSession();
    if(session.isValid()) {
    final SSLSessionContext context = session.getSessionContext();
    context.setSessionCacheSize(preferences.getInteger("ftp.ssl.session.cache.size"));
    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);
    putMethod.invoke(cache, String.format("%s:%s", peerHost, socket.getPort()).toLowerCase(Locale.ROOT), session);
    }
    catch(NoSuchFieldException e) {
    // Not running in expected JRE
    log.warn("No field sessionHostPortCache in SSLSessionContext", e);
    }
    catch(Exception e) {
    // Not running in expected JRE
    log.warn(e.getMessage());
    }
    }
    else {
    log.warn(String.format("SSL session %s for socket %s is not rejoinable", session, socket));
    }
    }
    }
    }
  • It seems that the _prepareDataSocket_ method was added to Commons Net FTPSClient specifically to allow the TLS/SSL session reuse implementation:

    https://issues.apache.org/jira/browse/NET-426

    A native support for the reuse is still pending:

    https://issues.apache.org/jira/browse/NET-408

  • You will obviously need to override the Spring Integration DefaultFtpsSessionFactory.createClientInstance() to return your custom FTPSClient implementation with the session reuse support.


The above solution does not work on its own anymore since JDK 8u161.

According to JDK 8u161 Update Release Notes (and the answer by @Laurent):

Added TLS session hash and extended master secret extension support

...

In case of compatibility issues, an application may disable negotiation of this extension by setting the System Property jdk.tls.useExtendedMasterSecret to false in the JDK

I.e., you can call this to fix the problem (you still need to override the _prepareDataSocket_):

System.setProperty("jdk.tls.useExtendedMasterSecret", "false");

Though this should be a considered a workaround only. I do not know a proper solution.


An alternative implementation is here:

https://issues.apache.org/jira/browse/NET-408


There's a separate question about problems in 1.8.0_161:

SSL Session reuse in Apache FTPS client in JDK 8u161


I actually had the same problem in the past (just in C++/OpenSSL, I do not do Java), so I knew what to google for.

SSL Session reuse in Apache FTPS client in JDK 8u161

A possible solution is described here.

Essentially, it is reverting changed behavior from JDK8u161 to the way this worked before. You need to set the system property

jdk.tls.useExtendedMasterSecret

to false to do that.

There are two ways:

  • call System.setProperty("jdk.tls.useExtendedMasterSecret", "false"); before initiating the FTP/S connection
  • Pass the property to your java process with java -Djdk.tls.useExtendedMasterSecret=false [...]

Keep in mind that this solution disables a security enhancement JVM-wide - proceed with caution.

Upload file to implicit FTPS server in C# with TLS session reuse

You can use WinSCP .NET assembly.

It supports implicit TLS (port 990). And uses OpenSSL TLS implementation (not .NET Framework), so it should not have the problem that FluentFTP has. It definitely works for me against FileZilla FTP server, even with session resumption requirement turned on.

SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "username",
Password = "password",
FtpSecure = FtpSecure.Implicit,
TlsHostCertificateFingerprint = "xx:xx:xx:...",
};

using (Session session = new Session())
{
session.Open(sessionOptions);

session.PutFiles(localPath, remotePath).Check();
}

(I'm the author of WinSCP)

For more references about the problem, see also Can connect to FTP using FileZilla or WinSCP, but not with FtpWebRequest or FluentFTP.

TLS/SSL session resume on FTP transfer connection with OpenSSL

Using the SSL_get1_session and the SSL_set_session worked in the end. I must have used them incorrectly when trying the first time.

  • Once the TLS/SSL session on the control connection is established, use SSL_get1_session to retrieve the session.

    • I specifically do it from a callback set by the SSL_set_info_callback, when where & SSL_ST_CONNECT.
    • But for TLS 1.3 (SSL_version >= TLS1_3_VERSION), I had to use SSL_CTX_set_session_cache_mode with SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE | SSL_SESS_CACHE_NO_AUTO_CLEAR, and use a callback set by SSL_CTX_sess_set_new_cb.
  • Call the SSL_set_session with the reference to the control connection session, when setting up TLS/SSL session for the data connection.



Related Topics



Leave a reply



Submit