First off, I know there is a similar question here but it doesn't answer my doubts. I have a FTPS server (vsftpd) configured with SSL.
I've generated the appropriate keys with:
openssl req -x509 -nodes -days 1825 -newkey rsa:2048 \
-keyout private/vsftpd.key \
-out certs/vsftpd.crt
And configured the respective conf file in the server.
Now, in the Java client code with Apache commons net, I'm able to communicate with the server over SSL with something like this:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;
public class CommonsNetFTPSTest {
public static void main(String[] args) throws Exception {
System.setProperty("javax.net.debug", "ssl");
String server = "XXX.XXX.XXX.XXX";
String username = "USER_TEST";
String password = "ABCD1234";
String remoteFile = "/Data/Input/PH240819";
String localFile = "PH240819";
String protocol = "SSL"; // TLS / null (SSL)
int port = 990;
int timeoutInMillis = 10000;
boolean isImpicit = true;
FTPSClient client = new FTPSClient(protocol, isImpicit);
client.setDataTimeout(timeoutInMillis);
client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
System.out.println("################ Connecting to Server ################################");
try
{
int reply;
System.out.println("################ Connect Call ################################");
client.connect(server, port);
client.login(username, password);
System.out.println("################ Login Success ################################");
//client.setFileType(FTP.BINARY_FILE_TYPE);
client.setFileType(FTP.NON_PRINT_TEXT_FORMAT);
client.execPBSZ(0); // Set protection buffer size
client.execPROT("P"); // Set data channel protection to private
client.enterLocalPassiveMode();
System.out.println("Connected to " + server + ".");
reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
client.disconnect();
System.err.println("FTP server refused connection.");
System.exit(1);
}
client.listFiles();
boolean retrieved = client.retrieveFile(remoteFile, new FileOutputStream(localFile));
}
catch (Exception e)
{
if (client.isConnected())
{
try
{
client.disconnect();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
System.err.println("Could not connect to server.");
e.printStackTrace();
return;
}
finally
{
//client.disconnect();
client.logout();
System.out.println("# client disconnected");
}
}
}
But in that way my client is accepting any certificate, and I guess it's possible a man-in-the-middle attack. So I'd like to tell my client what certificates to accept, and only connect when the certificate is valid.
Now, I have both files generated previously: vsftpd.key and vsftpd.crt, and I've imported the crt file in a local keystore like this:
keytool -import -alias alias -file vsftps.crt -keypass keypass -keystore mykeystore.jks -storepass Hello1
My question is, How do I tell my client that use the certificate from mykeystore? what else am I missing?. Thanks
Ok, now I have it.
I was doing it wrong from the beginning. To start with, you need to convert the two files (vsftpd.crt and vsftpd.key) into a single PKCS12 file.
Next, you need to import the PKCS12 file into a keystore:
Detailed instructions [here].2
Finally, you just need to instantiate a trust manager with the generated keystore, and hand it to the FTPSClient. Something like:
you have to generate your own keystore from previous comment.
Now use this link https://issues.apache.org/jira/browse/NET-326 Find this comment (Bogdan Drozdowski added a comment - 10/Mar/11 15:16) and do FTPSCLient(SSLContext sslContext) constructor like in this comment, and your ftpsClient will work with certificate and private key auth.