Перенос файлов с андроида с 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.
Вы можете попробовать следующие шаги кода, чтобы заставить его работать:
Добавьте следующий пропатченный класс в ваш проект. (Этот класс расширяет существующую реализацию 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); } } } }
Используйте этот измененный фрагмент кода.
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.
Надеюсь, что это сработает для вас и решит вашу проблему.