Task: Connect to a CentOS box using FTP4J FTP using a self signed certificate.
I am able to successfully connect to the CentOS box over FTP using
org.apache.commons.net.ftp.FTPSClient
however I receive the following error, which I know to be from the self signed certificate, using FTP4J.
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
A related question here on SO asked a similar question, just with a Jersey Client. The answer there (cleaned up below, as the accepted answer yields a compiler error) does not work.
Here is the FTP4J code with the trust all certs code, which does not work.
import java.io.IOException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import it.sauronsoftware.ftp4j.FTPClient;
import it.sauronsoftware.ftp4j.FTPDataTransferListener;
import it.sauronsoftware.ftp4j.FTPException;
import it.sauronsoftware.ftp4j.FTPIllegalReplyException;
FTPClient ftpsClient = new FTPClient();
try
{
// Initialize the transfer information.
this.oFtpArgs.oFtp.transferCount = this.oFtpArgs.pathFiles.size();
this.oFtpArgs.oFtp.transferCompleted = 0;
this.oFtpArgs.oFtp.filePercentComplete = 0L;
this.oFtpArgs.oFtp.bytesTransferredTotal = 0L;
// Connect to the server using active mode enabling FTP over explicit TLS/SSL (FTPES).
ftpsClient.setSecurity(FTPClient.SECURITY_FTPES);
ftpsClient.setPassive(false);
ftpsClient.connect(this.oFtpArgs.serverIp, port);
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager()
{
@Override
public X509Certificate[] getAcceptedIssuers(){return null;}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType){}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType){}
}};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Log into the server.
ftpsClient.login(user, pass);
}
catch (IOException ex)
{
System.out.println("Error: " + ex.getMessage());
ex.printStackTrace();
}
catch (Exception ex)
{
System.out.println("Error: " + ex.getMessage());
ex.printStackTrace();
}
The Apache FTPSClient code connects flawlessly, just has trouble doing uploads, hence the FTP4J code now.
import java.io.IOException;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;
FTPSClient ftpsClient = new FTPSClient("TLS", false);
try
{
FTPClientConfig ftpClientConfig = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
ftpsClient.configure(ftpClientConfig);
ftpsClient.connect(this.oFtpArgs.serverIp, port);
int ftpReplyCode = ftpsClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(ftpReplyCode))
return;
// Set protection buffer size
ftpsClient.execPBSZ(0);
// Set data channel protection to private
ftpsClient.execPROT("P");
// Log into the server.
ftpsClient.login(user, pass);
ftpReplyCode = ftpsClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(ftpReplyCode))
return;
// Enter local active mode .
ftpsClient.enterLocalActiveMode();
}
catch (IOException ex)
{
System.out.println("Error: " + ex.getMessage());
ex.printStackTrace();
}
I did peruse other questions, but could not find something that worked. Any thoughts on how to tell FTP4J to ignore errors on self-signed certificates?
You are so close. First of all, get rid of the reference to
HttpsURLConnection. Has absolutely nothing to do with FTP or FTPS.But keep the
TrustManagerandSSLContextstuff, with just one minor change.... direct yourFTPClientto use the resultantSSLSocketFactory.So your final result looks something like this:
My source? The FTP4J manual. (I do note that that example there uses "SSL" instead of "TLS"; you may have to try it both ways.)