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.
openssl pkcs12 -export -in vsftpd.crt -inkey vsftpd.key > vsftpd.p12
Next, you need to import the PKCS12 file into a keystore:
keytool -importkeystore -srckeystore vsftpd.p12 -destkeystore keystore.jks -srcstoretype pkcs12
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:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.io.Util;
import org.apache.commons.net.util.TrustManagerUtils;
public method() throws IOException, GeneralSecurityException{
File storeFile = new File("path/to/keystore");
KeyStore keyStore = loadStore("JKS", storeFile, "password");
X509TrustManager defaultTrustManager = TrustManagerUtils.getDefaultTrustManager(keyStore);
client = new FTPSClient(properties.getProtocol(), isImpicit);
client.setTrustManager(defaultTrustManager);
logOutput = new LogOutputStream(log, Level.INFO);
}
//Helper method from apache: http://commons.apache.org/proper/commons-net/apidocs/index.html?org/apache/commons/net/util/KeyManagerUtils.html
private KeyStore loadStore(String storeType, File storePath, String storePass)
throws KeyStoreException, IOException, GeneralSecurityException {
KeyStore ks = KeyStore.getInstance(storeType);
FileInputStream stream = null;
try {
stream = new FileInputStream(storePath);
ks.load(stream, storePass.toCharArray());
} finally {
Util.closeQuietly(stream);
}
return ks;
}