Search code examples
javacertificatesslhandshakeexceptionjkshttpsurlconnection

SSLHandshakeException while connecting to HTTPS site


I'm creating in Java HttpsURLConnection. I downloaded certificates from website and created file truststore.jks with this certs. My application is getting certs from truststore.jks and connecting to website. And it works... on my PC. But after deploy application on server I got this ugly exception:

Cause: 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

Stack trace:
[sun.security.ssl.Alerts.getSSLException(Unknown Source)
 sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
 sun.security.ssl.Handshaker.fatalSE(Unknown Source)
 sun.security.ssl.Handshaker.fatalSE(Unknown Source)
 sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
 sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
 sun.security.ssl.Handshaker.processLoop(Unknown Source)
 sun.security.ssl.Handshaker.process_record(Unknown Source)
 sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
 sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
 sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
 sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
 sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
 sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
 sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
 sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
 sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown Source)

I'm creating HttpsURLConnection in ConnectionFactory class, and running connection.getInputStream() method.

ConnectionFactory.java:

public final class ConnectionFactory {

    public HttpsURLConnection getHttpsURLConnection(URL url, String trustStorePath, String trustStorePassword)
            throws FileTransferWorkerException {
        KeyStore keyStore = loadKeyStore(trustStorePath, trustStorePassword);
        TrustManagerFactory trustManagerFactory = initTrustManagerFactory(keyStore);
        SSLSocketFactory sslSocketFactory = buildSSLSocketFactory(trustManagerFactory);
        return buildConnection(url, sslSocketFactory);
    }

    private KeyStore loadKeyStore(String path, String password) throws FileTransferWorkerException {
        KeyStore keystore;
        try {
            keystore = KeyStore.getInstance("JKS");
        } catch (KeyStoreException e) {
            throw new FileTransferWorkerException(e);
        }

        try (FileInputStream fileInputStream = new FileInputStream(path)) {
            keystore.load(fileInputStream, password.toCharArray());
        } catch (IOException | CertificateException | NoSuchAlgorithmException e) {
            throw new FileTransferWorkerException("Can not load keyStore from " + path, e);
        }

        return keystore;
    }

    private TrustManagerFactory initTrustManagerFactory(KeyStore keyStore) throws FileTransferWorkerException {
        TrustManagerFactory trustManagerFactory;

        try {
            trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        } catch (NoSuchAlgorithmException e) {
            throw new FileTransferWorkerException(e);
        }

        try {
            trustManagerFactory.init(keyStore);
        } catch (KeyStoreException e) {
            throw new FileTransferWorkerException(e);
        }

        return trustManagerFactory;
    }

    private SSLSocketFactory buildSSLSocketFactory(TrustManagerFactory trustManagerFactory) throws FileTransferWorkerException {
        SSLContext sslContext;
        try {
            sslContext = SSLContext.getInstance("TLS");
        } catch (NoSuchAlgorithmException e) {
            throw new FileTransferWorkerException(e);
        }

        try {
            sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
        } catch (KeyManagementException e) {
            throw new FileTransferWorkerException(e);
        }

        return sslContext.getSocketFactory();
    }

    private HttpsURLConnection buildConnection(URL url, SSLSocketFactory sslSocketFactory) throws FileTransferWorkerException {
        HttpsURLConnection connection;

        try {
            connection = (HttpsURLConnection) url.openConnection();
        } catch (IOException e) {
            throw new FileTransferWorkerException("Can not connect to " + url.getPath(), e);
        }

        connection.setSSLSocketFactory(sslSocketFactory);
        return connection;
    }
}

and invoke method:

 private void download(URL url, String trustStorePath, String trustStorePassword, File file)
            throws IOException, FileTransferWorkerException {
        HttpsURLConnection connection = new ConnectionFactory().getHttpsURLConnection(url, trustStorePath, trustStorePassword);
        try (ReadableByteChannel reader = Channels.newChannel(connection.getInputStream()){
            ...
        } finally {
            connection.disconnect();
        }
    }

I need to use my truststor.jks file, not cacerts. Do you have any ideas where I made a mistake? Help.


Solution

  • I figured it. Locally I'm connected to my company's network and I have their certificates (because of proxy). But server isn't using proxy and should have real certificates from endpoint server.