FTPS Upload works with cURL command but not with Apache Commons FTPClient

256 views Asked by At

The following command works to upload a file to our FTPS service provider:

curl --ftp-ssl -T test.txt ftp://username:[email protected]/public/test.txt

I would like to achieve the same upload with Java using Apache Commons FTPSClient. I created this small sample program

package com.sample.simulator;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPSClient;

import java.io.*;

public class Application  {

    public static void main(String[] args) throws IOException {
        String server = "ftp.hidrive.ionos.com";
        String user = "username";
        String pass = "password";

        FTPSClient ftpClient = new FTPSClient(true);

        ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));

        try {
            ftpClient.connect(server);

            var success = ftpClient.login(user, pass);
            if(success) {
                System.out.println("Login successful");
            } else {
                System.out.println("Login failed");
            }

            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

            File firstLocalFile = new File("C:/Temp/test.txt");
            InputStream inputStream = new FileInputStream(firstLocalFile);

            String firstRemoteFile = "test.txt";

            ftpClient.changeWorkingDirectory("public");
            var currentWorkingDir = ftpClient.printWorkingDirectory();
            System.out.println("Current working directory " + currentWorkingDir);

            System.out.println("Start uploading first file");

            boolean done = ftpClient.storeFile(firstRemoteFile, inputStream);

            inputStream.close();
            if (done) {
                System.out.println("The first file is uploaded successfully.");
            } else {
                var reply = ftpClient.getReplyString();
                System.out.println("Upload failed " + reply );
            }

            inputStream.close();
        } catch (IOException ex) {
            System.out.println("Error: " + ex.getMessage());
            ex.printStackTrace();
        } finally {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.disconnect();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

but it always throws the following exception:

java.net.SocketException: Connection reset by peer: socket write error
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
    at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
    at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
    at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
    at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
    at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
    at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
    at com.sample.simulator.Application.main(Application.java:21)

Setting the isImplicit - Flag in the Constructor of FTPSClient to false goes further but fails at the upload stage (because probably a different port would be used for data connection which is not setup at our provider).

220 Another visitor. Stay a while...
AUTH TLS
234 AUTH SSL OK.
USER username
331 FTP login okay, send password.
PASS password
230 User logged in, proceed.
Login successful
TYPE I
200 Using BINARY mode to transfer data.
CWD public
250 Directory changed to /public
PWD
257 "/public" is current directory.
Current working directory /public
Start uploading first file
PORT 192,168,0,4,243,243
550 Permission denied.
Upload failed 550 Permission denied.

QUIT
221 Goodbye.

Update: When activating the flag -Djavax.net.debug=ssl i get the following output

C:\Users\xxx\.jdks\corretto-11.0.18\bin\java.exe -Djavax.net.debug=ssl "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.3\lib\idea_rt.jar=59373:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.3\bin" -Dfile.encoding=UTF-8 @C:\Users\xxx\AppData\Local\Temp\idea_arg_file1613146868 com.sample.simulator.Application -Djavax.net.debug=ssl
javax.net.ssl|DEBUG|01|main|2023-06-01 06:37:30.918 CEST|SSLCipher.java:464|jdk.tls.keyLimits:  entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
javax.net.ssl|WARNING|01|main|2023-06-01 06:37:31.262 CEST|SSLSocketImpl.java:1654|handling exception (
"throwable" : {
  java.net.SocketException: Connection reset by peer: socket write error
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
    at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
    at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
    at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
    at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
    at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
    at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
    at com.sample.simulator.Application.main(Application.java:21)}

)
javax.net.ssl|ERROR|01|main|2023-06-01 06:37:31.263 CEST|TransportContext.java:345|Fatal (UNEXPECTED_MESSAGE): Connection reset by peer: socket write error (
"throwable" : {
  java.net.SocketException: Connection reset by peer: socket write error
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
    at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
    at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
    at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
    at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
    at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
    at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
    at com.sample.simulator.Application.main(Application.java:21)}

)
javax.net.ssl|WARNING|01|main|2023-06-01 06:37:31.264 CEST|TransportContext.java:392|Fatal: failed to send fatal alert UNEXPECTED_MESSAGE (
"throwable" : {
  java.net.SocketException: Connection reset by peer: socket write error
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:81)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:389)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:296)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:291)
    at java.base/sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1682)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:466)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
    at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
    at com.sample.simulator.Application.main(Application.java:21)}

)
javax.net.ssl|DEBUG|01|main|2023-06-01 06:37:31.264 CEST|SSLSocketImpl.java:1737|close the underlying socket
javax.net.ssl|DEBUG|01|main|2023-06-01 06:37:31.264 CEST|SSLSocketImpl.java:1756|close the SSL connection (initiative)
java.net.SocketException: Connection reset by peer: socket write error
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/sun.security.ssl.SSLSocketOutputRecord.flush(SSLSocketOutputRecord.java:271)
    at java.base/sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:89)
    at java.base/sun.security.ssl.ClientHello$ClientHelloKickstartProducer.produce(ClientHello.java:647)
    at java.base/sun.security.ssl.SSLHandshake.kickstart(SSLHandshake.java:525)
    at java.base/sun.security.ssl.ClientHandshakeContext.kickstart(ClientHandshakeContext.java:112)
    at java.base/sun.security.ssl.TransportContext.kickstart(TransportContext.java:233)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:449)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:427)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:283)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:219)
    at org.apache.commons.net.SocketClient._connect(SocketClient.java:254)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:212)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:316)
    at com.sample.simulator.Application.main(Application.java:21)
