0

I am trying to connect to FTP that requires 'Explicit FTP over TLS'. I am trying to do it from my local machine.

Below is the code which I use

FTPSClient ftpClient = new FTPSClient(false);
      ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
      ftpClient.setAuthValue("TLS");
      ftpClient.connect(host, port);
      int reply = ftpClient.getReplyCode();
      if (FTPReply.isPositiveCompletion(reply)) {

        if (ftpClient.login(username, password)) {

          ftpClient.execPBSZ(0);
          ftpClient.execPROT("P");
          ftpClient.enterLocalPassiveMode();

      InputStream is = new FileInputStream(localFilename);
      if (ftpClient.storeFile(remoteFilename, is)) {
        is.close();
      } else {
        System.out.println("Could not store file");
      }
      ftpClient.logout();

        } else {
          System.out.println("FTP login failed");
        }

        // Disconnect
        ftpClient.disconnect();

      } else {
        System.out.println("FTP connect to host failed");
      }

When I run, I get below exception

    Could not connect to server.
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:240)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:171)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:178)
    at TestFTP.main(TestFTP.java:64)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(Unknown Source)
    ... 8 more

Below is the log I get from Java CommandListener

220-FileZilla Server 0.9.60 beta
220-written by Tim Kosse (tim.kosse@filezilla-project.org)
220 Please visit https://filezilla-project.org/
AUTH TLS
234 Using authentication type TLS
FTP client received network error javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake

Below is the log when I try to connect using FileZilla. It is connecting fine and I can transfer the files. But when I try using Java, it isn't connecting

Response:   220-FileZilla Server 0.9.60 beta
Response:   220-written by Tim Kosse (tim.kosse@filezilla-project.org)
Response:   220 Please visit https://filezilla-project.org/
Command:    AUTH TLS
Response:   234 Using authentication type TLS
Status: Initializing TLS...
Status: Verifying certificate...
Command:    USER 
Status: TLS/SSL connection established.
Response:   331 Password required
Command:    PASS xxxx
Response:   230 Logged on
Command:    PBSZ 0
Response:   200 PBSZ=0
Command:    PROT P
Response:   200 Protection level set to P
Status: Connected
Status: Retrieving directory listing...
Command:    PWD
Response:   257 "/" is current directory.
Command:    TYPE I
Response:   200 Type set to I
Command:    PASV
Response:   227 Entering Passive Mode ()
Command:    MLSD
Response:   150 Opening data channel for directory listing of "/"
Response:   226 Successfully transferred "/"
Status: Directory listing successful
Status: Retrieving directory listing...
Command:    PASV
Response:   227 Entering Passive Mode ()
Command:    MLSD
Response:   150 Opening data channel for directory listing of "/"
Response:   226 Successfully transferred "/"
Status: Directory listing successful

Could you all please help?

Kind regards Jon

Louie
  • 1
  • 1
  • 8
  • What's the complete stacktrace and where is it failing? When trying to connect or when trying to do the file-transfer? What FTPS-client are you using? – Lothar Feb 19 '18 at 17:02
  • Hi, it is failing when I try to connect. FTPS-client? sorry I don't get you? – Louie Feb 19 '18 at 17:09
  • I have added complete stacktrace – Louie Feb 19 '18 at 17:15
  • Show us a verbose log file of any standalone FTP client that can successfully connect to the same server from the same machine that runs your Java code. – Martin Prikryl Feb 19 '18 at 17:46
  • What version of Java are you using? – Lothar Feb 19 '18 at 20:10
  • @MartinPrikryl I have updated with FileZilla log file – Louie Feb 20 '18 at 12:06
  • @Lothar I am using Java 7 – Louie Feb 20 '18 at 12:06
  • The log does not show any file upload + You didn't confirm if you are running FileZilla on the *same machine* as you run your Java code + What is a value of `port` in your code? + You do not seem to be using the latest version of Apache Commons Net. What version are you using? Can you try the latest version? + Can you include a log file from the FTP server? – Martin Prikryl Feb 20 '18 at 12:14
  • @MartinPrikryl I just updated the connection log, since that is my primary problem. Would you like to see the log of file transfer as well? Also, yes, I am running FileZilla on the same machine I run my Java code. I use port 21 in my code. I use Apache Commons Net FTP version 2.0. Unfortunately, I am trying to connect to our supplier's FTP, I am not sure whether they will give me log file from FTP server. – Louie Feb 20 '18 at 12:21
  • True, no need for the transfer log. + [**Apache Commons Net 2.0 is 10 years old**](http://commons.apache.org/proper/commons-net/changes-report.html#a2.0). – Martin Prikryl Feb 20 '18 at 12:24
  • @MartinPrikryl, just tried commons-net-3.6, still no luck :( – Louie Feb 20 '18 at 12:30
  • @MartinPrikryl, I have also updated the log I get from Java – Louie Feb 20 '18 at 12:32
  • So can you also update exception callstack for 3.6? + Also @Lothar has some good points in his answer. – Martin Prikryl Feb 20 '18 at 14:11
  • @MartinPrikryl I am now able to connect after upgrading to Java 8. But I am getting handshake exception while uploading the file – Louie Feb 27 '18 at 14:05

3 Answers3

1

First of all, make sure you connect to the plain port of the FTP-server because you're instantiating the FTPSClient in FTPS-explict-mode.

You use Java 7 that until update 75 still uses SSLv2Hello for initiating an SSL session. After Heartbleed, BEAST and all the other vulnerabilities found in SSL in the last couple of years most SSL-based servers are nowadays denying handshake attempts with TLS-packets with version < TLS 1.0. I suppose that this is happening here as well.

The easiest way to find out if I'm right is trying to start the application using Java 8 (that switched to TLS 1.2). If that works you need to make sure to deactivate SSLv2Hello. This can be done globally by calling the JVM with the following system properties:

java -Dhttps.protocols="TLSv1,TLSv1.1,TLSv1.2" -Djdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2" <MyApp>

or set the allowed protocols on the Socket before doing the handshake like this:

sslSocket.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});

