Skip to content

Instantly share code, notes, and snippets.

@riyaz-ali
Created February 20, 2018 05:09
Show Gist options
  • Save riyaz-ali/48fb486f51c258b4e92c2d2be30c35c4 to your computer and use it in GitHub Desktop.
Save riyaz-ali/48fb486f51c258b4e92c2d2be30c35c4 to your computer and use it in GitHub Desktop.
Patching TLS session resumption on Apache Commons Net FTPSClient
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPSClient;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
/**
* FTPUploadTest class to test FTPS upload using Apache commons net library
*/
public class FTPUploadTest {
public static void main(String[] args) throws IOException {
// file to upload
// create a test.jpeg file in your current directory
Path path = FileSystems.getDefault().getPath("test.jpeg").toAbsolutePath();
// we will be using https://dlptest.com/ftp-test/ FTP-server to test uploads
// thanks guys :D
FTPSClient client = new PatchedFTPSClient();
try {
// to debug
client.addProtocolCommandListener(new PrintCommandListener(System.out));
// connect to the server
client.connect("ftp.dlptest.com", 21);
// then login
client.login("[email protected]", "eiTqR7EMZD5zy7M");
// exec PROT P
client.execPBSZ(0);
client.execPROT("P");
// enter passive mode
client.enterLocalPassiveMode();
// upload
client.setFileType(FTP.BINARY_FILE_TYPE);
if (client.storeUniqueFile(new FileInputStream(path.toFile()))) {
System.out.println("File Saved!");
} else {
System.out.println("Failed to save file");
}
// list
for (FTPFile file : client.listFiles("/")) {
System.out.println(file.toFormattedString());
}
} finally {
client.disconnect();
}
}
}
import org.apache.commons.net.ftp.FTPSClient;
import javax.net.ssl.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Locale;
/**
* FTPSClient to patch TLS session resumption support in Apache FTPSClient
* see also: http://eng.wealthfront.com/2016/06/10/connecting-to-an-ftps-server-with-ssl-session-reuse-in-java-7-and-8/
* see also: https://gist.github.com/lukehansen/cd1931426d1e432d0993005f9976e6fb
*/
public class PatchedFTPSClient extends FTPSClient {
@Override protected void _prepareDataSocket_(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 putMethod = cache.getClass().getDeclaredMethod("put", Object.class, Object.class);
putMethod.setAccessible(true);
final Method getHostMethod = socket.getClass().getDeclaredMethod("getHost");
getHostMethod.setAccessible(true);
Object host = getHostMethod.invoke(socket);
final String key = String.format("%s:%s", host, String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT);
putMethod.invoke(cache, key, session);
} catch (Exception e) {
throw new IOException(e);
}
}
}
}
@HafizMSaad
Copy link

@riyaz-ali I am facing the same error as @anthonyblanchettepotvin

@HafizMSaad
Copy link

its not working on android!

@HafizMSaad
Copy link

No field sessionsByHostAndPort in class Lcom/android/org/conscrypt/ClientSessionContext; (declaration of 'com.android.org.conscrypt.ClientSessionContext' appears in /apex/com.android.conscrypt/javalib/conscrypt.jar)

@damian-cyte
Copy link

Hi. Thank you. I had to change .getDeclaredMethod("getHost"); for .getDeclaredMethod("getPeerHost"); in JRE8 (openJDK) to make it work. Also, in JRE8, you had to set the protocol in TLS1.2, with TLS1.3 fails (cause 1.8 doesn't support 1.3)

@HafizMSaad
Copy link

Hi. Thank you. I had to change .getDeclaredMethod("getHost"); for .getDeclaredMethod("getPeerHost"); in JRE8 (openJDK) to make it work. Also, in JRE8, you had to set the protocol in TLS1.2, with TLS1.3 fails (cause 1.8 doesn't support 1.3)

thanks, but I had to use this inside my android application and I wasn't able to access sessionsByHostAndPort key. So, I used RestrictionBypass and it worked!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment