2

I am trying to connect to my FTPS over explicit server to upload a file using the FTPSClient class in the Apache Commons Net library using Java. It is a Windows Filezilla Server and I can connect/upload/download to it normally with a username/password using a Filezilla or WinSCP client. I can connect to and log into the server, but when I try to upload a file with the FTPSClient.storeFile(file) method, it crashes. The error is below. So far I've tried mixing instantiating the FTPSClient in different ways, such as giving it an empty SSLContext to not have to deal with certificates, specifying the boolean isImplicit in the contructor for FTPSClient to be false, setting the buffer size of the FTPSClient to be twice as large as the size of the file I am uploading (usually not larger than 1MB), and sending some various FTP commands such as TYPE I or A. I can connect and log into the server, but it crashes as soon as I try to upload the file. My best guess is that I am not sending the data encrypted under SSL, or some certificate issue (but that would probably prevent me from connecting to the server in the first place?), but I don't know how to make sure I am uploading the file under SSL. Most examples I have seen, such as this one on Apache's website, don't seem to directly use any encryption.

This is my most recent attempt, with the empty SSLContext. I have commented out other instantiations I have tried that did not work.

JFileChooser chooser = new JFileChooser();
chooser.showOpenDialog(null);
File uploadingFile = chooser.getSelectedFile();
String hostname = JOptionPane.showInputDialog("Hostname?");
String port = JOptionPane.showInputDialog("Port?");
String username = JOptionPane.showInputDialog("Username?");
String uploadPath = "\\path\\to\\file";

try {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException{}
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException{}
        public X509Certificate[] getAcceptedIssuers(){return null;}
    };
    sslContext.init(null, new TrustManager[]{tm}, null);

    FTPSClient ftps = new FTPSClient(sslContext);
    //FTPSClient ftps = new FTPSClient("SSL", false);
    //FTPSClient ftps = new FTPSClient(false);
    //ftps.setAuthValue("SSL");
    ftps.addProtocolCommandListener(new PrintCommandListener(System.out));
    //ftps.setBufferSize((int)(uploadingFile.length()*2));
    ftps.connect(hostname, port);
    //ftps.execAUTH("TLS");
    if(!FTPReply.isPositiveCompletion(ftps.getReplyCode())) {
        ftps.disconnect();
        JOptionPane.showMessageDialog(null, "FTPS server refused connection.", "MyFTPSClient", JOptionPane.ERROR_MESSAGE);
    }
    String password = promptPassword();
    while(!ftps.login(username, password)) {
        JOptionPane.showMessageDialog(null, "Incorrect password.", "MyFTPSClient", JOptionPane.ERROR_MESSAGE);
        password = promptPassword();
    }
    ftps.execPBSZ(0);
    ftps.execPROT("P");
    //ftps.setFileType(FTP.ASCII_FILE_TYPE);
    ftps.enterLocalPassiveMode();
    ftps.changeWorkingDirectory(uploadPath);
    //ftps.setFileType(FTP.BINARY_FILE_TYPE);
    InputStream fileStream = new FileInputStream(uploadingFile);
    if(ftps.storeFile(uploadingFile.getName(), fileStream)){
        JOptionPane.showMessageDialog(null, "File successfully uploaded.", "MyFTPSClient", JOptionPane.INFORMATION_MESSAGE);
    }else{
        JOptionPane.showMessageDialog(null, "File not successfully uploaded.", "MyFTPSClient", JOptionPane.ERROR_MESSAGE);
    }
    fileStream.close();
    ftps.logout();
    ftps.disconnect();
} catch(Exception e) {
    System.out.println("Error caught by MyFTPSClient."); e.printStackTrace();
}

This is the console output, with sensitive information redacted with <>.

