2

I have a web app (on WebSphere) that interacts with 3rd party to format some received data, and then make RESTful call to said 3rd party, which resides on a Tomcat server. Within the web app I can successfully make a basic RESTful GET call using Java's native HttpsUrlConnection (see below), but when I use Apache's HttpClient, it fails with an SSL chaining error.

The HttpsURLConnection:

    String urlWithParams = "https://example.com/rest/issue/59"; 

    String methodType = "GET"; 
    String acceptType = MediaType.APPLICATION_JSON;

    HttpsURLConnection conn = null; 

    try {

        URL url = new URL(urlWithParams);
        conn = (HttpsURLConnection) url.openConnection();
        conn.setRequestMethod(methodType);
        conn.setRequestProperty("Accept", acceptType);

        if (conn.getResponseCode() != 200) {
            System.out.println("Failed. httpErrorUrl=" + conn.getResponseCode()
                    + " httpErrorCode=" + conn.getResponseCode()
                    + " httpErrorMsg=" + conn.getResponseMessage());

        }

        BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

        String rawData;
        String toReturn = "";
        while ((rawData = br.readLine()) != null) {
            toReturn += rawData;
        }

        System.out.println(toReturn);

    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally{
        if(conn != null){
            conn.disconnect();
        } else {
            System.out.println("httpUrlConnection is null");
        }
    }

The HttpClient code:

    String uri = "https://example.com/rest/issue/59"; 
    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
    connManager.setDefaultMaxPerRoute(20);
    connManager.setMaxTotal(40);

    CloseableHttpClient httpclient = HttpClients.createDefault();

The HttpClient methods are executed in the function below:

    private JSON request(HttpRequestBase req) throws RestException, IOException {
        req.addHeader("Accept", "application/json");

        if (creds != null)
            creds.authenticate(req);

        HttpResponse resp = httpClient.execute(req);
        HttpEntity ent = resp.getEntity();
        StringBuilder result = new StringBuilder();

        if (ent != null) {
            String encoding = null;
            if (ent.getContentEncoding() != null) {
                encoding = ent.getContentEncoding().getValue();
            }

            if (encoding == null) {
                Header contentTypeHeader = resp.getFirstHeader("Content-Type");
                HeaderElement[] contentTypeElements = contentTypeHeader.getElements();
                for (HeaderElement he : contentTypeElements) {
                    NameValuePair nvp = he.getParameterByName("charset");
                    if (nvp != null) {
                        encoding = nvp.getValue();
                    }
                }
            }

            InputStreamReader isr =  encoding != null ?
                new InputStreamReader(ent.getContent(), encoding) :
                new InputStreamReader(ent.getContent());
            BufferedReader br = new BufferedReader(isr);
            String line = "";

            while ((line = br.readLine()) != null)
                result.append(line);
        }

        StatusLine sl = resp.getStatusLine();

        if (sl.getStatusCode() >= 300)
            throw new RestException(sl.getReasonPhrase(), sl.getStatusCode(), result.toString());

        return result.length() > 0 ? JSONSerializer.toJSON(result.toString()): null;
    }    

The error produced is a typical chaining exception, which usually means the SSL certs are not correct. Since the certs are imported at the App Server level, I would expect the SSL connections to be handled the same for both HttpsURLConnection and HttpClient calls.

Caused by: `com.ibm.jsse2.util.j: PKIX` path building failed: `java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl` co

uld not build a valid CertPath.; internal cause is: java.security.cert.CertPathValidatorException: The certificate issued by CN=Entrust Root Certification Authority, OU="(c) 2 006 Entrust, Inc.", OU=www.entrust.net/CPS is incorporated by reference, O="Entrust, Inc.", C=US is not trusted; internal cause is:

    `java.security.cert.CertPathValidatorException:` Certificate chaining error
    at com.ibm.jsse2.util.h.b(h.java:18) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.util.h.b(h.java:118) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.util.g.a(g.java:14) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.pc.a(pc.java:41) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.pc.checkServerTrusted(pc.java:1) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.pc.b(pc.java:90) ~[na:6.0 build_20141024]
    at com.ibm.jsse2.lb.a(lb.java:499) ~[na:6.0 build_20141024]
    ... 80 common frames omitted

Caused by: java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl could not build a valid CertPath. at com.ibm.security.cert.PKIXCertPathBuilderImpl.engineBuild(PKIXCertPathBuilderImpl.java:411) ~[na:na] at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:258) ~[na:na] at com.ibm.jsse2.util.h.b(h.java:61) ~[na:6.0 build_20141024] ... 86 common frames omitted

Why are certificates being applied for HttpsURLConnection but not for HttpClient? What am I doing wrong?

Kasun Siyambalapitiya
  • 3,956
  • 8
  • 38
  • 58
Sean Charles
  • 267
  • 3
  • 18
  • My guess this is an issue with SNI, see http://stackoverflow.com/questions/5879894/android-ssl-sni-support. If if this post is form 2011 it is still relevant. – Steffen Ullrich Apr 19 '16 at 16:30

2 Answers2

3

HttpClient does not take system properties into account unless explicitly configured to do do.

Try replacing

HttpClients.createDefault()

with

HttpClients.createSystem()

Please also note that PoolingHttpClientConnectionManager instance in your code is not used. Do not create custom connection managers unless you have a very good reason to do so.

ok2c
  • 26,450
  • 5
  • 63
  • 71
  • Thanks @oleg. Wow, I cannot believe after all of my google searching on Apache HttpClient and SSL I did not come across HttpClients.createSystem(). Thank you very much for the info, it fixed my issues. – Sean Charles Apr 19 '16 at 18:47
0

The following question has a good code example on how to use HTTPS over HttpClient.

Using Apache httpclient for https

In brief: your code is missing all of the HTTPS specific setup.. you are using it as if it was just a regular HTTP request.

Community
  • 1
  • 1
MichaelK
  • 2,859
  • 1
  • 14
  • 16
  • I'm not sure I understand what you are saying. In the [HttpClient SSL Docs](http://hc.apache.org/httpclient-3.x/sslguide.html), it states that **Once you have JSSE correctly installed, secure HTTP communication over SSL should be as simple as plain HTTP communication**. Since the source of my call is within WebSphere, it should handle all of the SSL negotiations (like it does when I use HttpsURLConnection, right? – Sean Charles Apr 19 '16 at 16:26
  • Does not seem like that is the case now does it. Try the example given in the linked post and see if it works. – MichaelK Apr 19 '16 at 16:32