Error: Connection reset by peer: socket write error

Process finished with exit code 0

Update 2: This is the verbose log output of the above curl command:

curl -v --ftp-ssl -T test.txt ftp://username:[email protected]/public/testx2.txt

Warning: --ssl is an insecure option, consider --ssl-reqd instead
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 85.214.3.104:21...
* Connected to ftp.hidrive.ionos.com (85.214.3.104) port 21 (#0)
< 220 Another visitor. Stay a while...
> AUTH SSL
< 234 AUTH SSL OK.
* schannel: disabled automatic use of client certificate
> USER username
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
< 331 FTP login okay, send password.
> PASS password
< 230 User logged in, proceed.
> PBSZ 0
< 200 PBSZ=0
> PROT P
< 200 Switched to protected data transfer mode.
> PWD
< 257 "/" is current directory.
* Entry path is '/'
> CWD public
* ftp_perform ends with SECONDARY: 0
< 250 Directory changed to /public
> EPSV
* Connect data stream passively
< 500 Syntax error, command unrecognized.
* Failed EPSV attempt. Disabling EPSV
> PASV
< 227 Entering Passive Mode (85,214,3,104,230,253).
* Skip 85.214.3.104 for data connection, re-use ftp.hidrive.ionos.com instead
* Connecting to 85.214.3.104 (85.214.3.104) port 59133
*   Trying 85.214.3.104:59133...
* Connected to ftp.hidrive.ionos.com (85.214.3.104) port 21 (#0)
> TYPE I
< 200 Using BINARY mode to transfer data.
> STOR testx2.txt
< 150 Opening BINARY mode SSL data connection.
} [8 bytes data]
* We are completely uploaded and fine
* Remembering we are in dir "public/"
* schannel: shutting down SSL/TLS connection with ftp.hidrive.ionos.com port 21
< 226 Transfer complete. Closing data connection.
100     8    0     0  100     8      0     10 --:--:-- --:--:-- --:--:--    10
* Connection #0 to host ftp.hidrive.ionos.com left intact

Update 3:

When i use passive mode (new FTPSClient(false)) and entering localPassiveMode connect and login works but i get a permission error when uploading the file. How can this be when i am using the same credentials and the same folder as with cURL?

220 Another visitor. Stay a while...
AUTH TLS
234 AUTH SSL OK.
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.190 CEST|SSLCipher.java:464|jdk.tls.keyLimits:  entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.674 CEST|SSLCipher.java:1866|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.678 CEST|SSLCipher.java:2020|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.729 CEST|SSLCipher.java:1866|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:27.731 CEST|SSLCipher.java:2020|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
USER username
331 FTP login okay, send password.
PASS password
230 User logged in, proceed.
Login successful
TYPE I
200 Using BINARY mode to transfer data.
CWD public
250 Directory changed to /public
PWD
257 "/public" is current directory.
Current working directory /public
Start uploading first file
PASV
550 Permission denied.
Upload failed 550 Permission denied.

javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:28.006 CEST|SSLSocketImpl.java:578|duplex close of SSLSocket
javax.net.ssl|DEBUG|01|main|2023-06-01 12:54:28.008 CEST|SSLSocketImpl.java:1756|close the SSL connection (passive)

Is there any option i am missing which would correctly upload the file as the cURL command does?

0

There are 0 answers