【问题标题】:How to enable SNI in HTTP request using Apache HTTPComponents HttpClient?如何使用 Apache HTTPComponents HttpClient 在 HTTP 请求中启用 SNI?
【发布时间】:2014-11-09 08:42:57
【问题描述】:

我正在尝试弄清楚如何将成功的 HTTP GET 请求发送到需要 SNI 的服务器。

在SO等地方搜了一下,发现有文章说JDK7现在支持SNI,还有Apache HTTP Components。

https://issues.apache.org/jira/browse/HTTPCLIENT-1119 https://wiki.apache.org/HttpComponents/SNISupport

相关SO文章:Certificate chain different between HTTPSURLconnection and Apache (System) DefaultHttpClient

--

但是,我似乎找不到任何说明如何使其工作的文档。

这是我正在使用的代码...


            KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
            String trustedCertsPath = System.getenv("JAVA_HOME") + "/jre/lib/security/cacerts";
            FileInputStream certstream = new FileInputStream(new File(trustedCertsPath));
            try {
                trustStore.load(certstream, "changeit".toCharArray());
            } finally {
                certstream.close();
            }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
                .build();

        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        CloseableHttpClient httpclient2 = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();

        CloseableHttpClient httpclient = HttpClients.createDefault();

        HttpGet httpget = new HttpGet(uri);
        CloseableHttpResponse response = httpclient.execute(httpget);
        try {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                tempFile = File.createTempFile(httpFile.getTempFilePrefix(), httpFile.getTempFilePosfix());
                FileOutputStream os = new FileOutputStream(tempFile);

                InputStream instream = entity.getContent();
                try {
                    IOUtils.copy(instream, os);
                } finally {
                    try { instream.close(); } catch (Exception e) {}
                    try { os.close(); } catch (Exception e) {}
                }
            }
        } finally {
            response.close();
        }

当我运行它时,请求失败。

服务器在请求中需要 SNI,如果没有它,它会返回一个具有错误 CommonName 的过期证书,因此会被拒绝。

如果我使用 httpclient2 实例,即使用自定义 SSL 上下文设置以允许所有 hTTP 证书,则请求成功。但是,我不想在每天针对不同主机进行大量下载的服务器上启用此功能。

我正在使用 httpclient v 4.3.5

任何帮助表示赞赏。

谢谢。

【问题讨论】:

    标签: apache-httpclient-4.x sni


    【解决方案1】:

    在 Java 1.7 或更高版本上运行时,SNI 应该完全透明地工作。无需配置。如果由于某种原因与启用了 SNI 的服务器的 SSL 握手失败,您应该能够通过打开 SSL 调试日志来找出原因(以及是否正确使用了 SNI 扩展),如此处 [1] 和此处 [2] 所述

    “调试 SSL/TLS 连接”可能看起来已经过时,但在解决 SSL 相关问题时仍然很有用。

    [1]http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/ReadDebug.html

    [2]http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#Debug

    【讨论】:

    • 我通过 maven 运行代码。即使 java7 jre 在路径上并启用,maven 仍在选择旧的 jre6。不知道为什么。当我将代码隔离到一个单独的类中并通过 jre7 运行它时,它起作用了。
    • 如果有人在 Java7 中偶然发现了这个问题。确保没有人设置System.setProperty("jsse.enableSNIExtension", "false");。这需要删除或设置为 true。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 1970-01-01
    • 2023-03-04
    • 2011-12-20
    相关资源
    最近更新 更多