Я разрабатываю приложение на Android с Android Studio и хочу использовать FTP для отправки файлов на сервер. Мне нужно поддерживать повторное использование сеанса, так как сервер размещен у поставщика услуг хостинга, и у него, очевидно, включено повторное использование сеанса.
Я нашел этот взлом отражения в этом сообщении , используемом многими чтобы сделать это возможным:
// adapted from:
// https://trac.cyberduck.io/browser/trunk/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java
@Override
protected void _prepareDataSocket_(final 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 method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
method.setAccessible(true);
method.invoke(cache, String
.format("%s:%s", socket.getInetAddress().getHostName(), String.valueOf(socket.getPort()))
.toLowerCase(Locale.ROOT), session);
method.invoke(cache, String
.format("%s:%s", socket.getInetAddress().getHostAddress(), String.valueOf(socket.getPort()))
.toLowerCase(Locale.ROOT), session);
} catch (NoSuchFieldException e) {
throw new IOException(e);
} catch (Exception e) {
throw new IOException(e);
}
} else {
throw new IOException("Invalid SSL Session");
}
}
}
Вот код, который использует SSLSessionReuseFTPSClient:
System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
String host = "xxxxxxxx";
String user = "xxxxxxxx";
String password = "xxxxxxxx";
String directory = "xxxxxxxx";
ProtocolCommandListener listener = new MyProtocolCommandListener(host);
SSLSessionReuseFTPSClient client = new SSLSessionReuseFTPSClient("TLS", false);
client.addProtocolCommandListener(listener);
try {
client.connect(host);
client.execPBSZ(0);
client.execPROT("P");
if (client.login(user, password)) {
Log.w("myApp", "Logged in as " + user + " on " + host + ".");
}
if (client.changeWorkingDirectory(directory)) {
Log.w("myApp", "Working directory changed to " + directory + ".");
}
client.enterLocalPassiveMode();
InputStream input = new FileInputStream(file);
if (client.storeFile(file.getName(), input)) {
Log.w("myApp", "File " + file.getName() + " sent to " + host + ".");
} else {
Log.w("myApp", "Couldn't send file " + file.getName() + " to " + host + ".");
Log.w("myApp", "Reply: " + client.getReplyString());
}
client.logout();
client.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
Я впервые попробовал в Eclipse, и это сработало. Затем я попытался реализовать это в своем приложении Android, но я получаю эту ошибку:
java.io.IOException: java.lang.NoSuchFieldException: No field sessionHostPortCache in class Lcom/android/org/conscrypt/ClientSessionContext; (declaration of 'com.android.org.conscrypt.ClientSessionContext' appears in /system/framework/conscrypt.jar)
Я заметил, что когда я выполняю код в Eclipse и печатаю имя класса context
, Я получаю это: sun.security.ssl.SSLSessionContextImpl
, но в Android Studio я получаю: com.android.org.conscrypt.ClientSessionContext
.
Я искал почти два дня подряд, и я просто недостаточно опытен знать, что случилось. Почему com.android.org.conscrypt.ClientSessionContext
используется вместо sun.security.ssl.SSLSessionContextImpl
? Я проверяю файл java .security и из того, что я вижу, следует использовать sun.security.ssl.SSLSessionContextImpl
.
Если бы кто-то мог помочь мне с этим, я был бы безумно благодарен.
Наконец, Вот некоторая информация, которая может быть полезна:
Android Studio 3.6.2
commons-net-3.6
openjdk version "1.8.0_212-release"
OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b04)
OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)
Спасибо!