Sergey thanks for the many constructive suggestions. In the mean time we punted - bought a multi-name (SAN) certificate. CXF works *perfectly* with that certificate; my trivial Java client pukes :( a neat reversal of the previous situation!
> On Sep 15, 2016, at 12:27 PM, Sergey Beryozkin <[email protected]> wrote: > > But you can indeed set a custom SSLFactory wrapper easily on HttpConduit TLS > properties, get HTTPConduit as shown below and see if that solution works... > > Sergey > On 15/09/16 17:22, Sergey Beryozkin wrote: >> I've just given it a quick try myself - temporarily updated >> HttpsURLConnectionFactory ignore the verifier but I still see no >> server_name extension in the debug - I've tried jax_rs/basic_http demo >> with Java 8. >> >> Sergey >> >> >> On 15/09/16 16:43, Sergey Beryozkin wrote: >>> Hi >>> On 15/09/16 11:59, Chris Lott wrote: >>>> 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 >>> >>> Perhaps you can use Java 9 to run your client code ? >>> >>>> >>>> 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? >>>> >>> It does not seem to be a solution that can guarantee anything. Colm do >>> you think it is worth it ? >>> >>>> >>>> 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. >>> It does but I can see one issue being closed as can not reproduce. >>> If you'd like you can experiment with the following: >>> - check out CXF 3.1.x source >>> - add NoOpHostnameVerifier which will throw UOE in this package >>> https://github.com/apache/cxf/tree/master/rt/transports/http/src/main/java/org/apache/cxf/transport/https >>> >>> >>> - update this code, >>> https://github.com/apache/cxf/blob/master/rt/transports/http/src/main/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java#L167 >>> >>> >>> >>> to check if the verifier an instance of NoOpHostnameVerifier, if yes - >>> do not set it at all. >>> - rebuild rt/transports/http >>> >>> - configure HTTPConduit TSL properties with an instance of >>> NoOpHostnameVerifier (you can get to it with >>> WebClient.getConfig(webClient).getHttpConduit()) >>> >>> and see if avoiding setting the verifier at all can fix it... >>> If yes then we can decide on the final fix >>> >>> Cheers, Sergey >>> >>> >>>> >>>> HTH >>>> >>>> >>>>> On Sep 15, 2016, at 5:43 AM, Sergey Beryozkin <[email protected]> >>>>> 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/ >>>>> >>>> >>> >>> >> >> > > > -- > Sergey Beryozkin > > Talend Community Coders > http://coders.talend.com/ >
