Sean Charles
Sean Charles

Reputation: 359

With https URL, HttpsURLConnection works but HttpClient does not

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?

Upvotes: 2

Views: 2612

Answers (2)

ok2c
ok2c

Reputation: 27528

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.

Upvotes: 3

MichaelK
MichaelK

Reputation: 3083

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.

Upvotes: 0

Related Questions