[
https://issues.apache.org/jira/browse/NET-408?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18066390#comment-18066390
]
Tim Boeckstaens edited comment on NET-408 at 3/17/26 4:25 PM:
--------------------------------------------------------------
We ran into the same issue.
Because we use java 21 the reflection fix does not work.
However we found a different workaround to make the TLS session reuse work
without reflection on java internal SSL classes.
Our setup is:
java client -> HTTP proxy -> FTP server that requires TLS session reuse between
command and data connection
{code:java}
@Test
public void test() throws GeneralSecurityException, IOException {
SSLContext sslContext = //custom setup with private key and custom
truststore;
FTPSClient ftpClient = new PatchedFTPSClient(sslContext);
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("x.x.x.x",
8080));
ftpClient.setProxy(proxy);
// use java 1.7+ endpoint validation
ftpClient.setEndpointCheckingEnabled(true);
// debug logging
ftpClient.addProtocolCommandListener(new PrintCommandListener(new
PrintWriter(System.out), false));
ftpClient.connect("ftphost");
ftpClient.enterLocalPassiveMode();
// use enhanced passive mode
// this way server returns no IP and only PORT for data connection
// by using the patched PatchedFTPSClient this results in a data connection
to the host WITH TLS session reuse
ftpClient.setUseEPSVwithIPv4(true);
ftpClient.execPBSZ(0);
ftpClient.execPROT("P");
ftpClient.login("username", "");
// Verify the connection
if (ftpClient.isConnected()) {
System.out.println("Connected in Enhanced Passive Mode.");
}
FTPFile[] files = ftpClient.listFiles();
for (FTPFile file : files) {
System.out.printf("File name: %s\n", file.getName());
}
}
public class PatchedFTPSClient extends FTPSClient {
public PatchedFTPSClient(SSLContext sslContext) {
super(sslContext);
}
@Override
protected void _parseExtendedPassiveModeReply(String reply) throws
MalformedServerReplyException {
super._parseExtendedPassiveModeReply(reply);
try {
// FTPClient::_parseExtendedPassiveModeReply sets the passiveHost
to:
// passiveHost = getRemoteAddress().getHostAddress();
// for connections with a proxy this returns the proxy IP
// resulting in client connecting to the proxy on the returned data
connection port
// and results in FTPClient stuck on:
// EPSV
// 229 Entering Extended Passive Mode (|||40001|)
// LIST
// To fix this we override the passiveHost field with the peer host
of the sslsocket
// this is the correct host and results in TLS session reuse
Class<?> ftpClientClass =
getClass().getSuperclass().getSuperclass();
Field passiveHost = ftpClientClass.getDeclaredField("passiveHost");
passiveHost.setAccessible(true);
String peerHost = ((SSLSocket) _socket_).getSession().getPeerHost();
passiveHost.set(this, peerHost);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
{code}
What we think the library could do to fix this:
Replace `passiveHost = getRemoteAddress().getHostAddress();` with call to a
protected method.
The FTPClient can return the IP of the socket.
The FTPSClient can return the peerHost of the TLS session.
This will make EPSV data connection with TLS session reuse work out of the box.
was (Author: JIRAUSER311528):
We ran into the same issue.
Because we use java 21 the reflection fix does not work.
However we found a different workaround to make the TLS session reuse work
without reflection on java internal SSL classes.
Our setup is:
java client -> HTTP proxy -> FTP server that requires TLS session reuse between
command and data connection
{code:java}
@Test
public void test() throws GeneralSecurityException, IOException {
SSLContext sslContext = //custom setup with private key and custom
truststore;
FTPSClient ftpClient = new PatchedFTPSClient(sslContext);
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("x.x.x.x",
8080));
ftpClient.setProxy(proxy);
// use java 1.7+ endpoint validation
ftpClient.setEndpointCheckingEnabled(true);
// debug logging
ftpClient.addProtocolCommandListener(new PrintCommandListener(new
PrintWriter(System.out), false));
ftpClient.connect("ftphost");
ftpClient.enterLocalPassiveMode();
// use enhanced passive mode
// this way server returns no IP and only PORT for data connection
// by using the patched PatchedFTPSClient this results in a data connection
to the host WITH TLS session reuse
ftpClient.setUseEPSVwithIPv4(true);
ftpClient.execPBSZ(0);
ftpClient.execPROT("P");
ftpClient.login("username", "");
// Verify the connection
if (ftpClient.isConnected()) {
System.out.println("Connected in Enhanced Passive Mode.");
}
FTPFile[] files = ftpClient.listFiles();
for (FTPFile file : files) {
System.out.printf("File name: %s\n", file.getName());
}
}
public class PatchedFTPSClient extends FTPSClient {
public PatchedFTPSClient(SSLContext sslContext) {
super(sslContext);
}
@Override
protected void _parseExtendedPassiveModeReply(String reply) throws
MalformedServerReplyException {
super._parseExtendedPassiveModeReply(reply);
try {
// FTPClient::_parseExtendedPassiveModeReply sets the passiveHost
to:
// passiveHost = getRemoteAddress().getHostAddress();
// for connections with a proxy this returns the proxy IP
// resulting in client connecting to the proxy on the returned data
connection port
// and results in FTPClient stuck on:
// EPSV
// 229 Entering Extended Passive Mode (|||40001|)
// LIST
// To fix this we override the passiveHost field with the peer host
of the sslsocket
// this is the correct host and results in TLS session reuse
Class<?> ftpClientClass =
getClass().getSuperclass().getSuperclass();
Field passiveHost = ftpClientClass.getDeclaredField("passiveHost");
passiveHost.setAccessible(true);
String peerHost = ((SSLSocket) _socket_).getSession().getPeerHost();
passiveHost.set(this, peerHost);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
{code}
> problem connecting to ProFTPD with FTPES
> ----------------------------------------
>
> Key: NET-408
> URL: https://issues.apache.org/jira/browse/NET-408
> Project: Commons Net
> Issue Type: Bug
> Components: FTP
> Affects Versions: 2.2, 3.0
> Environment: ProFTPD 1.3.3d on SUSE Linux Enterprise Server 10.1
> 32bit, Kernel 2.6.16.46-0.12-default (config file attached)
> ProFTPD 1.3.3d on OpenSUSE 64bit Linux 2.6.34.8-0.2-desktop
> Java 1.5
> Reporter: Michael Voigt
> Priority: Major
> Attachments: BCFTPSClient.java, FTPSClientWithTLSResumption.zip,
> PTFTPSClient.java, ftpes.jpg, proftpd.conf
>
>
> I have a problem with the FTPClient connecting to a ProFTPD server.
> If the server uses the configuration option "TLSProtocol TLSv1", I
> cannot connect to it at all. I recieve the following error message:
> - javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection
> On the server side I see in the log:
> unable to accept TLS connection: protocol error:
> - (1) error:14094416:SSL routines:SSL3_READ_BYTES:sslv3 alert
> certificate unknown
> - TLS/TLS-C negotiation failed on control channel
> If the server uses the configuration option "TLSProtocol SSLv23", I
> can connect to it but I cant transfer any files. In the server log I
> see:
> - starting TLS negotiation on data connection
> - TLSv1/SSLv3 renegotiation accepted, using cipher RC4-MD5 (128 bits)
> - client did not reuse SSL session, rejecting data connection (see
> TLSOption NoSessionReuseRequired)
> - unable to open data connection: TLS negotiation failed
> If I add the NoSessionReuseRequired parameter to the ProFTPD config
> everything works fine.
> Here is my code:
> FTPClient ftpClient = new FTPClient();
> ftpClient = new FTPSClient("TLS");
> // this throws an exception with TLSProtocol TLSv1
> ftpClient.connect(host, port);
> int reply = ftpClient.getReplyCode();
> if (!FTPReply.isPositiveCompletion(reply)) {
> ftpClient.disconnect();
> log.error("The FTP Server did not return a positive
> completion reply!");
> throw new
> FtpTransferException(ECCUtils.ERROR_FTP_CONNECTION);
> }
> boolean loginSuccessful = ftpClient.login(userName, password);
> if (!loginSuccessful) {
> log.error("Login to the FTP Server failed! The
> credentials are not valid.");
> throw new
> FtpTransferException(ECCUtils.ERROR_FTP_LOGIN);
> }
> ftpClient.execPBSZ(0);
> ftpClient.execPROT("P");
> boolean success = ftpClient.storeFile(fileName, fis);
> if (!success) {
> // this is false if "NoSessionReuseRequired" is not set
> }
> Now my question is if it is generally possible to connect to a server
> with "TLSProtocol TLSv1" or "TLSProtocol SSLv23" without the
> "NoSessionReuseRequired" parameter? Could someone provide a piece of
> example code for this?
--
This message was sent by Atlassian Jira
(v8.20.10#820010)