When using Java Apache FTPClient for FTP TLS getti

2019-01-15 10:52发布

问题:

This question already has an answer here:

  • How to connect to FTPS server with data connection using same TLS session? 3 answers

I ran a Java (1.8) program on Windows 10 64x for FTP TLS (org.apache.commons.net.ftp):

FTPSClient ftpClient = new FTPSClient();

System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
// LISTENER
ftpClient.addProtocolCommandListener(
    new PrintCommandListener(new PrintWriter(System.out), true));

ftpClient.connect(server);
ftpClient.login(user, pass);

// Enter local passive mode
ftpClient.enterLocalPassiveMode();
// useEpsvWithIPv4
ftpClient.setUseEPSVwithIPv4(true);
// Set protection buffer size
ftpClient.execPBSZ(0);
// Set data channel protection to private
ftpClient.execPROT("P");

System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");

ftpClient.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());

ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

System.out.println("Remote system is " + 
ftpClient.getEnabledCipherSuites());

System.out.println("SSL: " + 
ftpClient.getEnableSessionCreation());
// PROTOCOLOS
String[] Protocols = ftpClient.getEnabledProtocols();
System.out.println("Protocols " + Protocols);
// AUTH
boolean Auth = ftpClient.getNeedClientAuth();
System.out.println("Auth: " + Auth);
ftpClient.getWantClientAuth();
ftpClient.getTrustManager();
ftpClient.feat();              

// APPROACH #1: using retrieveFile(String, OutputStream)
String remoteFile1 = "/readme.txt";
File downloadFile1 = new File("C:\\readme.txt");
OutputStream outputStream1 = new BufferedOutputStream(new 
FileOutputStream(downloadFile1));
ftpClient.retrieveFile(remoteFile1, outputStream1);
outputStream1.close();

For the first FTP server (Microsoft FTP Service) works just fine! debug:

run:
220 Microsoft FTP Service
AUTH TLS
234 AUTH command ok. Expecting TLS Negotiation.
USER *******
331 Password required for demo.
PASS *******
230 User logged in.
PBSZ 0
200 PBSZ command successful.
PROT P
200 PROT command successful.
TYPE I
200 Type set to I.
SSL: true
SYST
215 Windows_NT
Remote system is Windows_NT
Protocols [Ljava.lang.String;@3f2a3a5
Auth: false
FEAT
211-Extended features supported:
 LANG EN*
 UTF8
 AUTH TLS;TLS-C;SSL;TLS-P;
 PBSZ
 PROT C;P;
 CCC
 HOST
 SIZE
 MDTM
 REST STREAM
211 END
EPSV
229 Entering Extended Passive Mode (|||1025|)
RETR /readme.txt
125 Data connection already open; Transfer starting.
226 Transfer complete.
QUIT
221 Goodbye.
BUILD SUCCESSFUL (total time: 7 seconds)

For the second FTP server (FileZilla Server 0.9.59 beta) goes wrong, debug:

run:
220-FileZilla Server 0.9.59 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
USER *******
331 Password required for xxx
PASS *******
230 Logged on
PBSZ 0
200 PBSZ=0
PROT P
200 Protection level set to P
TYPE I
200 Type set to I
SSL: true
SYST
215 UNIX emulated by FileZilla
Remote system is UNIX emulated by FileZilla
Protocols [Ljava.lang.String;@246ae04d
Auth: false
FEAT
211-Features:
 MDTM
 REST STREAM
 SIZE
 MLST type*;size*;modify*;
 MLSD
 AUTH SSL
 AUTH TLS
 PROT
 PBSZ
 UTF8
 CLNT
 MFMT
 EPSV
 EPRT
211 End
EPSV
229 Entering Extended Passive Mode (|||14393|)
RETR /readme.txt
150 Opening data channel for file download from server of "/readme.txt"
Error: Remote host closed connection during handshake
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
QUIT
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1002)
450 TLS session of data connection has not resumed or the session does not match the control connection
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
    at org.apache.commons.net.ftp.FTPSClient._openDataConnection_(FTPSClient.java:646)
    at org.apache.commons.net.ftp.FTPClient._retrieveFile(FTPClient.java:1899)
    at org.apache.commons.net.ftp.FTPClient.retrieveFile(FTPClient.java:1885)
    at ftps.App_FTP.main(App_FTP.java:96)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(InputRecord.java:505)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:983)
    ... 7 more
BUILD SUCCESSFUL (total time: 5 seconds)

Using the FileZilla client works fine (download/upload files) but using the Java code I can't do nothing just connect and logon. Any suggestion? or any other solution for a automated FTP TLS?

My ftp Object

回答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.



回答2:

The important information is not the exception message itself, but this message in the log:

450 TLS session of data connection has not resumed or the session does not match the control connection

Some FTP servers do require that you reuse the TLS session for data connections.

This is not (yet) natively supported by Apache Commons Net library, but it's not difficult to implement it.

How to do that, is shown in my answer to:
How to connect to FTPS server with data connection using same TLS session?