我正在尝试连接到需要'显式FTP over TLS'的FTP。我试图从我的本地机器上做到这一点。
以下是我使用的代码
FTPSClient ftpClient = new FTPSClient(false);
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
ftpClient.setAuthValue("TLS");
ftpClient.connect(host, port);
int reply = ftpClient.getReplyCode();
if (FTPReply.isPositiveCompletion(reply)) {
if (ftpClient.login(username, password)) {
ftpClient.execPBSZ(0);
ftpClient.execPROT("P");
ftpClient.enterLocalPassiveMode();
InputStream is = new FileInputStream(localFilename);
if (ftpClient.storeFile(remoteFilename, is)) {
is.close();
} else {
System.out.println("Could not store file");
}
ftpClient.logout();
} else {
System.out.println("FTP login failed");
}
// Disconnect
ftpClient.disconnect();
} else {
System.out.println("FTP connect to host failed");
}
当我跑步时,我得到以下异常
Could not connect to server.
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:240)
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:171)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:178)
at TestFTP.main(TestFTP.java:64)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at sun.security.ssl.InputRecord.read(Unknown Source)
... 8 more
以下是我从Java CommandListener获取的日志
220-FileZilla Server 0.9.60 beta
220-written by Tim Kosse (tim.kosse@filezilla-project.org)
220 Please visit https://filezilla-project.org/
AUTH TLS
234 Using authentication type TLS
FTP client received network error javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
以下是我尝试使用FileZilla进行连接时的日志。它连接正常,我可以传输文件。但是当我尝试使用Java时,它并没有连接
Response: 220-FileZilla Server 0.9.60 beta
Response: 220-written by Tim Kosse (tim.kosse@filezilla-project.org)
Response: 220 Please visit https://filezilla-project.org/
Command: AUTH TLS
Response: 234 Using authentication type TLS
Status: Initializing TLS...
Status: Verifying certificate...
Command: USER
Status: TLS/SSL connection established.
Response: 331 Password required
Command: PASS xxxx
Response: 230 Logged on
Command: PBSZ 0
Response: 200 PBSZ=0
Command: PROT P
Response: 200 Protection level set to P
Status: Connected
Status: Retrieving directory listing...
Command: PWD
Response: 257 "/" is current directory.
Command: TYPE I
Response: 200 Type set to I
Command: PASV
Response: 227 Entering Passive Mode ()
Command: MLSD
Response: 150 Opening data channel for directory listing of "/"
Response: 226 Successfully transferred "/"
Status: Directory listing successful
Status: Retrieving directory listing...
Command: PASV
Response: 227 Entering Passive Mode ()
Command: MLSD
Response: 150 Opening data channel for directory listing of "/"
Response: 226 Successfully transferred "/"
Status: Directory listing successful
你能帮忙吗?
亲切的问候 乔恩
答案 0 :(得分:1)
首先,请确保连接到FTP服务器的普通端口,因为您要在FTPS-explict-mode中实例化FTPSClient。
您使用Java 7,直到更新75仍使用SSLv2Hello启动SSL会话。在Heartbleed,BEAST以及过去几年中在SSL中发现的所有其他漏洞之后,大多数基于SSL的服务器现在拒绝使用版本< TLS-packets的TLS包进行握手尝试。 TLS 1.0。我想这也是在这里发生的。
找出我是否正确的最简单方法是尝试使用Java 8(切换到TLS 1.2)启动应用程序。如果可以,您需要确保停用SSLv2Hello。这可以通过使用以下系统属性调用JVM来全局完成:
java -Dhttps.protocols="TLSv1,TLSv1.1,TLSv1.2" -Djdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2" <MyApp>
或在执行握手之前在Socket上设置允许的协议,如下所示:
sslSocket.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
或设置相应的SSLSocketFactory
到FTPClient
,以便在FTPClient
需要SSLSocket
时立即设置协议,如上所示。我不知道Apache的FTPClient
所以我不能告诉你如何做到这一点,但文档或谷歌搜索应该可以帮助你。
但无论如何,停用SSLv2Hello是一件好事[TM],所以将JVM更新到最新版本(无论是Java 7 Update 80还是Java 8 Update 161)绝不是一个坏主意,也是为了获得所有其他修复与SSL无关。
如果我关于SSLv2Hello的理论不正确,那么唯一可以帮助您解决此问题的下一步就是使用像Wireshark这样的TCP记录器,它可以向您显示实际的SSL握手以及对等服务器在什么时候关闭连接。通常,您可以在该跟踪中看到SSL警报,解释在Java中收到的异常中未显示的实际问题。
答案 1 :(得分:0)
该解决方案很简单,并且在技术上有些棘手,根据已建立连接的SSL的 Session 假设,也应该将其用于数据,但是对于FTPS,库会尝试 FTPS类中未定义数据会话失败尝试,并且处理此操作的方法未定义。因此,解决方案是简单地扩展 FTPS类并提供该方法的实现。
以下是示例
public class ModifiedFTPSClient extends FTPSClient {
public ModifiedFTPSClient() {
super("TLS", false);
}
public ModifiedFTPSClient(boolean isImplicit) {
super("TLS", isImplicit);
}
@Override
protected void _prepareDataSocket_(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
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");
}
}
}}
现在,如果我尝试使用扩展类,它将在同一会话中运行。即
ModifiedFTPSClient ftpClient = new ModifiedFTPSClient(true);
ftpClient.addProtocolCommandListener(new PrintCommandListener(System.out));
ftpClient.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager());... and so on.
答案 2 :(得分:-1)
我使用Cyberduck库解决了这个问题。
https://github.com/iterate-ch/cyberduck
首先是为什么会出现握手问题,
因为一些进步FTP服务器只允许一个会话进行连接和数据传输。
1)控制会话 - &gt;用于连接
2)数据会话 - &gt;数据存储/下载/等
那么如何解决这个问题。
第1步 -
首先你需要从GitHub查看cyberduck repo。 从这里 - :https://github.com/iterate-ch/cyberduck
第2步 - 您将在结帐回购中看到FTP和核心模块 ftp and core modules
第3步 - 将此模块转换为maven并为其创建jar。
第4步 - 将此jar添加到您的项目中,但此jar还需要其他依赖项,因此也将相应的内容添加到项目构建路径中。
第5步 - 代码段。
public class TestFTPS {
public static void main(String[] args) throws SocketException, IOException {
TrustManager[] trustManagers = new TrustManager[] { new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub
}
} };
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustManagers, new SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
Protocol protocol = new FTPTLSProtocol();
FTPClient client = new FTPClient(protocol, sslSocketFactory, sslContext);
client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
client.connect(ftphostname, port);
client.execAUTH("TLS"); //SSL
System.out.println(client.getReplyCode());
if (client.login(username, password)) {
client.execPBSZ(0);
client.execPROT("P");
String date = "Testing Data Send to provide FTP location";
client.setFileType(FTP.BINARY_FILE_TYPE);
client.enterLocalPassiveMode();
InputStream is = new ByteArrayInputStream(date.getBytes());
client.storeFile("test.txt", is);
System.out.print(client.getReplyCode());
}
}
}
步骤6 - :运行此代码后,您的文件将成功传输到ftp位置。
注意 - :请在项目中添加所需的jar
希望这会对你有帮助。