首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Java SSLSocket的施用之二-让edtFTPj支持FTPS

2013-01-08 
Java SSLSocket的使用之二---让edtFTPj支持FTPS免费版的edtFTPj不支持FTPS等安全协议, 所以不能访问基于TL

Java SSLSocket的使用之二---让edtFTPj支持FTPS

免费版的edtFTPj不支持FTPS等安全协议, 所以不能访问基于TLS/SSL FTP服务器。最近对SSL有了些概念,项目也使用过edtFTPj库,所以尝试给这个库添加TLS/SSL支持,就当是个练习。

?

1. commons-net的FTP

?

commons-net包支持TLS/SSL FTP,首先参考它的实现。


Java SSLSocket的施用之二-让edtFTPj支持FTPS

FTP:实现了基本的FTP命令,

FTPClient:对FTP中的基本FTP命令进行封装

FTPSClient: 提供 TLS/SSL FTP功能.?

?

FTP、FTP、FTPSClient继承了SocketClient类的connect()方法, 该方法主要是发起到远端的连接并调用_connectAction_()方法。。 这三个子类重写了_connectAction_()方法。

?

?

    // SocketClient.java    public void connect(InetAddress host, int port,                        InetAddress localAddr, int localPort)    throws SocketException, IOException    {        _socket_ = _socketFactory_.createSocket();        if (receiveBufferSize != -1) {            _socket_.setReceiveBufferSize(receiveBufferSize);        }        if (sendBufferSize != -1) {            _socket_.setSendBufferSize(sendBufferSize);        }        _socket_.bind(new InetSocketAddress(localAddr, localPort));        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);        _connectAction_();    }

?

?

?

?

        // FTPSClient.java    protected void _connectAction_() throws IOException {        // Implicit mode.        if (isImplicit) {            sslNegotiation();        }        super._connectAction_();        // Explicit mode.        if (!isImplicit) {            execAUTH();            sslNegotiation();        }    }

?

?FTPSClient重写_connectAction_方法:如果isImplicit为false, 则执行AUTH命令并开始SSL握手;否则直接开始SSL握手。

?

?

    // FTPSClient    protected void sslNegotiation() throws IOException {        plainSocket = _socket_;        initSslContext();        SSLSocketFactory ssf = context.getSocketFactory();        String ip = _socket_.getInetAddress().getHostAddress();        int port = _socket_.getPort();        SSLSocket socket =            (SSLSocket) ssf.createSocket(_socket_, ip, port, false);        socket.setEnableSessionCreation(isCreation);        socket.setUseClientMode(isClientMode);        // server mode        if (!isClientMode) {            socket.setNeedClientAuth(isNeedClientAuth);            socket.setWantClientAuth(isWantClientAuth);        }        if (protocols != null) {            socket.setEnabledProtocols(protocols);        }        if (suites != null) {            socket.setEnabledCipherSuites(suites);        }        socket.startHandshake();        _socket_ = socket;        _controlInput_ = new BufferedReader(new InputStreamReader(                socket .getInputStream(), getControlEncoding()));        _controlOutput_ = new BufferedWriter(new OutputStreamWriter(                socket.getOutputStream(), getControlEncoding()));    }

?FTPSClient的sslNegotiation()方法说白了就是用一个已经连接到远程FTP服务器的Socket来创建一个SSLSocket,并替换原来的_socket_成员变量

?

?

2. edtFTPj添加FTPS支持

按照上面的思路给edtFTPj添加 FTPS支持。首先看edtFTPj主要的类,(蓝色是我添加的类)

?


Java SSLSocket的施用之二-让edtFTPj支持FTPS

我们写的应用使用edtFTPj包提供的FileTransferClient作为FTP客户端。一般这样使用

?

FileTransferClient client = new FileTransferClient();

?得到一个不支持TLS/SSL的FTP客户端。?

?

?

根据以上用法, 主要作以下修改:

1. 实现一个继承自FTPClient的类FTPSClient

2. 实现一个承自FTPControlSocket的类FTPSControlSocket

3. 实现一个SSLSocket的代理MySSLSocket, 这个类同时继承StreamSocket

