I'm trying to connect to a couple of Linux FTPS Servers (One is Red Hat 5.11 and the other is Red Hat 6.8), and download files from the Linux FTPS Server to my local Windows Server using org.apache.commons.net.ftp.FTPSClient.

I'm trying to connect using Apache Commons API in 2 different ways.

1. Using the first way I can connect to both Linux hosts and successfully download a test file testftps.txt from both the remote Linux servers to my Local Windows server. My connection method for the first one is connectToFtp() as shown in the below code. It uses a certificate.jks that is located in a path that is defined in my Eclipse Java Project Build Path. The file has an encryption password that is used for protecting the private key in the JKS and account id is also associated with the JKS.

I have created /tmp/testftps.txt files in both Linux hosts hostname1 and hostname2 . I just changed the host variable to point to either hostname1 or hostname2, and it successfully downloads the file testftps.txt from the corresponding host to my local Windows server using the downloadFiles() Function code shown below.



private void connectToFtp() throws Exception {
    try {
        ftpClient = new FTPSClient("SSL");
        String host = "hostname1";
        String user = "accountid";
        String password = "";
        String jksFileName = "certificate.jks";
        String jksFilePassword = "encryptionpassword";
 
        File jksFile = new File(ClassLoader.getSystemResource(jksFileName).toURI());
        String jksFilename = jksFile.getName();
        ftpClient.setKeyManager(KeyManagerUtils.createClientKeyManager(jksFile, jksFilePassword));
        ftpClient.connect(host, 2121);
        ftpClient.enterLocalPassiveMode();
        ftpClient.login(user, password);
        ftpClient.execPBSZ(0);
        ftpClient.execPROT("P");
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    } catch(Exception e) {
        System.out.println("Failed to connect: Exception=" + e);
        throw e;
    }
}
 
public void downloadFiles() throws Exception {
    try {
        String specifiedRemoteFileName = "testftps.txt";
        File localFile = new File(localFilePath + "/" + specifiedRemoteFileName);
        OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(localFile));
        String remoteFile = remoteFilePath + "/" + specifiedRemoteFileName;
        boolean downloaded = ftpClient.retrieveFile(remoteFile, outputStream);
        if (downloaded) {
            System.out.println("File has been downloaded successfully to local destination " + localFile.getAbsolutePath());
        } else {
            System.out.println("File " + remoteFile + " was not downloaded from remote server.");
        }
        outputStream.close();
    }
    catch(Exception e) {
        System.out.println("Error during download...");
        throw e;
    }
}

2. For the second program I'm using the same certificate.jks , accountid and encryption password, the same Apache Commons API and I'm trying to fetch same testftps.txt from hostname1 and hostname2 to my local Windows VM. For the second method my connection mechanism connectToFtpUsing_TrustStoreKeystore() is slightly different as shown below. Though it uses the same certificate.jks, it gets Truststore , Keystore , uses keyManagetFactory, TrustManagerFactory SSLContext, etc. I'm still using org.apache.commons.net.ftp.FTPSClient object, however with this method the connection works fine for remote Linux FTPS server hostname1 and I'm able to download fine, but for remote Linux FTPS server hostname2 and a few other hosts the program gives Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found as shown below.

I'd appreciate if you could look at the connection method below and comparing it with the previous method, let me know why the above method connectToFtp() is successfully downloading files from all Linux FTPS Server whereas below connectToFtpUsing_TrustStoreKeystore() connects to only a few FTPS Servers successfully and fails to connect to many servers with the No trusted certificate found ERROR even though both programs use the same CERTIFICATE.JKS file and the same Apache Commons FTPSClient API. How can this be fixed?


private void connectToFtpUsing_TrustStoreKeystore() throws Exception {
try {
        ftpClient = new FTPSClient("TLS", Boolean.FALSE);
        KeyStore keyStore = getKeyStore();
        KeyStore trustStore = getTrustStore(keyStore);
        keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keyStore, "encryptionpassword".toCharArray());
        trustManagerFactory.init(trustStore);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
        FTPSSocketFactory socketFactory = getSocketFactory(sslContext);
        ftpClient.setRemoteVerificationEnabled(false);
        ftpClient.setSocketFactory(socketFactory);
        ftpClient.setNeedClientAuth(false);
        ftpClient.setBufferSize(0);
        ftpClient.setControlEncoding("UTF-8");
        ftpClient.setKeyManager(keyManagerFactory.getKeyManagers()[0]);
        ftpClient.setTrustManager(trustManagerFactory.getTrustManagers()[0]);
        ftpClient.connect("hostname", Integer.parseInt("2121"));
        ftpClient.execPBSZ(0);
        ftpClient.execPROT("P");
        ftpClient.login("accountid", "encryptionpassword");
        ftpClient.enterLocalPassiveMode();
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    } catch(Exception e) {
        System.out.println("Failed to connect using Trustore keystore" + e);
        throw e;
    }
}
 
private static KeyStore getKeyStore() throws Exception {
    KeyStore keyStore = KeyStore.getInstance("JKS");
    InputStream inputStream = new FileInputStream(new File("windows path of certificate.jks"));
    keyStore.load(inputStream, "encryptionpassword".toCharArray());
    return keyStore;
}
 
private static KeyStore getTrustStore(KeyStore keyStore) throws Exception {
    KeyStore trustStore = KeyStore.getInstance("JKS");
    trustStore.load(null);
    for (Enumeration < String > t = keyStore.aliases(); t.hasMoreElements();) {
        String alias = t.nextElement();
        if (keyStore.isKeyEntry(alias)) {
            Certificate[] keyStore_certificate = keyStore.getCertificateChain(alias);
            X509Certificate x5091 = (X509Certificate) keyStore_certificate[1];
            trustStore.setCertificateEntry(x5091.getSubjectDN().toString(), x5091);
        }
    }
    return trustStore;
}
 
private static FTPSSocketFactory getSocketFactory(SSLContext sslContext) throws Exception {
    FTPSSocketFactory socketFactory = new FTPSSocketFactory(sslContext) {
        public Socket createSocket() throws IOException {
            return new Socket();
        }
    };
    return socketFactory;
}

Stack trace of the error I'm getting with the second method:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1649)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)Failed to connect using Trustore keystorejavax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
 
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:893)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
    at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:259)
    at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:205)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:169)
    at org.apache.commons.net.SocketClient.connect(SocketClient.java:189)
    at ftps.apache.FTPSUsingApacheComm.connectToFtpUsing_TrustStoreKeystore(FTPSUsingApacheComm.java:160)
    at ftps.apache.FTPSUsingApacheComm.<init>(FTPSUsingApacheComm.java:132)
    at ftps.apache.FTPSUsingApacheCommMain.main(FTPSUsingApacheCommMain.java:27)
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
    at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:330)
    at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:110)
    at sun.security.validator.Validator.validate(Validator.java:218)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)
    ... 14 more


Appreciate your HELP GREATLY with above issue.