jdk17 连接 ftp over tls

 

 

https://stackoverflow.com/questions/76372858/how-to-connect-to-ftps-server-tls-enabled-secure-ftp-connection-on-java-17

https://stackoverflow.com/questions/49257998/ssl-session-reuse-in-apache-ftps-client-in-jdk-8u161

https://stackoverflow.com/questions/70903926/how-to-establish-a-ftps-data-connection-to-a-filezilla-server-1-2-0

https://stackoverflow.com/questions/32398754/how-to-connect-to-ftps-server-with-data-connection-using-same-tls-session

 

https://gitlab.com/gnutls/gnutls/-/issues/1451

 

 

package com.nvxclouds.baize.ftp.ferry.client;

import org.apache.commons.net.ftp.FTPSClient;
import sun.misc.Unsafe;

import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Locale;

/**
 * According this link :
 * https://gitlab.com/gnutls/gnutls/-/issues/1451
 * https://forum.filezilla-project.org/viewtopic.php?t=56219
 * JDK has a compatibility issues with TLSv1.3 caused by a non-standard use of 'user_canceled' message.
 */
public class FTPSClientWithSessionResumption extends FTPSClient {
    static {
        System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
        System.setProperty("jdk.tls.client.enableSessionTicketExtension", "false");
    }

    public FTPSClientWithSessionResumption() {
        super();
    }

    public FTPSClientWithSessionResumption(boolean isImplicit) {
        super(isImplicit);
    }

    public FTPSClientWithSessionResumption(String protocol) {
        super(protocol);
    }

    @Override
    protected void _connectAction_() throws IOException {
        super._connectAction_();
        execPBSZ(0);
        execPROT("P");
    }

    @Override
    protected void _prepareDataSocket_(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 {
                    unsafeSetup();
                    final Field sessionHostPortCache = context.getClass()
                        .getDeclaredField("sessionHostPortCache");
                    sessionHostPortCache.setAccessible(true);
                    final Object cache = sessionHostPortCache.get(context);
                    final Method putMethod = cache.getClass()
                        .getDeclaredMethod("put", Object.class, Object.class);
                    putMethod.setAccessible(true);
                    Method getHostMethod;
                    try {
                        getHostMethod = socket.getClass()
                            .getMethod("getPeerHost");
                    } catch (final NoSuchMethodException e) {
                        // Running in IKVM
                        getHostMethod = socket.getClass()
                            .getDeclaredMethod("getHost");
                    }
                    getHostMethod.setAccessible(true);
                    final Object peerHost = getHostMethod.invoke(socket);
                    final InetAddress iAddr = socket.getInetAddress();
                    final int port = socket.getPort();
                    putMethod.invoke(cache, String.format("%s:%s", peerHost, port)
                        .toLowerCase(Locale.ROOT), session);
                    putMethod.invoke(cache, String.format("%s:%s", iAddr.getHostName(), port)
                        .toLowerCase(Locale.ROOT), session);
                    putMethod.invoke(cache, String.format("%s:%s", iAddr.getHostAddress(), port)
                        .toLowerCase(Locale.ROOT), session);
                } catch (final Exception e) {
                    throw new IOException(e);
                }
            } else {
                throw new IOException("Invalid SSL Session");
            }
        }
    }

    private void unsafeSetup() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field field = unsafeClass.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        Module baseModule = Object.class.getModule();
        Class currentClass = FTPSClientWithSessionResumption.class;
        long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
        unsafe.getAndSetObject(currentClass, addr, baseModule);
    }
}

 

posted @ 2025-05-31 09:38  牧之丨  阅读(40)  评论(0)    收藏  举报