How to handle invalid SSL certificates with Apache HttpClient?

Viewed 355816

I know, there are many different questions and so many answers about this problem... But I can't understand...

I have: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 installed "as is" from off. rep. I need connecting to some site over the HTTPS. For this I use Apache's HttpClient.

From tutorial I read:

"Once you have JSSE correctly installed, secure HTTP communication over SSL should be as
simple as plain HTTP communication." And some example:

HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/"); 
try { 
  httpclient.executeMethod(httpget);
  System.out.println(httpget.getStatusLine());
} finally {
  httpget.releaseConnection();
}

By now, I write this:

HttpClient client = new HttpClient();

HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);

try {
    int status = client.executeMethod(get);
    System.out.println(status);

    BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
    int r=0;byte[] buf = new byte[10];
    while((r = is.read(buf)) > 0) {
        System.out.write(buf,0,r);
    }

} catch(Exception ex) {
    ex.printStackTrace();
}

As a result I have a set of errors:

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:1627)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
        at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
        at simpleapachehttp.Main.main(Main.java:41)
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:302)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
        at sun.security.validator.Validator.validate(Validator.java:235)
        at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
        ... 17 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:191)
        at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
        ... 23 more

What have I to do to create simplest SSL connection? (Probably without KeyManager and Trust manager etc. while.)

18 Answers

want to paste the answer here:

in Apache HttpClient 4.5.5

How to handle invalid SSL certificate with Apache client 4.5.5?

HttpClient httpClient = HttpClients
            .custom()
            .setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
            .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
            .build();

I happened to face the same issue, all of a sudden all my imports were missing. I tried deleting all the contents in my .m2 folder. And trying to re-import everything , but still nothing worked. Finally what I did was opened the website for which the IDE was complaining that it couldn't download in my browser. And saw the certificate it was using, and saw in my

$ keytool -v -list  PATH_TO_JAVA_KEYSTORE

Path to my keystore was /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts

that particular certificate was not there.

So all you have to do is put the certificate into the JAVA JVM keystore again. It can be done using the below command.

$ keytool -import -alias ANY_NAME_YOU_WANT_TO_GIVE -file PATH_TO_YOUR_CERTIFICATE -keystore PATH_OF_JAVA_KEYSTORE

If it asks for password, try the default password 'changeit' If you get permission error when running the above command. In windows open it in administration mode. In mac and unix use sudo.

After you have successfully added the key, You can view it using :

$ keytool -v -list  /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts 

You can view just the SHA-1 using teh command

$ keytool -list  /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts 

This link explains the requirement you have step by step. If You are not really concerned which certificate you can proceed with the process in below link.

Note You might want to double check what you are doing since, this is a unsafe operation.

I'm useing httpclient 3.1.X ,and this works for me

        try {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        sslContext.init(null, new TrustManager[]{trustManager}, null);
        SslContextSecureProtocolSocketFactory socketFactory = new SslContextSecureProtocolSocketFactory(sslContext,false);
        Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) socketFactory, 443));//同样会影响到HttpUtils
    } catch (Throwable e) {
        e.printStackTrace();

}

public class SslContextSecureProtocolSocketFactory implements      SecureProtocolSocketFactory {

private SSLContext sslContext;
private boolean verifyHostname;

public SslContextSecureProtocolSocketFactory(SSLContext sslContext, boolean verifyHostname) {
    this.verifyHostname = true;
    this.sslContext = sslContext;
    this.verifyHostname = verifyHostname;
}

public SslContextSecureProtocolSocketFactory(SSLContext sslContext) {
    this(sslContext, true);
}

public SslContextSecureProtocolSocketFactory(boolean verifyHostname) {
    this((SSLContext)null, verifyHostname);
}

public SslContextSecureProtocolSocketFactory() {
    this((SSLContext)null, true);
}

public synchronized void setHostnameVerification(boolean verifyHostname) {
    this.verifyHostname = verifyHostname;
}

public synchronized boolean getHostnameVerification() {
    return this.verifyHostname;
}

public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
    SSLSocketFactory sf = this.getSslSocketFactory();
    SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port, clientHost, clientPort);
    this.verifyHostname(sslSocket);
    return sslSocket;
}

