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 NetFTPSClient
), 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 NetFTPSClient
specifically to allow the TLS/SSL session reuse implementation:
https://issues.apache.org/jira/browse/NET-426A native support for the reuse is still pending:
https://issues.apache.org/jira/browse/NET-408You will obviously need to override the Spring Integration
DefaultFtpsSessionFactory.createClientInstance()
to return your customFTPSClient
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 Propertyjdk.tls.useExtendedMasterSecret
tofalse
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
, whenwhere & SSL_ST_CONNECT
. - But for TLS 1.3 (
SSL_version >= TLS1_3_VERSION
), I had to useSSL_CTX_set_session_cache_mode
withSSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE | SSL_SESS_CACHE_NO_AUTO_CLEAR
, and use a callback set bySSL_CTX_sess_set_new_cb
.
- I specifically do it from a callback set by the
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
How to Write Ndef Records to Nfc Tag
Firebaseapp with Name [Default] Doesn't Exist
How to Set Timeout in Retrofit Library
How to Create the Directory Error
Generate All Combinations from Multiple Lists
What Is the Main Difference Between Inheritance and Polymorphism
How to Change JPAnel Inside a Jframe on the Fly
Why Can't We Have Static Method in a (Non-Static) Inner Class (Pre-Java 16)
Pkix Path Building Failed: Unable to Find Valid Certification Path to Requested Target
Android Changing Floating Action Button Color
How to Get Specific Pushedid in Firebase
How to Use Vectordrawable in Buttons and Textviews Using Android:Drawableright
How to View/Change Socket Connection Timeout on Linux
Why Call Super() in a Constructor
How to Decompile a Whole Jar File