2

When I try to programmatically download a file using Java code, I'm getting an exception:

Exception in thread "main" 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
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at java.net.URL.openStream(URL.java:1045)
    at DownloadFileExample.download(DownloadFileExample.java:15)
    at DownloadFileExample.main(DownloadFileExample.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
    ... 20 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
    ... 26 more

Here is my code including the file I want to download:

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class DownloadFileExample
{
    public static void download(String downloadURL) throws IOException
    {
        URL website = new URL(downloadURL);
        String fileName = "downloaded.zip";

        try (InputStream inputStream = website.openStream())
        {
            Files.copy(inputStream, Paths.get(fileName), StandardCopyOption.REPLACE_EXISTING);
        }
    }

    public static void main(String[] arguments) throws IOException
    {
        String downloadURL = "https://mh-nexus.de/downloads/HxDSetupEN.zip";
        download(downloadURL);
    }
}

I tried downloading using the HTTP protocol at first but it gives me a 301 moved permanently status code so I realized that it redirects to HTTPS. However, HTTPS gives me the SSLHandshakeException from above. Using a browser, the download works flawlessly. How do I download the file correctly using Java code?

BullyWiiPlaza
  • 12,477
  • 7
  • 82
  • 129

2 Answers2

2

This occurs because the certificate https://mh-nexus.de website is not on the whitelist of your JRE

Options

1) Include the server certificate in JRE whitelist (jre / lib / security / cacerts)    To download the server certificate, open site with browser, right-click on green lock, select 'view certificate' and download

The simplest way to explore cacerts and import trusted certificate is to use a GUI tool like portecle (http://portecle.sourceforge.net/). You can also use keytool

keytool -import -trustcacerts -keystore /opt/java/jre/lib/security/cacerts -alias mycert -noprompt -storepass changeit -file /tmp/examplecert.crt

See How to properly import a selfsigned certificate into Java keystore that is available to all Java applications by default?

2) Use your own truststore and include the server certificate

System.setProperty ("javax.net.ssl.trustStore" path_to_your_cacerts_file);

3) Do not use truststore at all (BullyWiiPlaza response)

Community
  • 1
  • 1
pedrofb
  • 30,587
  • 4
  • 69
  • 122
0

Alright, so I found the solution here. I simply need to install an all-trusting SSL certificate TrustManager. This is not generally a good idea though but serves well for my purpose.

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

public class DownloadFileExample
{
    public static void download(String downloadURL) throws IOException
    {
        URL website = new URL(downloadURL);
        String fileName = "downloaded.zip";

        try (InputStream inputStream = website.openStream())
        {
            Files.copy(inputStream, Paths.get(fileName), StandardCopyOption.REPLACE_EXISTING);
        }
    }

    private static void trustAllCertificates() throws NoSuchAlgorithmException, KeyManagementException
    {
        TrustManager[] trustManagers = new TrustManager[]{new X509TrustManager()
        {
            public X509Certificate[] getAcceptedIssuers()
            {
                return new X509Certificate[0];
            }

            public void checkClientTrusted(
                    X509Certificate[] certs, String authType)
            {
            }

            public void checkServerTrusted(
                    X509Certificate[] certs, String authType)
            {
            }
        }};

        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustManagers, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
    }

    public static void main(String[] arguments) throws IOException, NoSuchAlgorithmException, KeyManagementException
    {
        trustAllCertificates();

        String downloadURL = "https://mh-nexus.de/downloads/HxDSetupEN.zip";
        download(downloadURL);
    }
}
Community
  • 1
  • 1
BullyWiiPlaza
  • 12,477
  • 7
  • 82
  • 129