public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
    if(params == null) {
        throw new IllegalArgumentException("Parameters may not be null");
    } else {
        int timeout = params.getConnectionTimeout();
        Socket socket = null;
        SSLSocketFactory socketfactory = this.getSslSocketFactory();
        if(timeout == 0) {
            socket = socketfactory.createSocket(host, port, localAddress, localPort);
        } else {
            socket = socketfactory.createSocket();
            InetSocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
            InetSocketAddress remoteaddr = new InetSocketAddress(host, port);
            socket.bind(localaddr);
            socket.connect(remoteaddr, timeout);
        }

        this.verifyHostname((SSLSocket)socket);
        return socket;
    }
}

public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
    SSLSocketFactory sf = this.getSslSocketFactory();
    SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port);
    this.verifyHostname(sslSocket);
    return sslSocket;
}

public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    SSLSocketFactory sf = this.getSslSocketFactory();
    SSLSocket sslSocket = (SSLSocket)sf.createSocket(socket, host, port, autoClose);
    this.verifyHostname(sslSocket);
    return sslSocket;
}

private void verifyHostname(SSLSocket socket) throws SSLPeerUnverifiedException, UnknownHostException {
    synchronized(this) {
        if(!this.verifyHostname) {
            return;
        }
    }

    SSLSession session = socket.getSession();
    String hostname = session.getPeerHost();

    try {
        InetAddress.getByName(hostname);
    } catch (UnknownHostException var10) {
        throw new UnknownHostException("Could not resolve SSL sessions server hostname: " + hostname);
    }

    X509Certificate[] certs = (X509Certificate[])((X509Certificate[])session.getPeerCertificates());
    if(certs != null && certs.length != 0) {
        X500Principal subjectDN = certs[0].getSubjectX500Principal();
        List cns = this.getCNs(subjectDN);
        boolean foundHostName = false;
        Iterator i$ = cns.iterator();
        AntPathMatcher matcher  = new AntPathMatcher();
        while(i$.hasNext()) {
            String cn = (String)i$.next();
            if(matcher.match(cn.toLowerCase(),hostname.toLowerCase())) {
                foundHostName = true;
                break;
            }
        }

        if(!foundHostName) {
            throw new SSLPeerUnverifiedException("HTTPS hostname invalid: expected \'" + hostname + "\', received \'" + cns + "\'");
        }
    } else {
        throw new SSLPeerUnverifiedException("No server certificates found!");
    }
}

private List<String> getCNs(X500Principal subjectDN) {
    ArrayList cns = new ArrayList();
    StringTokenizer st = new StringTokenizer(subjectDN.getName(), ",");

    while(st.hasMoreTokens()) {
        String cnField = st.nextToken();
        if(cnField.startsWith("CN=")) {
            cns.add(cnField.substring(3));
        }
    }

    return cns;
}

protected SSLSocketFactory getSslSocketFactory() {
    SSLSocketFactory sslSocketFactory = null;
    synchronized(this) {
        if(this.sslContext != null) {
            sslSocketFactory = this.sslContext.getSocketFactory();
        }
    }

    if(sslSocketFactory == null) {
        sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
    }

    return sslSocketFactory;
}

public synchronized void setSSLContext(SSLContext sslContext) {
    this.sslContext = sslContext;
}

}

For HttpClient, we can do this :

SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
        SSLContext.setDefault(ctx);

        String uri = new StringBuilder("url").toString();

        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        };

        HttpClient client = HttpClientBuilder.create().setSSLContext(ctx)
                .setSSLHostnameVerifier(hostnameVerifier).build()

Used the following along with DefaultTrustManager and it worked in httpclient like charm. Thanks a ton!! @Kevin and every other contributor

    SSLContext ctx = null;
    SSLConnectionSocketFactory sslsf = null;
    try {

        ctx = SSLContext.getInstance("TLS");
        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
        SSLContext.setDefault(ctx);

        sslsf = new SSLConnectionSocketFactory(
                ctx,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

    } catch (Exception e) {
        e.printStackTrace();
    }

     CloseableHttpClient client = HttpClients.custom()
            .setSSLSocketFactory(sslsf)
            .build();
Related