Как подключиться к FTPS-серверу (безопасное FTP-соединение с поддержкой TLS) на Java 17

Мы рассмотрели несколько решений по передаче файлов с помощью FTPS. Они отлично работают с более низкими версиями Java, такими как Java 11 и т. д. Но сталкиваются с той же проблемой «неправильное завершение работы узла SSL» в Java 17. Сообщите нам, есть ли какое-либо решение для Java 17.

Реферальные ссылки:

Как подключиться к FTPS-серверу с подключением для передачи данных, используя тот же сеанс TLS? (Решение отлично работает в Java 11)

Трассировки стека:

Вызвано: java.io.EOFException: одноранговый узел SSL завершил работу неправильно в java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:483) в java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:472) по адресу java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:160) по адресу java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:111) по адресу java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1505) ... еще 9

Уже пробовал ниже ссылки:

Исключение FTPSClient javax.net.ssl.SSLHandshakeException: соединение с удаленным хостом закрыто во время рукопожатия

Решение:

Добавление System.setProperty("jdk.tls.client.enableSessionTicketExtension", "false") устранило проблему!

1 ответ

Я столкнулся с той же проблемой при попытке подключения к серверу FileZilla с помощью Apache FTPSClient на Java 17.
Наконец, соединение заработало с этой конфигурацией:

      FTPClient client = new FTPSClientWithSessionResumption("TLSv1.2");

Понижение версии TLS (TLSv1.2 вместо TLSv1.3)

По этой ссылке: https://gitlab.com/gnutls/gnutls/-/issues/1451
JDK имеет проблемы совместимости с TLSv1.3, вызванные нестандартным использованием сообщения user_canceled.

Использование специальной реализации FTPSClient, которая позволяет повторно использовать сеанс SSL.

Другими словами, если вы хотите передать файл или прочитать содержимое каталога и, таким образом, должны открыть сокет данных, если управляющее соединение защищено (TLS), то должны быть выполнены оба следующих условия:

  • соединение для передачи данных также должно быть безопасным (TLS);
  • соединение для передачи данных должно использовать сеанс TLS совместно с управляющим соединением, к которому оно относится.

форума Filezilla )

По этой ссылке: Как установить FTPS-соединение для передачи данных с сервером FileZilla 1.2.0.

Вот скопированная/вставленная реализация, отвечающая предварительным условиям.

      public class FTPSClientWithSessionResumption extends FTPSClient {
    static {
        System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
        System.setProperty("jdk.tls.client.enableSessionTicketExtension", "false");
    }

    public FTPSClientWithSessionResumption() {
        super();
    }

    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 {
                    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");
            }
        }
    }
}

Вы должны открыть эти два модуля, чтобы разрешить отражение SSLSession:--add-opens=java.base/sun.security.ssl=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED

Другие вопросы по тегам