How to handle invalid SSL certificates with Apache HttpClient? [duplicate] How to handle invalid SSL certificates with Apache HttpClient? [duplicate] java java

How to handle invalid SSL certificates with Apache HttpClient? [duplicate]


https://mms.nw.ru uses a self-signed certificate that's not in the default trust manager set. To resolve the issue, do one of the following:

  • Configure SSLContext with a TrustManager that accepts any certificate (see below).
  • Configure SSLContext with an appropriate trust store that includes your certificate.
  • Add the certificate for that site to the default Java trust store.

Here's a program that creates a (mostly worthless) SSL Context that accepts any certificate:

import java.net.URL;import java.security.SecureRandom;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.HttpsURLConnection;import javax.net.ssl.KeyManager;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSession;import javax.net.ssl.TrustManager;import javax.net.ssl.X509TrustManager;public class SSLTest {        public static void main(String [] args) throws Exception {        // configure the SSLContext with a TrustManager        SSLContext ctx = SSLContext.getInstance("TLS");        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());        SSLContext.setDefault(ctx);        URL url = new URL("https://mms.nw.ru");        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();        conn.setHostnameVerifier(new HostnameVerifier() {            @Override            public boolean verify(String arg0, SSLSession arg1) {                return true;            }        });        System.out.println(conn.getResponseCode());        conn.disconnect();    }        private static class DefaultTrustManager implements X509TrustManager {        @Override        public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}        @Override        public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}        @Override        public X509Certificate[] getAcceptedIssuers() {            return null;        }    }}


https://mms.nw.ru likely uses a certificate not issued by a certification authority. Consequently, you need to add the certificate to your trusted Java key store as explained in unable to find valid certification path to requested target:

When working on a client that works with an SSL enabled server running in https protocol, you could get error 'unable to find valid certification path to requested target' if the server certificate is not issued by certification authority, but a self signed or issued by a private CMS.

Don't panic. All you need to do is to add the server certificate to your trusted Java key store if your client is written in Java. You might be wondering how as if you can not access the machine where the server is installed. There is a simple program can help you. Please download the Java program and run

% java InstallCert _web_site_hostname_

This program opened a connection to the specified host and started an SSL handshake. It printed the exception stack trace of the error that occured and shows you the certificates used by the server. Now it prompts you add the certificate to your trusted KeyStore.

If you've changed your mind, enter 'q'. If you really want to add the certificate, enter '1', or other numbers to add other certificates, even a CA certificate, but you usually don't want to do that. Once you have made your choice, the program will display the complete certificate and then added it to a Java KeyStore named 'jssecacerts' in the current directory.

To use it in your program, either configure JSSE to use it as its trust store or copy it into your $JAVA_HOME/jre/lib/security directory. If you want all Java applications to recognize the certificate as trusted and not just JSSE, you could also overwrite the cacerts file in that directory.

After all that, JSSE will be able to complete a handshake with the host, which you can verify by running the program again.

To get more details, you can check out Leeland's blog No more 'unable to find valid certification path to requested target'


In addition to Pascal Thivent's correct answer, another way is to save the certificate from Firefox (View Certificate -> Details -> export) or openssl s_client and import it into the trust store.

You should only do this if you have a way to verify that certificate. Failing that, do it the first time you connect, it will at least give you an error if the certificate changes unexpectedly on subsequent connections.

To import it in a trust store, use:

keytool -importcert -keystore truststore.jks -file servercert.pem

By default, the default trust store should be $JAVA_HOME/jre/lib/security/cacerts and its password should be changeit, see JSSE Reference guide for details.

If you don't want to allow that certificate globally, but only for these connections, it's possible to create an SSLContext for it:

TrustManagerFactory tmf = TrustManagerFactory    .getInstance(TrustManagerFactory.getDefaultAlgorithm());KeyStore ks = KeyStore.getInstance("JKS");FileInputStream fis = new FileInputStream("/.../truststore.jks");ks.load(fis, null);// or ks.load(fis, "thepassword".toCharArray());fis.close();tmf.init(ks);SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null, tmf.getTrustManagers(), null);

Then, you need to set it up for Apache HTTP Client 3.x by implementing one if its SecureProtocolSocketFactory to use this SSLContext. (There are examples here).

Apache HTTP Client 4.x (apart from the earliest version) has direct support for passing an SSLContext.