java - 当服务器使用服务器名称指示(SNI)扩展时,如何使用 HttpClient 4.5.2 和 JDK 1.8 获取 HTTPS 网页资源?

标签 java https httpclient apache-httpcomponents sni

我无法使用 HttpClient 从 SNI 站点获取资源。我尝试获取的网址是:https://dabar.srce.hr/ , https://www.lutrija.hr/cms/splash .

我收到此错误: javax.net.ssl.SSLHandshakeException:收到致命警报:handshake_failure

据我了解文档,它应该像这样开箱即用(并且它适用于非 sni https 站点):

url = "https://dabar.srce.hr/";
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
    // trust all certificates
    public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        return true;
    }
}).build();
SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslSF).build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpGet);
System.out.println(response.getStatusLine().toString());
HttpEntity entity = response.getEntity();
System.out.println(EntityUtils.toString(entity));

我尝试显式启用 SNIExtension:

System.setProperty("jsse.enableSNIExtension", "true");                        

我尝试覆盖 SSLConnectionSocketFactory:

SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE) {
    String targetHost = "";
    @Override
    public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context)
                throws IOException {
        this.targetHost = target;
        return super.createLayeredSocket(socket, target, port, context);
    }
    @Override
    protected void prepareSocket(SSLSocket socket) throws IOException {
        try {
            PropertyUtils.setProperty(socket, "host", this.targetHost);
        } catch (Exception ex) {
        }
        super.prepareSocket(socket);
    }
    @Override
    public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) 
        throws IOException {
        if (socket instanceof SSLSocket) {
            try {
                PropertyUtils.setProperty(socket, "host", host.getHostName());
            } catch (NoSuchMethodException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {
        }
    }
    return super.connectSocket(connectTimeout, socket, host, remoteAddress,
                    localAddress, context);
    }
};

我错过了什么?

最佳答案

我也遇到了同样的问题。这个自定义 SSLConnectionSocketFactory 为我修复了它:

public class TrustAllSslSocketFactory extends SSLConnectionSocketFactory {

    static final HostnameVerifier ALL_HOSTNAME_VERIFIER = (String hostname, SSLSession session) -> true;

    public TrustAllSslSocketFactory(SSLContext sslContext) {
        super(sslContext, ALL_HOSTNAME_VERIFIER);
    }

    @Override
    protected void prepareSocket(SSLSocket socket) throws IOException {
                String hostName = socket.getInetAddress().getHostName();
        SNIHostName sniHostName = new SNIHostName(hostName);
        List serverNames = new ArrayList(1);
        serverNames.add(sniHostName);
        SSLParameters sslParameters = socket.getSSLParameters();
        sslParameters.setServerNames(serverNames);
        socket.setSSLParameters(sslParameters);
    }


}

参见http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SNIExamples

关于java - 当服务器使用服务器名称指示(SNI)扩展时,如何使用 HttpClient 4.5.2 和 JDK 1.8 获取 HTTPS 网页资源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38807435/

相关文章:

java - 如何使用Logback以JSON方式登录?

spring - 如何保护个人金融网络应用程序?

java - 在java中查找服务器响应时间

android - 不能从静态上下文中引用非静态方法

cxf - 动态调用 Web 服务的简便方法(无需 JDK 或代理类)

java - 如何将循环中的值放入多数组

java - 在 Java 中,堆内存与系统内存

wcf - 在 IIS 上通过 WCF 服务启用 HTTPS

apache - 在 Apache 反向代理后面使用 SSL 的 JIRA 服务器无法正常工作

java - 如何使用同一个客户端发送多个请求?