220-My FTPeS server.
220 <WELCOME MESSAGE>
AUTH TLS
234 Using authentication type TLS
USER <USERNAME>
331 Password required for <USERNAME>
PASS <PASSWORD>
230 Logged on
PBSZ 0
200 PBSZ=0
PROT P
200 Protection level set to P
CWD \path\to\file
250 CWD successful. "/path/to/file" is current directory.
PASV
227 Entering Passive Mode (<INTERNAL IP ADDRESS>,82,99)
[Replacing site local address <INTERNAL IP ADDRESS> with <EXTERNAL IP ADDRESS>]
STOR testImage.jpg
150 Opening data channel for file upload to server of "/path/to/file/testImage.jpg"
Error caught by MyFTPSClient.
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._openDataConnection_(FTPSClient.java:646)
    at org.apache.commons.net.ftp.FTPClient._storeFile(FTPClient.java:657)
    at org.apache.commons.net.ftp.FTPClient.__storeFile(FTPClient.java:643)
    at org.apache.commons.net.ftp.FTPClient.storeFile(FTPClient.java:2034)
    at main.MyFTPSClient.main(MyFTPSClient.java:98)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(Unknown Source)
    ... 9 more

This is the output of the Filezilla server.

(000006)5/13/2016 12:39:22 PM - (not logged in) (192.168.1.1)> Connected on port 21, sending welcome message...
(000006)5/13/2016 12:39:22 PM - (not logged in) (192.168.1.1)> 220-<WELCOME MESSAGE>
(000006)5/13/2016 12:39:22 PM - (not logged in) (192.168.1.1)> 220 <HOSTNAME>
(000006)5/13/2016 12:39:22 PM - (not logged in) (192.168.1.1)> AUTH TLS
(000006)5/13/2016 12:39:22 PM - (not logged in) (192.168.1.1)> 234 Using authentication type TLS
(000006)5/13/2016 12:39:22 PM - (not logged in) (192.168.1.1)> SSL connection established
(000006)5/13/2016 12:39:30 PM - (not logged in) (192.168.1.1)> USER <USERNAME>
(000006)5/13/2016 12:39:30 PM - (not logged in) (192.168.1.1)> 331 Password required for <USERNAME>
(000006)5/13/2016 12:39:30 PM - (not logged in) (192.168.1.1)> PASS ***************
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> 230 Logged on
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> PBSZ 0
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> 200 PBSZ=0
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> PROT P
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> 200 Protection level set to P
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> CWD \path\to\file
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> 250 CWD successful. "/path/to/file" is current directory.
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> PASV
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> 227 Entering Passive Mode (<INTERNAL IP ADDRESS>,82,57)
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> STOR testImage.jpg
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> 150 Opening data channel for file upload to server of "/path/to/file/testImage.jpg"
(000006)5/13/2016 12:39:30 PM - <USERNAME> (192.168.1.1)> 450 TLS session of data connection has not resumed or the session does not match the control connection
(000006)5/13/2016 12:39:31 PM - <USERNAME> (192.168.1.1)> disconnected.

I enabled debugging with -Djavax.net.debug=all as per recommendation. Most of it looks like blocks of successful encryption and decryption. The following is a block of the debug output that seems to show that the en/decryption didn't have a complete in/output.

main, WRITE: TLSv1.2 Handshake, length = 80
[Raw write]: length = 85
0000: 16 03 03 00 50 54 87 BA   78 CB D2 AF 8A B8 25 7A  ....PT..x.....%z
0010: A8 91 92 84 81 FE 77 89   51 D0 A3 A9 43 E8 8F 04  ......w.Q...C...
0020: 8C E9 1C 4D 37 83 4C 5A   47 EB 70 A5 A4 F2 FD 21  ...M7.LZG.p....!
0030: BF 46 68 76 DB 0C 9F FA   2C 47 9B 27 03 04 6C 52  .Fhv....,G.'..lR
0040: F9 65 5A 16 0B 11 EA AA   7C 4B 51 E6 D3 11 F1 1E  .eZ......KQ.....
0050: 31 04 C2 1D AB                                     1....
main, received EOFException: error
main, handling exception: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
%% Invalidated:  [Session-2, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256]
main, SEND TLSv1.2 ALERT:  fatal, description = handshake_failure

I don't know how it happens or what causes this. Maybe it has something to do with the bufferSize or the FileInputStream (probably not the FileInputStream, this looks like an encryption or certificate handshake issue). Whenever I connect to this server with a normal FTP client like FileZilla or WinSCP, it shows me the server certificate with details and asks me to trust it. This Java implementation has never asked me that and I don't know if the Apache Commons Net FTPSClient has anything that handles that.

It brings up the error at the same place in the encrypted information block every time, but the contents of the block are a little different each time.

f_puras
  • 2,521
  • 4
  • 33
  • 38

0 Answers0