Java Client using FTP over SSL (Explicit)


FTPS extends the FTP protocol with support for SSL and TLS.

Explicit security requires that the FTP client sends an explicit command (AUTH SSL or AUTH TLS) to the FTP server to initiate a secure control connection. The default FTP server port is used. The data connection can also be secured using the commands PBSZ and PROT.

The example code listed below uses Jakarta Commons Net and secures the control connection and data connection using TLS.



import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;

public class FTPSTest {

  public void putFile(String host,
                      int port,
                      String username,
                      String password,
                      String localFilename,
                      String remoteFilename) {
    try {
      FTPSClient ftpClient = new FTPSClient(false);
      // Connect to host
      ftpClient.connect(host, port);
      int reply = ftpClient.getReplyCode();
      if (FTPReply.isPositiveCompletion(reply)) {

        // Login
        if (ftpClient.login(username, password)) {

          // Set protection buffer size
          ftpClient.execPBSZ(0);
          // Set data channel protection to private
          ftpClient.execPROT("P");
          // Enter local passive mode
          ftpClient.enterLocalPassiveMode();

          // Store file on host
	  InputStream is = new FileInputStream(localFilename);
	  if (ftpClient.storeFile(remoteFilename, is)) {
	    is.close();
	  } else {
	    System.out.println("Could not store file");
	  }
	  // Logout
	  ftpClient.logout();

        } else {
          System.out.println("FTP login failed");
        }

        // Disconnect
    	ftpClient.disconnect();

      } else {
        System.out.println("FTP connect to host failed");
      }
    } catch (IOException ioe) {
      System.out.println("FTP client received network error");
    } catch (NoSuchAlgorithmException nsae) {
      System.out.println("FTP client could not use SSL algorithm");
    }
  }
}



The resulting FTP server log


CONNECT [     0] - Incoming connection request
CONNECT [     0] - FTP Connection request accepted
COMMAND [     0] - AUTH TLS
  REPLY [     0] - 234 Authentication method accepted

CONNECT [     0] - SSL connection using TLSv1/SSLv3 (RC4-MD5)
CONNECT [     0] - SSL connection established
COMMAND [     0] - USER test
  REPLY [     0] - 331 User test, password please

COMMAND [     0] - PASS ***********
CONNECT [     0] - Native user 'test' authenticated
  REPLY [     0] - 230 Password Ok, User logged in

COMMAND [     0] - PBSZ 0
  REPLY [     0] - 200 PBSZ=0

COMMAND [     0] - PROT P
  REPLY [     0] - 200 PROT P OK, data channel will be secured

COMMAND [     0] - PASV
  REPLY [     0] - 227 Entering Passive Mode (127,0,0,1,43,41)

COMMAND [     0] - STOR test.txt
  REPLY [     0] - 150 Opening data connection

CONNECT [     0] - SSL connection using TLSv1/SSLv3 (RC4-MD5)
CONNECT [     0] - SSL data connection established
 SYSTEM [     0] - Successfully stored file at 'c:\ftp\test.txt'
  REPLY [     0] - 226 Transfer complete

COMMAND [     0] - QUIT
CONNECT [     0] - Connection terminated

 

 

