JDK-8163267 : Inconsistent behavior when using SSL over socks proxy
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 8
  • Priority: P3
  • Status: Resolved
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2016-01-19
  • Updated: 2016-09-28
  • Resolved: 2016-09-28
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux l-qfwrapper28.f.cn8 2.6.32-358.23.2.el6.x86_64 #1 SMP Wed Oct 16 18:37:12 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
Some websites need server_name in Client Hello.

I create SSLSocket over a socks proxyed socket and call SSLSocket.setHost(String) in following way:
        
        final Socket socket = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksProxyHost, socksProxyPort)));
        socket.connect(new InetSocketAddress(hostname, port));
        final Socket sslSocket = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(socket, socksProxyHost, socksProxyPort, true);
        final SSLSocketImpl impl = (SSLSocketImpl) sslSocket;
        impl.setHost(hostname);


This works in jdk1.7.0_45, but fails in lastest jdk1.8.0_65.

In jdk1.8, create SSLSocket using connected socket and then setHost don't modify serverNames in HandShaker, which will be used in handshake later.

REGRESSION.  Last worked in version 7u76

ADDITIONAL REGRESSION INFORMATION: 
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Using source code in "Source code for an executable test case:"

Compile and run with:
java -Djavax.net.debug=all SSLOverSocksMain  | grep server_name

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
[tianyu.yang@l-qfwrapper28.f.cn8 ~]$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

[tianyu.yang@l-qfwrapper28.f.cn8 ~]$ java -Djavax.net.debug=all SSLOverSocksMain  | grep server_name
Extension server_name, server_name: [host_name: www.baidu.com]

[tianyu.yang@l-qfwrapper28.f.cn8 ~]$ 

ACTUAL -
[tianyu.yang@l-qfwrapper28.f.cn8 ~]$ java -versionjava version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

[tianyu.yang@l-qfwrapper28.f.cn8 ~]$ java -Djavax.net.debug=all SSLOverSocksMain  | grep server_name

[tianyu.yang@l-qfwrapper28.f.cn8 ~]$ 

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import sun.security.ssl.SSLSocketImpl;

import javax.net.ssl.SNIHostName;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Collections;

/**
 * @author tianyu.yang
 */
public class SSLOverSocksMain {
    public static void main(String[] args) throws IOException {
        final String socksProxyHost = "10.89.38.236"; // a socks proxy
        final int socksProxyPort = 16045;

        final String hostname = "www.baidu.com";
        final int port = 443;

        final Socket socket = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksProxyHost, socksProxyPort)));
        socket.connect(new InetSocketAddress(hostname, port));
        final Socket sslSocket = ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(socket, socksProxyHost, socksProxyPort, true);

        try {
            sslSocket.setSoTimeout(3000);
            final SSLSocketImpl impl = (SSLSocketImpl) sslSocket;

            // jdk 1.7 works
            impl.setHost(hostname);

            // jdk 1.8 works
            // final SSLParameters sslParameters = impl.getSSLParameters();
            // sslParameters.setServerNames(Collections.<SNIServerName>singletonList(new SNIHostName(hostname)));
            // impl.setSSLParameters(sslParameters);

            // request
            String getRequest = "GET / HTTP/1.1\r\nHost: www.baidu.com\r\nAccept: */*\r\n\r\n";
            sslSocket.getOutputStream().write(getRequest.getBytes());
            sslSocket.getOutputStream().flush();

            // consume response
            final StringBuilder buf = new StringBuilder();
            int read = sslSocket.getInputStream().read();
            try {
                while (read >= 0) {
                    buf.append((char)read);
                    read = sslSocket.getInputStream().read();
                }
            } catch (SocketTimeoutException e) {
                // ignore
            }

            // System.out.println(buf.toString());
        } finally {
            sslSocket.close();
        }
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Comment code below "jdk 1.7 works" and uncomment the code below "jdk 1.8 works". Then it works under jdk1.8.


Comments
This bug is already addressed by https://bugs.openjdk.java.net/browse/JDK-8144566
28-09-2016

To reproduce the issue, edit the socksProxyHost and socksProxyPort fields in the attached code to point to a SOCKS PROXY host and port and run the code. Following are the results: JDK 7u80- Pass JDK 8 - Fail JDK 8u102- Fail JDK 9ea+128 - Pass Following is output on various versions: JDK 7u80 - Extension server_name, server_name: [host_name: www.google.com] Extension server_name, server_name: [host_name: www.google.com] JDK 8 and 8u102 - Blank JDK 9ea+128- Extension server_name, server_name: [type=host_name (0), value=www.google.com] Extension server_name, server_name: [type=host_name (0), value=www.google.com]
05-08-2016