一、背景说明
1、开发环境:
- targetSdk 35
- commons-net:3.11.1
2、开发背景:
之前使用FTP连接FileZilla Server。现在要求使用FTPS连接。
实现方法:
二、Android 端
1、增加依赖库:
// Android API restriction bypass for all Android Versions
implementation 'com.github.ChickenHook:RestrictionBypass:2.2'
2、新建FTPClient子类:
public class SSLSessionReuseFTPSClient extends FTPSClient {
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
final SSLSession sessionAux = ((SSLSocket) _socket_).getSession();
if (sessionAux.isValid()) {
final SSLSessionContext sessionsContext = sessionAux.getSessionContext();
try {
// lets find the sessions in the context' cache
final Field fieldSessionsInContext = sessionsContext.getClass().getDeclaredField("sessionsByHostAndPort");
fieldSessionsInContext.setAccessible(true);
final Object sessionsInContext = fieldSessionsInContext.get(sessionsContext);
// lets find the session of our conexion
int portNumb = sessionAux.getPeerPort();
Set keys = ((HashMap) sessionsInContext).keySet();
if (keys.size() == 0)
throw new IOException("Invalid SSL Session");
final Field fieldPort = ((keys.toArray())[0]).getClass().getDeclaredField("port");
fieldPort.setAccessible(true);
int i = 0;
while (i < keys.size() && ((int) fieldPort.get((keys.toArray())[i])) != portNumb)
i++;
if (i < keys.size()) // it was found
{
Object ourKey = (keys.toArray())[i];
// building two objects like our key but with the new port and the host Name and host address
final Constructor construc = ourKey.getClass().getDeclaredConstructor(String.class, int.class);
construc.setAccessible(true);
Object copy1Key = construc.newInstance(socket.getInetAddress().getHostName(), socket.getPort());
Object copy2Key = construc.newInstance(socket.getInetAddress().getHostAddress(), socket.getPort());
// getting our session
Object ourSession = ((HashMap) sessionsInContext).get(ourKey);
// Lets add the pairs copy1Key-ourSession & copy2Key-ourSession to the context'cache
final Method method = sessionsInContext.getClass().getDeclaredMethod("put", Object.class, Object.class);
method.setAccessible(true);
method.invoke(sessionsInContext, copy1Key, ourSession);
method.invoke(sessionsInContext, copy2Key, ourSession);
} else
throw new IOException("Invalid SSL Session");
} catch (NoSuchFieldException e) {
throw new IOException(e);
} catch (Exception e) {
throw new IOException(e);
}
} else {
throw new IOException("Invalid SSL Session");
}
}
}
}
3、修改连接方法:
原先初始化FTP对象的语句为:
FTPClient ftpClient = new FTPClient();
现改为:
FTPClient ftpClient = new SSLSessionReuseFTPSClient();
或者可以直接将ftpClient声明为SSLSessionReuseFTPSClient类型。
在ftpClient.login()方法后加一句:
((SSLSessionReuseFTPSClient) ftpClient).execPROT("P");
如果有需要,加上这一句:
ftpClient.enterLocalPassiveMode();
这时就可以通过FTPS连接FileZilla Server了。
三、FileZilla Server 端
1、 选择协议:
点击FileZilla Server->Server->Configure菜单。在“Server listeners”中,Protocol有三个选项:
如果只想使用FTPS,就选择第一项:Require explicit FTP over TLS。
如果想同时使用FTPS和FTP,就选择第二项:Explicit FTP over TLS and insecure plain FTP。
2、使用证书:
在Settings窗口点击Protocols settings->FTP and FTP over TLS(FTPS)。可以设置TLS版本及证书等信息。这里可以上传证书,也可以使用FileZilla创建证书。
如果要使用FileZilla创建证书,点击”Generate new“按钮,在”Data for self-signed X.509 certificate“窗口中填写自己的相关信息(也可以什么都不填写)。点击”OK“按钮后,就能生成新的评书。有效期为一年。