package examples.ftp; 

 import java.io.FileInputStream; 
 import java.io.FileOutputStream; 
 import java.io.IOException; 
 import java.io.InputStream; 
 import java.io.OutputStream; 
 import java.io.PrintWriter; 
 import java.security.NoSuchAlgorithmException; 

 import javax.net.SocketFactory; 
 import javax.net.ssl.SSLSocketFactory; 

 import org.apache.commons.net.PrintCommandListener; 
 import org.apache.commons.net.ftp.FTPClient; 
 import org.apache.commons.net.ftp.FTPConnectionClosedException; 
 import org.apache.commons.net.ftp.FTPFile; 
 import org.apache.commons.net.ftp.FTPReply; 
 import org.apache.commons.net.ftp.FTPSClient; 

 public final class FTPSExample { 
 public static final String USAGE = "Usage: ftp [-s] [-b] <127.0.0.1> <shanming> <shanming> <test.txt> <shanming.txt>\n" 
 + "\nDefault behavior is to download a file and use ASCII transfer mode.\n" 
 + "\t-s store file on server (upload)\n" 
 + "\t-b use binary transfer mode\n"; 

 public static final void main(String[] args) 
 throws NoSuchAlgorithmException { 
 int base = 0; 
 boolean storeFile = false, binaryTransfer = false, error = false; 
 String server, username, password, remote, local; 
 String protocol = "SSL"; // SSL/TLS  
 FTPClient ftps; 
 for (base = 0; base < args.length; base++) { 
 if (args[base].startsWith("-s")) 
 storeFile = true; 
 else if (args[base].startsWith("-b")) 
 binaryTransfer = true; 
 else 
 break; 
 } 
 if ((args.length - base) != 5) { 
 System.err.println(USAGE); 
 System.exit(1); 
 } 
 server = args[base++]; 
 username = args[base++]; 
 password = args[base++]; 
 remote = args[base++]; 
 local = args[base]; 
 ftps = new FTPSClient(protocol); 
 ftps.enterLocalPassiveMode(); 
 ftps.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out))); 
 try { 
 int reply; 
 ftps.connect(server); 
 System.out.println("Connected to " + server + "."); 
 // After connection attempt, you should check the reply code to verify  
 // success.  
 reply = ftps.getReplyCode(); 
 if (!FTPReply.isPositiveCompletion(reply)) { 
 ftps.disconnect(); 
 System.err.println("FTP server refused connection."); 
 System.exit(1); 
 } 
 } catch (IOException e) { 
 if (ftps.isConnected()) { 
 try { 
 ftps.disconnect(); 
 } catch (IOException f) { 
 // do nothing  
 } 
 } 
 System.err.println("Could not connect to server."); 
 e.printStackTrace(); 
 System.exit(1); 
 } 
 __main: try { 
 ftps.setBufferSize(1000); 
 if (!ftps.login(username, password,"990")) { 
 ftps.logout(); 
 error = true; 
 break __main; 
 } 

 System.out.println("Remote system is: " + ftps.getSystemName()); 
 System.out.println("Working directory is: " + ftps.printWorkingDirectory()); 
 System.out.println("default port is: " + ftps.getDefaultPort()); 
 //if (binaryTransfer) 
 //ftps.setFileType(FTP.BINARY_FILE_TYPE); 
 // Use passive mode as default because most of us are  
 // behind firewalls these days.  
 SocketFactory factory = SSLSocketFactory.getDefault(); 
 ftps.setSocketFactory(factory); 
 FTPFile[] files=ftps.listFiles(); 
 for(FTPFile file:files){ 
 System.out.println("ftpfile name is :" + file.getName()); 
 } 

 if (storeFile) { 
 System.out.println("Here is upload!"); 
 ftps.changeWorkingDirectory("/"); 
 InputStream input; 
 input = new FileInputStream(local); 
 ftps.storeFile(remote, input); 
 input.close(); 
 } else { 
 System.out.println("Here is download!"); 
 ftps.changeWorkingDirectory("/"); 
 OutputStream output; 
 output = new FileOutputStream(local); 
 ftps.retrieveFile(remote, output); 
 output.close(); 
 } 
 ftps.logout(); 
 } catch (FTPConnectionClosedException e) 
 { 
 error = true; 
 System.err.println("Server closed connection."); 
 e.printStackTrace(); 
 } catch (IOException e) { 
 error = true; 
 e.printStackTrace(); 
 } finally { 
 if (ftps.isConnected()) { 
 try { 
 ftps.disconnect(); 
 } catch (IOException f) 

 { 
 // do nothing  
 } 
 } 
 } 
 System.exit(error ? 1 : 0); 
 } // end main  
 } 
 ============================== 
 输出结果: 
 220-FileZilla Server version 0.9.39 beta 
 220-written by Tim Kosse (Tim.Kosse@gmx.de) 
 220 Please visit http://sourceforge.net/projects/filezilla/ 
 AUTH TLS 
 234 Using authentication type TLS 
 Connected to 127.0.0.1. 
 USER shanming 
 331 Password required for shanming 
 PASS shanming 
 230 Logged on 
 SYST 
 215 UNIX emulated by FileZilla 
 Remote system is: UNIX emulated by FileZilla 
 PWD 
 257 "/" is current directory. 
 Working directory is: / 
 default port is: 21 
 PORT 127,0,0,1,9,238 
 200 Port command successful 
LIST 
 521 PROT P required 
 Here is download! 
 CWD / 
 250 CWD successful. "/" is current directory. 
 PORT 127,0,0,1,9,239 
 200 Port command successful 
 RETR ss.txt 
 521 PROT P required 
 QUIT 
 221 Goodbye


本例中我们以Apache FTPServer为例进行发布FTPS, 也以Apache FTPClient为例进行客户端连接FTPS。

 

首先我们启动FTPServer

 


[java] view plain copy print ?