or set a corresponding SSLSocketFactory to the FTPClient to set the protocols as shown above as soon as the FTPClient needs an SSLSocket. I don't know Apache's FTPClient so I can't tell you how to do this, but the documentation or a Google-search should help you on this.

But deactivating SSLv2Hello is a Good Thing [TM] anyway, so updating your JVM to the most current version (be it Java 7 Update 80 or Java 8 Update 161) is never a bad idea, also to get all the other fixes that are not SSL-related.

If my theory about SSLv2Hello isn't correct the only next step that will help you go down on this issue is using a TCP logger like Wireshark that can show you the actual SSL handshake and at what point the peer server is closing the connection. Often enough you can see an SSL Alert in that trace explaining the actual problem that is not shown in the exception you receive within Java.

Lothar
  • 5,323
  • 1
  • 11
  • 27
  • Hi, sorry for delay in getting back. Changing to Java 8 done the trick. Now I am able to connect to FTP, but I am getting handshake while uploading the file. – Louie Feb 27 '18 at 14:04
  • @Louie Without error message it's only guessing but most likely the FTPS-Client you're using doesn't support SSL Session Reuse which is nowerdays checked by most FTPS servers out there. I'm not familiar with the Apache FTPS client to say since when (if at all) this is supported and if they've solved it JVM-version independent (there is a slight difference between the ways you need to do it in Java 7 and 8). – Lothar Feb 27 '18 at 16:31
  • @Louie A short Google search came up with http://eng.wealthfront.com/2016/06/10/connecting-to-an-ftps-server-with-ssl-session-reuse-in-java-7-and-8/ so at least since June last year FTPSClient didn't support SSL Session Reuse, but that page shows a workaround that works for Java 7 and Java 8. – Lothar Feb 27 '18 at 16:33
  • thank you for the suggestion.I will have a read. In the meantime, I have opened up another thread with the recent exception I am getting https://stackoverflow.com/questions/49012248/ftp-client-received-network-error-javax-net-ssl-sslhandshakeexception-remote-ho – Louie Feb 27 '18 at 16:50
  • @Louie OK, you might accept this answer then, since it seems to have helped you with this issue. – Lothar Feb 27 '18 at 16:53
  • But I think the error I get is different to error in the link you provided. https://stackoverflow.com/questions/49012248/ftp-client-received-network-error-javax-net-ssl-sslhandshakeexception-remote-ho is the ticket I created with new error I get in this case – Louie Feb 27 '18 at 16:54
  • @Louie I'll have a look and will answer there if I know something – Lothar Feb 27 '18 at 16:55
0

The solution is simple and a bit of technically tricky, as per assumption Session of SSL which the connection has been opened in should also be used for the data, but in case of FTPS the library tries out of session attempt for data and the method handling this operation isn’t defined in the FTPS class. Hence the solution is to simply extend the FTPS class and provide implementation for the method.

Here is an example

