Extended server_name (SNI Extension) not sent with jdk1.8.0 but send with jdk1.7.0

跟風遠走 提交于 2019-11-27 02:03:54

As mentioned, the cause is related to the JDK bug where using setHostnameVerifier() breaks SNI (Extension server_name). https://bugs.openjdk.java.net/browse/JDK-8144566

Our workaround: After testing we found that setting a connection's SSLSocketFactory to just about anything from the default seems to fix the issue.

This does not work: HttpsURLConnection.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());

This does work: HttpsURLConnection.setSSLSocketFactory(new SSLSocketFactoryFacade());

So, to fix it for a JAX-WS client, you could do something like this: bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", new SSLSocketFactoryFacade());

Our SSLSocketFactory facade: (Note that it really doesn't do anything)

public class SSLSocketFactoryFacade extends SSLSocketFactory {

    SSLSocketFactory sslsf;

    public SSLSocketFactoryFacade() {
        sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();;
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return sslsf.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return sslsf.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
        return sslsf.createSocket(socket, s, i, b);
    }

    @Override
    public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
        return sslsf.createSocket(s, i);
    }

    @Override
    public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
        return sslsf.createSocket(s, i, inetAddress, i1);
    }

    @Override
    public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
        return createSocket(inetAddress, i);
    }

    @Override
    public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
        return createSocket(inetAddress, i, inetAddress1, i1);
    }
}

You, or the underlying libs (the WS lib does it) might be using setHostnameVerifier(..)

There is a bug in java8, where if setHostnameVerifier(..) is used the SNI is not done from the client side.

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

Please use JDK version 8u141 and above where this issue has been fixed. Please review the JDK 8u141 Bugs Fixes page for more details

First of all, this "server_name" stuff is associate with the SNI (Server Name Indication) extension. The Java 8 JSSE documentation talks about it here.

The documentation includes example code that shows how to set the server names that are sent. The code is for Java 8.

However, I can't figure out why (apparently) Java 7 is setting the server name by default, and Java 8 isn't. (The easy way to figure it out would be to use a debugger to figure out how the SSL engine object is created and initialized.)

I tried the solution provided by Benjamin Parry, but it did not work for me. After some digging around, I also found this solution which looks very similar, however the SSLSocketFactoryFacade manually inserts the correct SSL header instead of being a pure pass-though. Providing my final code below which is slightly different, but credit to be given to Girish Kamath at javabreaks for the basic idea:

    private static class SSLSocketFactoryFacade extends SSLSocketFactory {
    private SSLSocketFactory sslsf;
    private SSLParameters sslParameters;

    public SSLSocketFactoryFacade(String hostName) {
        sslParameters = new SSLParameters();
        sslParameters.setServerNames(Arrays.asList(new SNIHostName(hostName)));
        sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();;
    }

    public Socket createSocket() throws IOException {
        Socket socket = sslsf.createSocket();
        ((SSLSocket) socket).setSSLParameters(sslParameters);
        return socket;
    }

    public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
        Socket socket = sslsf.createSocket(arg0, arg1, arg2, arg3);
        ((SSLSocket) socket).setSSLParameters(sslParameters);
        return socket;
    }

    public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
        Socket socket = sslsf.createSocket(arg0, arg1);
        ((SSLSocket) socket).setSSLParameters(sslParameters);
        return socket;
    }

    public Socket createSocket(Socket arg0, InputStream arg1, boolean arg2) throws IOException {
        Socket socket = sslsf.createSocket(arg0, arg1, arg2);
        ((SSLSocket) socket).setSSLParameters(sslParameters);
        return socket;
    }

    public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3) throws IOException {
        Socket socket = sslsf.createSocket(arg0, arg1, arg2, arg3);
        ((SSLSocket) socket).setSSLParameters(sslParameters);
        return socket;
    }

    public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3)
            throws IOException, UnknownHostException {
        Socket socket = sslsf.createSocket(arg0, arg1, arg2, arg3);
        ((SSLSocket) socket).setSSLParameters(sslParameters);
        return socket;
    }

    public Socket createSocket(String arg0, int arg1) throws IOException, UnknownHostException {
        Socket socket = sslsf.createSocket(arg0, arg1);
        ((SSLSocket) socket).setSSLParameters(sslParameters);
        return socket;
    }

    public String[] getDefaultCipherSuites() {
        return sslsf.getDefaultCipherSuites();
    }

    public String[] getSupportedCipherSuites() {
        return sslsf.getSupportedCipherSuites();
    }
}

And then I can call

sslConnection.setSSLSocketFactory(new SSLSocketFactoryFacade(sslConnection.getURL().getHost()));

where sslConnection is the HttpsURLConnection.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!