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/
> 

Reply via email to