public class ModifiedFTPSClient extends FTPSClient {

public ModifiedFTPSClient() {
    super("TLS", false);
}

public ModifiedFTPSClient(boolean isImplicit) {
    super("TLS", isImplicit);
}

@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
    if (socket instanceof SSLSocket) {
        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 method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
                method.setAccessible(true);
                method.invoke(cache, String
                        .format("%s:%s", socket.getInetAddress().getHostName(), String.valueOf(socket.getPort()))
                        .toLowerCase(Locale.ROOT), session);
                method.invoke(cache, String
                        .format("%s:%s", socket.getInetAddress().getHostAddress(), String.valueOf(socket.getPort()))
                        .toLowerCase(Locale.ROOT), session);
            } catch (NoSuchFieldException e) {
                throw new IOException(e);
            } catch (Exception e) {
                throw new IOException(e);
            }
        } else {
            throw new IOException("Invalid SSL Session");
        }
    }
}}

Now If I try to use the extended class This will operate in the same session. i.e

 ModifiedFTPSClient ftpClient = new ModifiedFTPSClient(true);
    ftpClient.addProtocolCommandListener(new PrintCommandListener(System.out));
    ftpClient.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());... and so on.
  • This is not an answer to the OP's problem. OP has a problem with a control connection. Your answer is actually the same as the one by @DhirajPandit (and equally irrelevant). I understand that you may get the same *error message* as OP, but that does not mean that you have the same problem. You should explain that in your answer. And a credit for the code that you have not written yourself would be nice too. – Martin Prikryl Oct 30 '18 at 08:10
  • ModifiedFTPSClient ftpClient = new ModifiedFTPSClient(false); work for me. thanks – Didier Jun 02 '20 at 13:14
-1

I have Solved this problem using Cyberduck library. https://github.com/iterate-ch/cyberduck
first why this Handshake problem arises, because some advances FTP servers allow only one session for connection and data transmission.

1)Control Session - > for connection
2)Data Session - > data storage/download/etc

So how to solve this.

Step 1 -:

First You need to checkout cyberduck repo from GitHub. from here - : https://github.com/iterate-ch/cyberduck

Step 2 -: You will see FTP and core module in checkout repo ftp and core modules

Step 3 -: Convert this module into maven and create jar for the same.

Step 4 -: add this jar into you project, but this jar also requires other dependencies so add those accordingly into project build path as well.

Step 5 -: Code snippet.

public class TestFTPS {
    public static void main(String[] args) throws SocketException, IOException {
        TrustManager[] trustManagers = new TrustManager[] { new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                // TODO Auto-generated method stub
                return null;
            }
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // TODO Auto-generated method stub
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // TODO Auto-generated method stub
            }
        } };
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustManagers, new SecureRandom());
        } catch (Exception e) {
            e.printStackTrace();
        }
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        Protocol protocol = new FTPTLSProtocol();
        FTPClient client = new FTPClient(protocol, sslSocketFactory, sslContext);
        client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
        client.connect(ftphostname, port);
        client.execAUTH("TLS"); //SSL
        System.out.println(client.getReplyCode());
        if (client.login(username, password)) {
            client.execPBSZ(0);
            client.execPROT("P");
            String date = "Testing Data Send to provide FTP location";
            client.setFileType(FTP.BINARY_FILE_TYPE);
            client.enterLocalPassiveMode();
            InputStream is = new ByteArrayInputStream(date.getBytes());
            client.storeFile("test.txt", is);
            System.out.print(client.getReplyCode());
        }
    }
}

Step 6 -: After running this code, your file will be transferred successfully to ftp location.

Note - :Please add required jar in your project

Hope This will help you.

  • While your explanation is not much clear, you are probably referring to TLS/SSL session reuse as covered in [How to connect to FTPS server with data connection using same TLS session?](https://stackoverflow.com/q/32398754/850848) - Though, the OP has a problem opening even the control connection, so **it is a different problem**. – Martin Prikryl Feb 20 '18 at 07:50
  • Thanks @MartinPrikry cyberduck library is your suggestions, l had also face same issue, while uploading file on ftp location (FileZilla 0.9.60), i have solved this cyberduck library – Dhiraj Pandit Feb 20 '18 at 08:44
  • Yes, Cyberduck code was my suggestion. So I know that **this problem is different**. One can get the problem with session ID reuse only when opening a data connection (for file transfer). And OP never gets to even trying to transfer a file. If you check the callstack, you will see that the exception is raised already by `ftpClient.connect`, i.e. **well before** any attempt to transfer a file. – Martin Prikryl Feb 20 '18 at 08:51
  • @DhirajPandit thank you for your comments, but as Martin said, I am not able to connect at all, only If I solve that I can follow your steps to transfer the files. – Louie Feb 20 '18 at 12:08