1. package
2. import
3. import
4. import
5. import
6. import
7. import
8. import
9. public class
10. {  
11. /**
12.      * 通过程序启动FTP with SSL认证,以Apache FTPServer为例
13.      * @param args
14.      * @throws FtpException 
15.      */
16. public static void main(String[] args) throws
17.     {  
18. // TODO Auto-generated method stub 
19. new
20.           
21. new
22.                   
23. // set the port of the listener 
24. 2221);  
25. // define SSL configuration 
26. new
27. new File("F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/kserver.keystore"));  
28. "123456");  
29.           
30. //      ssl.setTruststoreFile(new File("F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/tserver.keystore")); 
31. //      ssl.setKeystorePassword("123456"); 
32.   
33. // set the SSL configuration for the listener 
34.         factory.setSslConfiguration(ssl.createSslConfiguration());  
35. true);  
36.           
37.           
38. // replace the default listener 
39. "default", factory.createListener());  
40.                   
41. new
42. new File("F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/conf/users.properties"));  
43.                   
44.         serverFactory.setUserManager(userManagerFactory.createUserManager());  
45.                   
46. // start the server 
47.         FtpServer server = serverFactory.createServer();   
48.                   
49.         server.start();  
50.     }  
51. }  
 
package examples.ftpServer;
import java.io.File;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.ssl.SslConfigurationFactory;
import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
public class StartFTPS
{
	/**
	 * 通过程序启动FTP with SSL认证,以Apache FTPServer为例
	 * @param args
	 * @throws FtpException 
	 */
	public static void main(String[] args) throws FtpException
	{
		// TODO Auto-generated method stub
		FtpServerFactory serverFactory = new FtpServerFactory();
        
		ListenerFactory factory = new ListenerFactory();
		        
		// set the port of the listener
		factory.setPort(2221);
		// define SSL configuration
		SslConfigurationFactory ssl = new SslConfigurationFactory();
		ssl.setKeystoreFile(new File("F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/kserver.keystore"));
		ssl.setKeystorePassword("123456");
		
//		ssl.setTruststoreFile(new File("F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/tserver.keystore"));
//		ssl.setKeystorePassword("123456");

		// set the SSL configuration for the listener
		factory.setSslConfiguration(ssl.createSslConfiguration());
		factory.setImplicitSsl(true);
		
		
		// replace the default listener
		serverFactory.addListener("default", factory.createListener());
		        
		PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
		userManagerFactory.setFile(new File("F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/conf/users.properties"));
		        
		serverFactory.setUserManager(userManagerFactory.createUserManager());
		        
		// start the server
		FtpServer server = serverFactory.createServer(); 
		        
		server.start();
	}
}

 
 
然后是客户端连接FTPS
 
 
   [c-sharp] 
    view plain 
   copy 
   print 
   ? 
  
 
 