4. 修改FileTransferClient的构造方法, 通过一个参数来区分是生成FTPClient还是FTPSClient对象

?

基本上,对原有代码的修改只有一处,其他位置都是扩展。 比较关键的两处代码如下

?

?

public class FTPSClient extends FTPClient {@Overridepublic void connect() throws IOException, FTPException {checkConnection(false);if (remoteAddr == null)remoteAddr = InetAddress.getByName(remoteHost);// 创建SocketFTPSControlSocket sock = new FTPSControlSocket(remoteAddr, controlPort,timeout, controlEncoding, messageListener);initialize(sock);// 在Socket上发出第一条命令AUTH TLSSystem.out.println("send AUTH TLS");lastReply = control.sendCommand("AUTH TLS");if (!"234".equals(lastReply.getReplyCode())) {System.err.println("err");throw new IOException();}// 创建SSLSocketStreamSocket ss = MySSLSocket.createSSLSocket(sock.getControlSock(),remoteAddr, controlPort, timeout);// 用SSLSocket替换原来的的Socketsock.setControlSock(ss);sock.initStreams();}

?这段代码所做的工作类似于前面提到的sslNegotiation方法

?

?

?

public class MySSLSocket implements StreamSocket {private SSLSocket sslSocket;protected String remoteHostname;private MySSLSocket(SSLSocket sslSocket) {this.sslSocket = sslSocket;}public static MySSLSocket createSSLSocket(Socket socket, InetAddress host,int port, int timeout) throws IOException {SSLSocket sock = getSSLSocket(socket, host.getHostName(), port);if (null == sock) {throw new IOException("null SSLSocket");}// 注意: 已连接, 不用再次连接// sock.connect(addr, timeout);return new MySSLSocket(sock);}private static SSLSocket getSSLSocket(Socket socket, String host, int port) {long now = System.currentTimeMillis();try {SSLContext sslContext = SSLContext.getInstance("SSL");sslContext.init(null,new TrustManager[] { new MyX509TrustManager() }, null);SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, false);sslSocket.setEnableSessionCreation(true);sslSocket.setUseClientMode(true);System.out.println(System.currentTimeMillis() - now);now = System.currentTimeMillis();sslSocket.startHandshake();System.out.println("shakehand takes"+ (System.currentTimeMillis() - now));return sslSocket;} catch (KeyManagementException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}

?

?这段代码主要是通过一个已经连接到FTP服务器的Socket(控制连接)来创建一个SSLSocket,之后FTPSClient使用这个SSLSocket与FTP服务器交互。

?

?

public static void main(String[] args) throws FTPException, IOException {// 使用自己添加的构造函数, 获取一个支持FTPS的 FileTransferClientFileTransferClient client = new FileTransferClient(FTPType.FTPS);client.setRemoteHost("10.204.80.48");client.setRemotePort(21);client.setUserName("tom");client.setPassword("123456");// 二进制传输模式client.setContentType(FTPTransferType.BINARY);// 控制信道上的字符集为utf-8client.getAdvancedSettings().setControlEncoding("utf-8");// 不要自动登录client.getAdvancedSettings().setAutoLogin(false);client.getAdvancedFTPSettings().setConnectMode(FTPConnectMode.PASV);try {client.connect();client.manualLogin();// 遍历当前目录for (String string : client.directoryNameList()) {System.out.println(string);}client.changeDirectory("data");// 遍历data目录for (String string : client.directoryNameList()) {System.out.println(string);}client.uploadFile("c:\\a.txt", "cm.txt");System.out.println("upload!!");} catch (IOException e) {e.printStackTrace();}}

?

?简单验证了下FTPSClient的功能, 可以登录、切换目录、显示目录列表、上传文件。用Wireshark抓包看了下, 控制连接和数据连接上数据果然都加密了。

?

Java SSLSocket的施用之二-让edtFTPj支持FTPS

3. 遗留的问题

?

(1) 登录很慢。 对比了下, 用commons-net包里面的FTPSClient,登录非常快。?

(2) 数据连接也加密了吗? 还需要再看看代码

?

?

热点排行