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