zoukankan      html  css  js  c++  java
  • FTPClient TLS 与 FTP 进行数据传输异常:Remote host closed connection during handshake

    环境:java JDK 1.8、org.apache.commons-net-3.6.jar、端口已放开

    FTPClient ftpClient = new FTPClient(protocol, false);
    ftpClient.setRemoteVerificationEnable(false);
    ftpClient.setControlKeepAliveTimeout(300);
    ftpClient.setDataTimeout(300);
    InputStream fin = null;
    try {
        ftpClient.connect(host, port);
        int reply = ftpClient.getReplyCode();
        if (ftpClient.isPositiveComletion(reply)) {
            if (ftpClient.login(username, password)) {
                ftpClient.feat();
                ftpClient.execPBSZ(0);
                ftpClient.execPROT("p");
                ftpClient.setControlEncoding("UTF-8");
                ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
                ftpClient.enterLocalPassiveMode();
                try {
                    fin = new FileInoutStream(new File("C:\doc_home\test1.txt"));
                } catch (FileNotFoundException e) {
                    System.out.println("---file not found");
                }
                String remoteFile = "test1.txt";
                ftpClient.mlsd();
                if (ftpClient.storeFile(remoteFile, fin)) {
                    fin.close();
                } else {
                    System.out.println("could not store file");
                }
                fin.close();
            } else {
                System.out.println("FTP login failed");
            }
        } else {
            System.out.println("FTP connect to host failed");
        }
    } catch (IOException ioe) {
        ioe.printStackTrace();
        System.out.println("FTP client received network error");
    } finally {
        if (fin != null) {
            try {
                fin.close();
            } catch (IOException ioe) {
                //do nothing
            }
        }
        
    }
    
    异常:  
    javax.net.ssl.SSLHandshakeException:Remote host closed connection during handshake
    FTP client received network error
    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(FTPClient.java:646)
    at org.apache.commons.net.ftp.FTPSClient.storeFile(FTPClient.java:653)
    at org.apache.commons.net.ftp.FTPSClient.storeFile(FTPClient.java:2030)
    at ibgdashboardtest.Demo4.main(Demo4.java:75)
    Caused by:java.io.EOFException:SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(Unknown Source)
    ... 9 more
    

    解决方法:自己定义一个类继承FTPSClient,重载_prepareDataSocket_(final Socket socket)方法,添加了TLS的session hash支持并扩展了密钥,使用时用该类来替代FTPSClient的使用

    import java.io.IOException;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.net.Socket;
    import java.util.Locale;
    
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.SSLSessionContext;
    import javax.net.ssl.SSLSocket;
    
    import org.apache.commons.net.ftp.FTPSClient;
    
    public class SSLSessionReuseFTPSClient extends FTPSClient {
    
        // adapted from:
        // https://trac.cyberduck.io/browser/trunk/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java
        @Override
        protected void _prepareDataSocket_(final Socket socket) throws IOException {
            if (socket instanceof SSLSocket) {
                // Control socket is SSL
                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");
                }
            }
        }
    }
    

    如果出现兼容问题,应用程序可能会通过JDK中将系统设置属性jdk.tls.useExtendedMasterSecret设置为false来禁用此扩展,在JDK 1.8.0_161中设置:

    System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
    

    以上解决方法源自:https://stackoverflow.com/questions/32398754/how-to-connect-to-ftps-server-with-data-connection-using-same-tls-session

  • 相关阅读:
    Linux下处理文件中的^M
    python selenium-webdriver 生成测试报告 (十四)
    Apache 配置Https 转发Tomcat Http
    自动化测试神器 之 python unittest 断言
    创建高性能索引笔记
    【转】正向代理vs反向代理
    vue 常用问题
    eslint 代码规范2
    WebStrom2018注册码
    Vue-selller 饿了吗
  • 原文地址:https://www.cnblogs.com/zys-blog/p/9454589.html
Copyright © 2011-2022 走看看