Thanks Sergey for looking at this.  I am using fully qualified domain names in 
all cases, should've mentioned that.  

These bug reports at OpenJDK/Sun/Oracle are relevant; the second says they know 
the problem but there's no resolution; any fix will probably be in v9, not 1.8.:

        https://bugs.openjdk.java.net/browse/JDK-8072464

        http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8144566

Stack overflow also says that CXF+SNI worked in 1.7, not in 1.8 and offers a 
CXF workaround for 1.8 users; I wonder if this solution could be incorporated 
into CXF?

        
http://stackoverflow.com/questions/30817934/extended-server-name-sni-extension-not-sent-with-jdk1-8-0-but-send-with-jdk1-7
        
So, does CXF configure a custom hostname verifier?  The mere presence of that 
seems to trigger the bug in v1.8.

HTH


> On Sep 15, 2016, at 5:43 AM, Sergey Beryozkin <sberyoz...@gmail.com> wrote:
> 
> Hi
> 
> According to
> http://stackoverflow.com/questions/35366763/in-java-8-can-httpsurlconnection-be-made-to-send-server-name-indication-sni
> 
> SNI is sent only if it is a fully qualified name.
> 
> HTH, Sergey
> 
> On 14/09/16 22:22, Chris Lott wrote:
>> 
>> 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);
>>    }
>> }
> 
> 
> -- 
> Sergey Beryozkin
> 
> Talend Community Coders
> http://coders.talend.com/
> 

Reply via email to