<分区>
我已阅读 RFC 并尝试在我的 FTP 服务器中实现 SSL 连接。
我已经使用 javax.net.ssl.SSLSocket 将我所有的套接字切换到 SSLServer 套接字和 SSLsockets; javax.net.ssl.SSLSocketFactory;
当使用 FileZilla 使用 SSL 显式加密时,我收到一条消息说, “连接到 127.0.0.1,协商 SSL 连接”
它会卡在那里。
如何对我的服务器进行编程以协商 SSL 连接?
下面这段代码是启动 ServerSocket 并等待连接的地方。
// Starts a server Socket to listen to new clients
System.setProperty("javax.net.ssl.trustStore","ServerFiles\\default_KeyStore\\server.keystore");
SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
this.entryPortSock = (SSLServerSocket)sslserversocketfactory.createServerSocket((Integer) st.getValue("CMDPort"));
SSLSocket scCMDPort;
// Initiates the Rolling Log
RollingLog rollingLog = new RollingLog((String) st.getValue("LOGfiledirectory"), st);
Runnable RunnableRollingLog = rollingLog;
Thread logger = new Thread(RunnableRollingLog);
logger.start();
// Continues to listen to new clients and connect to them when
// available.
while (true) {
scCMDPort =(SSLSocket) this.entryPortSock.accept();
System.out.println("System accepted a new friend!");
// Starting the service for the client
Runnable ftpService = new FTPService(rollingLog, (String) st.getValue("ROOT"), scCMDPort, st);
Thread service = new Thread(ftpService);
service.start();
}
下面是我的服务器可以从客户端接受的支持的命令。
private void action(ServerCmd command) {
// Process of logging in, Must pass this boundary before any other
// command can be given.
if (DEBUG_INCOMING) {
System.out.println("Client: " + command.cmdType + " " + reJoinString(command.args));
}
printLog("Client: " + command.cmdType + " " + reJoinString(command.args));
if (command.cmdType.equals(ServerCommands.AUTH)) {
if(command.args.get(0).equalsIgnoreCase("SSL")){
this.sendOut("334 Yo homie we can start that SSL connection!");
}
}
else if (command.cmdType.equals(ServerCommands.USER)) {
this.User(command);
}
else if (command.cmdType.equals(ServerCommands.PASS)) {
this.Pass(command.args.get(0));
}
// After login in, all of these commands can be read.
else if (this.Status == 1) {
if (command.cmdType.equals(ServerCommands.SYST)) {
// Reads a request for the operating system of the Server.
this.Syst();
}
else if (command.cmdType.equals(ServerCommands.FEAT)) {
// Reads a request FEAT
this.Feat();
} else if (command.cmdType.equals(ServerCommands.PWD)) {
// Reads a request to print the working directory
this.Pwd();
}
else if (command.cmdType.equals(ServerCommands.TYPE)) {
// Read a request Type
this.Type(command.args.get(0));
}
else if (command.cmdType.equals(ServerCommands.PASV)) {
// Request PASV connection
this.Pasv();
} else if (command.cmdType.equals(ServerCommands.LIST)) {
// Request directory listing
this.List();
}
else if (command.cmdType.equals(ServerCommands.CWD)) {
//
String temp = this.reJoinString((command.args));
this.Cwd(temp);
}
else if (command.cmdType.equals(ServerCommands.CDUP)) {
this.Cdup();
}
else if (command.cmdType.equals(ServerCommands.STOR)) {
this.Stor(this.reJoinString(command.args));
} else if (command.cmdType.equals(ServerCommands.RETR)) {
this.Retr(this.reJoinString(command.args));
} else if (command.cmdType.equals(ServerCommands.QUIT)) {
System.exit(0);
} else if (command.cmdType.equals(ServerCommands.HELP)) {
sendOut("214 Help: Connect to FileZilla, the older version without caching to use this FTP Server. Or, a command prompt client would work as well.\n"
+ "Accepted FTP Commands: USER, PASS, SYST, FEAT, PWD, TYPE, PASV, LIST, CWD, CDUP, EPSV, STOR, RETR, QUIT");
} else if (command.cmdType.equals((ServerCommands.PORT))) {
if (((String) (this.settings.getValue("PORTmode"))).equalsIgnoreCase("YES")) {
this.Port(this.reJoinString(command.args));
} else {
sendOut("522 This command is not supported");
}
} else if (command.cmdType.equals(ServerCommands.EPRT)) {
if (((String) (this.settings.getValue("PORTmode"))).equalsIgnoreCase("YES")) {
this.EPRT(this.reJoinString(command.args));
} else {
sendOut("522 This command is not supported");
}
} else if (command.cmdType.equals((ServerCommands.EPSV))) {
// Turn true to support EPSV
if (((String) (this.settings.getValue("PASVmode"))).equalsIgnoreCase("YES")) {
this.EPSV();
} else {
sendOut("522 This command is not supported");
}
}
}
更新: 一旦从客户端发送了 AUTH SSL 命令,我就实现了从套接字到 SSL 套接字的升级。这是我的代码:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
this.clientSocket1 = sslsocketfactory.createSocket(serverSocket.getInetAddress().getHostAddress(), serverSocket.getLocalPort());
((SSLSocket)this.clientSocket1).setUseClientMode(false);
this.sendOut("334 Yo homie we can start that SSL connection!");
客户端响应: Client: €j Q [] 9 8 5 [] [] 类似的东西。如何解密消息?
为了尝试解决上述问题,我使用以下代码重新实例化了我的缓冲区读取器,该读取器从常规套接字读取到来自 SSLSocket 的流:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
this.clientSocket1 = sslsocketfactory.createSocket(serverSocket.getInetAddress().getHostAddress(), serverSocket.getLocalPort());
((SSLSocket)this.clientSocket1).setUseClientMode(false);
((SSLSocket)this.clientSocket1).startHandshake();
cipher =sslsocketfactory.getDefaultCipherSuites();
this.encrypted = true;
this.sendOut("334 Yo homie we can start that SSL connection!");
this.in = new BufferedReader(new InputStreamReader(((SSLSocket)this.clientSocket1).getInputStream()));
this.out = new PrintWriter(((SSLSocket)this.clientSocket1).getOutputStream());
可能的握手崩溃的整个堆栈跟踪。
javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
at sun.security.ssl.InputRecord.handleUnknownRecord(Unknown Source)
at sun.security.ssl.InputRecord.read(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
at sun.security.ssl.AppInputStream.read(Unknown Source)
at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
at sun.nio.cs.StreamDecoder.read(Unknown Source)
at java.io.InputStreamReader.read(Unknown Source)
at java.io.BufferedReader.fill(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at FTPServer.FTPService.run(FTPService.java:100)
at java.lang.Thread.run(Unknown Source)
客户端的日志:<--- 文件 zilla 响应:220 IiiiDeentiffy YouuurSelf!!! 命令:AUTH SSL Response: 234 Yo homie 我们可以启动 SSL 连接了!
服务器的日志<--- 我正在编程的那个
javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
at sun.security.ssl.SSLSocketImpl.checkEOF(Unknown Source)
at sun.security.ssl.AppInputStream.read(Unknown Source)
at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
at sun.nio.cs.StreamDecoder.read(Unknown Source)
at java.io.InputStreamReader.read(Unknown Source)
at java.io.BufferedReader.fill(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at FTPServer.FTPService.run(FTPService.java:100)
at java.lang.Thread.run(Unknown Source)
Caused by: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
at sun.security.ssl.InputRecord.handleUnknownRecord(Unknown Source)
at sun.security.ssl.InputRecord.read(Unknown Source)
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.getSession(Unknown Source)
at FTPServer.FTPService.action(FTPService.java:143)
at FTPServer.FTPService.run(FTPService.java:102)
... 1 more
服务线程已停止?!
较短的版本
package testServer;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class driverServerTest {
public static void main(String args[]) {
try {
ServerSocket Ssc = new ServerSocket(21);
Socket sc;
sc = Ssc.accept();
PrintWriter out = new PrintWriter(sc.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(sc.getInputStream()));
out.println("220 The connection succeeded");
out.flush();
String temp;
while (true) {
temp = in.readLine();
System.out.println(temp);
if (temp.equalsIgnoreCase("AUTH SSL")) {
out.println("334 Yo homie we can start that SSL connection!");
out.flush();
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
sc = sslsocketfactory.createSocket(Ssc.getInetAddress().getHostAddress(), Ssc.getLocalPort());
((SSLSocket) sc).setUseClientMode(false);
in = new BufferedReader(new InputStreamReader(((SSLSocket) sc).getInputStream()));
out = new PrintWriter(((SSLSocket) sc).getOutputStream());
out.println("234 request login information");
out.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户回复:
Status: Connecting to 127.0.0.1 ...
Status: Connected with 127.0.0.1, negotiating SSL connection...
Response: 220 The connection succeeded
Command: AUTH SSL
Response: 334 Yo homie we can start that SSL connection!
最佳答案
您需要从普通的 ServerSocket
开始,然后使用 SSLSocketFactory.createSocket(Socket, ...)
将普通的 Socket
升级为如果您收到 AUTH TLS 命令,则使用 SSL。
在使用它执行任何 I/O 之前,不要忘记调用 SSLSocket.setUseClientMode(false);
。
关于java - FTP协议(protocol)如何协商SSL连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33729974/