Перенос файлов с андроида с FTPS на сервер

Я использую FTP-библиотеку Apache Commons в своем приложении для Android

Я делаю соединение через FTPS, и, хотя он прекрасно подключается к серверу, у меня есть проблема при передаче файлов.

Клиент, который заказывает приложение по соображениям безопасности, запрашивает возобновление сеанса TLS при подключении к данным при использовании PROT P.

Поэтому у меня включена эта опция на сервере:

Как я уже сказал, я могу подключиться к серверу, но не передавать файлы.Если я деактивирую поле "Требуется возобновление сеанса TLS при подключении к данным при использовании PROT P", передача работает правильно.

Я ищу способ передачи файлов с использованием библиотеки, но без успеха, однако, я понимаю, что должен быть способ.

Я даю вам часть связанного кода:

TransferImagenesFTP.ftpClient = new FTPSClient();

TransferImagenesFTP.ftpClient.connect(InetAddress.getByName("XXX_XXX_XX_X"), 26);
TransferImagenesFTP.ftpClient.enterLocalPassiveMode();
TransferImagenesFTP.ftpClient.setBufferSize(1024000);
TransferImagenesFTP.ftpClient.login("xxxxxx", "zzzzzz");
TransferImagenesFTP.ftpClient.execPROT("P");
TransferImagenesFTP.ftpClient.type(FTP.BINARY_FILE_TYPE);

Я ценю любую помощь, спасибо.

2 ответа

Проблема в вашем случае заключается в том, что Apache FTPSClient не поддерживает возобновление сеанса TLS и, таким образом, завершается неудачно при попытке передать файл.

Понимание проблемы

Когда вы подключаетесь к FTP-серверу по протоколу TLS, сервер инициирует безопасный сеанс ssl с клиентом на управляющем соединении. Затем клиент входит в пассивный режим, отправив PASV Команда и в ответ сервер открывает случайный непривилегированный порт и отправляет в ответ номер порта клиенту. Этот порт представляет соединение для передачи данных. Теперь, чтобы безопасно подключиться к этому новому порту, клиент должен повторно использовать существующий сеанс TLS, который он уже имеет с сервером на управляющем соединении.

Зачем повторно использовать сеанс TLS?

Отсутствие необходимости возобновления сеанса допускает атаки с кражей сеансов. Проблема с FTP заключается в том, что соединение для передачи данных не аутентифицирует клиента.
Если сервер / клиент не использует повторно существующий сеанс TLS, злоумышленник мог бы вместо этого подключиться к порту данных и загрузить вредоносное ПО. Таким образом, для защиты от такой атаки FTP-сервер требует, чтобы клиент повторно использовал уже установленный сеанс.

В вашем случае Apache FTPSClient не может повторно использовать сеанс (это известная проблема), и, таким образом, сервер считает, что ваш клиент не авторизован, и запрещает передачу.

Ознакомьтесь с публикацией Wealthfront о том, как патчить и пример реализации.

Источники:

Вы можете попробовать следующий код, надеюсь, он подойдет и для вашего случая.

Код использует Apache Commons vsf2 для загрузки файла через безопасное FTP-соединение (SFTP).

try {
  String filepath = "<FILE PATH>";
  String serverAddress = "<FTP SERVER ADDRESS>";
  String userId = "<FTP USER ID>";
  String password = "<FTP PASSWORD>";
  String remoteDirectory = "<FTP DIRECTORY TO UPLOAD TO>";   
  String keyPath = "<PATH TO YOUR KEY>";   
  String passPhrase = "<PASSWORD FOR YOUR KEY>";   


  File file = new File(filepath);
  if (!file.exists())
    throw new RuntimeException("Error. File not found");

  //Initializes the file manager
  StandardFileSystemManager manager = new StandardFileSystemManager();
  manager.init();

  //Setup our SFTP configuration
  FileSystemOptions opts = new FileSystemOptions();
  SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(opts, "no");
  SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);
  SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);

  // Create local file object
  FileObject localFile = manager.resolveFile(file.getAbsolutePath());

  // Create remote file object
  FileObject remoteFile = manager.resolveFile(createConnectionString(serverAddress, userId, password, keyPath, passPhrase, fileToFTP), createDefaultOptions(keyPath, passPhrase));


  // Copy local file to sftp server
  remoteFile.copyFrom(localFile, Selectors.SELECT_SELF);
  System.out.println("File upload successful");

}
catch (Exception ex) {
  ex.printStackTrace();
  return false;
}
finally {
  manager.close();
}

Вы можете проверить больше в документации Apache Commons VFS

отредактированный

После понимания логики, стоящей за FTPS и постом @riyaz-ali, и ссылкой на ссылку в вашем комментарии к этой статье

Существует проблема с клиентом Apache FTP, он не поддерживает возобновление сеанса TLS. Вы можете исправить существующую реализацию библиотеки Apache Commons.

Вы можете попробовать следующие шаги кода, чтобы заставить его работать:

  1. Добавьте следующий пропатченный класс в ваш проект. (Этот класс расширяет существующую реализацию FTPS, данную в Apache Commons с патчем)

    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;
    
    import com.google.common.base.Throwables;
    
    public class PatchedFTPSClient extends FTPSClient {
    
            @Override
            protected void _prepareDataSocket_(final Socket socket) throws IOException {
                    if(socket instanceof SSLSocket) {
                            final SSLSession session = ((SSLSocket) _socket_).getSession();
                            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);
                                    final String key = String.format("%s:%s", socket.getInetAddress().getHostName(),
                                                                                                    String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
                                    method.invoke(cache, key, session);
                            } catch(Exception e) {
                                    throw Throwables.propagate(e);
                            }
                    }
            }
    
    }
    
  2. Используйте этот измененный фрагмент кода.

    TransferImagenesFTP.ftpClient = new PatchedFTPSClient();
    
    TransferImagenesFTP.ftpClient.connect(InetAddress.getByName<SERVER-ADDRESS>"), 26);
    TransferImagenesFTP.ftpClient.login("<USERNAME>", "<PASSWORD>");
    TransferImagenesFTP.ftpClient.execPBSZ(0);
    TransferImagenesFTP.ftpClient.execPROT("P");
    TransferImagenesFTP.ftpClient.enterLocalPassiveMode();
    
    //Now use the FTP client to upload the file as usual.
    

    Надеюсь, что это сработает для вас и решит вашу проблему.

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