1. package examples.ftpClient;  
2. import java.io.FileInputStream;  
3. import java.io.IOException;  
4. import java.io.PrintWriter;  
5. import java.security.KeyStore;  
6. import javax.net.ssl.KeyManager;  
7. import javax.net.ssl.KeyManagerFactory;  
8. import javax.net.ssl.TrustManager;  
9. import javax.net.ssl.TrustManagerFactory;  
10. import org.apache.commons.net.PrintCommandListener;  
11. import org.apache.commons.net.ftp.FTPSClient;  
12. public class
13. {  
14. private static
15. private static final String trust_path = "F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/kclient.keystore";  
16. private static final String trust_pw = "123456";  
17. private static final String key_path = "F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/tclient.keystore";  
18. private static final String key_pw = "123456";  
19. private static final String serverIP = "127.0.0.1";  
20. private static final int
21. private static final int
22. private static final int
23. private static final int
24. /**
25.      * 测试连接FTP With SSL,以Apache FTPServer为例
26.      * @param args
27.      * @throws Exception
28.      */
29. public static void
30.     {  
31. if (!connect("active"))  
32.         {  
33. "passive");  
34.         }  
35. new
36. out.println("storeFile: " + ftpsClient.storeFile("test_file", fs));  
37.         fs.close();  
38.         ftpsClient.disconnect();  
39.     }  
40. /**
41.       * 登陆FTP
42.       * @param active
43.       * @return
44.       * @throws Exception
45.       */
46. private static
47.     {  
48. new FTPSClient(true);  
49. new PrintCommandListener(new PrintWriter(System.out)));  
50.         ftpsClient.setKeyManager(getKeyManager());  
51. //ftpsClient.setTrustManager(getTrustManager()); 
52.         ftpsClient.setDefaultTimeout(defaultTimeout);  
53.         ftpsClient.connect(serverIP, serverPort);  
54. out.println("已经连接FTP");  
55.         ftpsClient.setSoTimeout(soTimeout);  
56.         ftpsClient.getReplyCode();  
57.         ftpsClient.execPBSZ(0);  
58. "P");  
59. "admin", "admin");  
60. "/");  
61.         ftpsClient.setDataTimeout(dataTimeout);  
62. if (active.equalsIgnoreCase("active"))  
63.         {  
64.             ftpsClient.enterLocalActiveMode();  
65. else
66.         {  
67.             ftpsClient.enterLocalPassiveMode();  
68.         }  
69.           
70. out.println("已经登陆FTP");  
71. return
72.     }  
73. /**
74.      * 遍历FTP文件
75.      * @return
76.      */
77. private static
78.     {  
79. long
80. try
81.         {  
82. out.println("List file length:"
83. catch
84.         {  
85. out.println(e.getMessage());  
86. long
87. long
88. out.println("t: "
89. try
90.             {  
91.                 ftpsClient.disconnect();  
92. catch
93.             {  
94.                 e1.printStackTrace();  
95.             }  
96. return false;  
97.         }  
98. return true;  
99.     }  
100. private static
101.     {  
102. "JKS");  
103. new
104.         KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());  
105.         kmf.init(key_ks, key_pw.toCharArray());  
106.         KeyManager[] km = kmf.getKeyManagers();  
107. out.println("km len: "
108. return
109.     }  
110. private static
111.     {  
112. "JKS");  
113. new
114.         TrustManagerFactory tf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());  
115.         tf.init(trust_ks);  
116.         TrustManager[] tm = tf.getTrustManagers();  
117. out.println("tm len: "
118. return
119.     }  
120. }

package examples.ftpClient;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.KeyStore;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTPSClient;
public class ConnectFTPS
{
	private static FTPSClient ftpsClient;
	private static final String trust_path = "F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/kclient.keystore";
	private static final String trust_pw = "123456";
	private static final String key_path = "F:/FTP/ftpserver-1.0.5/apache-ftpserver-1.0.5/res/tclient.keystore";
	private static final String key_pw = "123456";
	private static final String serverIP = "127.0.0.1";
	private static final int serverPort = 2221;
	private static final int defaultTimeout = 10000;
	private static final int soTimeout = 900000;
	private static final int dataTimeout = 5000;
	/**
	 * 测试连接FTP With SSL,以Apache FTPServer为例
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception
	{
		if (!connect("active"))
		{
			connect("passive");
		}
		FileInputStream fs = new FileInputStream(trust_path);
		System.out.println("storeFile: " + ftpsClient.storeFile("test_file", fs));
		fs.close();
		ftpsClient.disconnect();
	}
	 /**
	  * 登陆FTP
	  * @param active
	  * @return
	  * @throws Exception
	  */
	private static boolean connect(String active) throws Exception
	{
		ftpsClient = new FTPSClient(true);
		ftpsClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
		ftpsClient.setKeyManager(getKeyManager());
		//ftpsClient.setTrustManager(getTrustManager());
		ftpsClient.setDefaultTimeout(defaultTimeout);
		ftpsClient.connect(serverIP, serverPort);
		System.out.println("已经连接FTP");
		ftpsClient.setSoTimeout(soTimeout);
		ftpsClient.getReplyCode();
		ftpsClient.execPBSZ(0);
		ftpsClient.execPROT("P");
		ftpsClient.login("admin", "admin");
		ftpsClient.changeWorkingDirectory("/");
		ftpsClient.setDataTimeout(dataTimeout);
		if (active.equalsIgnoreCase("active"))
		{
			ftpsClient.enterLocalActiveMode();
		} else
		{
			ftpsClient.enterLocalPassiveMode();
		}
		
		System.out.println("已经登陆FTP");
		return testLink();
	}
	/**
	 * 遍历FTP文件
	 * @return
	 */
	private static boolean testLink()
	{
		long t1 = System.currentTimeMillis();
		try
		{
			System.out.println("List file length:" + ftpsClient.listFiles().length);
		} catch (IOException e)
		{
			System.out.println(e.getMessage());
			long t2 = System.currentTimeMillis();
			long t = (t2 - t1) / 1000;
			System.out.println("t: " + t);
			try
			{
				ftpsClient.disconnect();
			} catch (IOException e1)
			{
				e1.printStackTrace();
			}
			return false;
		}
		return true;
	}
	private static KeyManager getKeyManager() throws Exception
	{
		KeyStore key_ks = KeyStore.getInstance("JKS");
		key_ks.load(new FileInputStream(key_path), key_pw.toCharArray());
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
		kmf.init(key_ks, key_pw.toCharArray());
		KeyManager[] km = kmf.getKeyManagers();
		System.out.println("km len: " + km.length);
		return km[0];
	}
	private static TrustManager getTrustManager() throws Exception
	{
		KeyStore trust_ks = KeyStore.getInstance("JKS");
		trust_ks.load(new FileInputStream(trust_path), trust_pw.toCharArray());
		TrustManagerFactory tf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		tf.init(trust_ks);
		TrustManager[] tm = tf.getTrustManagers();
		System.out.println("tm len: " + tm.length);
		return tm[0];
	}
}

 

以上实例是经过验证,如果对于其中有些概念性问题或者SSL证书的问题,可以参考先前的文章。