I'd like to ask about a behavior I see in CXF v 3.0.10 and v 3.1.7. I'm using JDK 1.8, and I have a tiny test program (see below). Our REST service is provided by Apache HTTPD (fronting Tomcat). The HTTPD is configured with 2 virtual hosts that differ only in name. A conforming client that sends SNI information works perfectly - receives the appropriate certificate. The CXF client does not work - throws an exception like this:

Caused by: java.io.IOException: HTTPS hostname wrong: should be <my-host-qa.my.company.com> at sun.net.www.protocol.https.HttpsClient.checkURLSpoofing(HttpsClient.java:649) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:573) 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 java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338) at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.getResponseCode(URLConnectionHTTPConduit.java:332) at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.doProcessResponseCode(HTTPConduit.java:1581) at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1610) at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1551) at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1348)
        ... 12 more


In a nutshell, the CXF library does not seem to provide Server Name Indication (SNI) to the remote HTTPS server. Note that fetching application/json content using a very plain Java method works just fine - it provides SNI and gets the appropriate certificate - so I do not believe it's a limitation of the platform that I'm using.

Is there some additional configuration I need to do in this sample client?

Thanks for listening.

---

package com.mycompany.ecomp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.cxf.jaxrs.client.WebClient;

/**
* Trivial Java REST client to test whether SNI is provided when HTTPS content
 * is fetched.  Tested with Apache CXF ver 3.0.10 and 3.1.7.
 */
public class FetchRestContent {

        class HttpResponse {
                int code;
                String body;
        }

        /**
         * Fetches REST content using only features provided by standard Java
         * libraries.
         *
         * @param url
         * @param user
         *            Set as header parameter "username"
         * @param pass
         *            Set as header parameter "password";
         * @return Code and body fetched from remote server
         * @throws Exception
         *             on any failure
         */
private HttpResponse getRestContentJdk(String uriString, String path, String user, String pass) throws Exception {

                URL url = new URL(uriString + '/' + path);
                HttpsURLConnection con = (HttpsURLConnection) 
url.openConnection();
                if (con == null)
                        throw new Exception("Failed to open HTTPS connection");

                con.setRequestMethod("GET");
                con.setConnectTimeout(2000);
                con.setReadTimeout(2000);
                con.setRequestProperty("username", user);
                con.setRequestProperty("password", pass);

                // Throws an exception on an HTTPS connection if the
                // local trust store is not configured appropriately to
                // accept the self-signed certificate at the remote end.
                HttpResponse resp = new HttpResponse();
                resp.code = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));

                Certificate[] certs = con.getServerCertificates();
                for (Certificate cert : certs) {
                        if (cert instanceof X509Certificate) {
                                X509Certificate xcert = (X509Certificate) cert;
System.out.println("X509 Principal name " + xcert.getSubjectX500Principal().getName());
                        }
                }

                StringBuffer sb = new StringBuffer();
                String inputLine;
                while ((inputLine = in.readLine()) != null)
                        sb.append(inputLine);
                in.close();
                con.getInputStream().close();
                con.disconnect();
                resp.body = sb.toString();
                return resp;
        }

        /**
         * Fetches REST content using Jax-RS, as implemented by Apache CXF.
         *
         * @param uri
         * @param path
         * @param user
         * @param pass
         * @return
         * @throws Exception
         */
private HttpResponse getRestContentJaxrs(String uriString, String path, String user, String pass) throws Exception {
                URI uri = new URI(uriString);
                WebClient client = WebClient.create(uri);
                
client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
                client.path(path);
                Response response = client.get();
                HttpResponse httpResponse = new HttpResponse();
                httpResponse.code = response.getStatus();
                httpResponse.body = response.readEntity(String.class);
                return httpResponse;
        }

        public static void main(String[] args) throws Exception {
                if (args.length != 4)
throw new IllegalArgumentException("Expect 4 arguments: URL path username password");
                if (!args[0].toLowerCase().startsWith("https"))
                        throw new IllegalArgumentException("Expect https 
prefix");
                FetchRestContent fetcher = new FetchRestContent();

System.out.println("GET-ing content via Java from " + args[0] + ", path " + args[1]); HttpResponse r1 = fetcher.getRestContentJdk(args[0], args[1], args[2], args[3]);
                System.out.println("HTTP response code is " + r1.code);
                System.out.println("Response body follows:");
                System.out.println(r1.body);

System.out.println("GET-ing content via Jax-RS from " + args[0] + ", path " + args[1]); HttpResponse r2 = fetcher.getRestContentJaxrs(args[0], args[1], args[2], args[3]);
                System.out.println("HTTP response code is " + r2.code);
                System.out.println("Response body follows:");
                System.out.println(r2.body);
        }
}

Reply via email to