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); } }
浙公网安备 33010602011771号