所以这是我对该代码的尝试。我已经将它隐藏在我们可以在部署时调整的配置属性后面,所以希望我们可以在 Web 服务服务器配置修复后尽快摆脱这个缺陷。
我没有检查过这段代码的任何安全隐患。我只用我的应用程序对此进行了测试,它可能不适合你。其他 JVM 版本的行为可能不同。如果您决定使用此代码,请不要让我负责!
2014-09-01 更新:不幸的是,此解决方法仅在您通过 HTTPS 代理连接到目标时才有效。如果你直接连接,你会得到有点不伦不类的错误。
Caused by: java.net.SocketException: Unconnected sockets not implemented
at javax.net.SocketFactory.createSocket(SocketFactory.java:125) ~[na:1.7.0_65]
由于我们的目标服务器已经修复了它的配置,我不再需要解决方法了。添加工作存根 createSocket() 方法要复杂得多,因为它需要在 HTTPClients 的 SSLSocketFactory 中完成。
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* When a TLS client sends the Server Name Indication extension in the Client Hello, the server might reply with an
* alert that it does not know this particular server name. Usually, this means that the server is misconfigured,
* or that you're using the wrong hostname. But as long as the server does get you to the correct web service or
* site, it's harmless.
* <p/>
* Oracle in it's wisdom has decided that the warning should be treated as an error. This cannot be changed. This
* class overrides all createSocket calls so that the hostname (for TLS purposes) is blanked-out. In the
* implementation in 1.7.0_51-b13, this makes SSL not issue the SNI extension in the hello,
* thereby not triggering the problematic response.
* <p/>
* To use this with Apache HttpComponents' HttpClient, you need to create a ConnectionManager that uses a
* SchemeManager, which in turn uses a custom https SchemeHandler using this socket factory.
*
* <pre>
* {@code
* SchemeRegistry schemeRegistry = new SchemeRegistry();
* schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
* if (disableServerNameIndication) {
* schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(new NoSNISSLSocketFactory
* (sslcontext.getSocketFactory()),
* SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)));
* } else {
* schemeRegistry.register(new Scheme("https", 443, new SSLSocketFactory(sslcontext)));
* }
* defaultHttpClient = new DefaultHttpClient(new PoolingClientConnectionManager(schemeRegistry));}
* </pre>
*/
public class NoSNISSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory sslSocketFactory;
protected NoSNISSLSocketFactory(SSLSocketFactory socketFactory) {
this.sslSocketFactory = socketFactory;
}
@Override
public String[] getDefaultCipherSuites() {
return sslSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return sslSocketFactory.createSocket(socket, "", port, autoClose);
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return createSocket(new Socket(host, port), host, port, true);
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException,
UnknownHostException {
return createSocket(new Socket(host, port, localHost, localPort), host, port, true);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return sslSocketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
return createSocket(new Socket(host, port, localHost, localPort), host.getHostName(), port, true);
}
}