如何使用 Apache HTTPComponents HttpClient 在 HTTP 请求中启用 SNI? [英] How to enable SNI in HTTP request using Apache HTTPComponents HttpClient?

查看:77
本文介绍了如何使用 Apache HTTPComponents HttpClient 在 HTTP 请求中启用 SNI?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想弄清楚如何向需要 SNI 的服务器发送成功的 HTTP GET 请求.

我在SO等地方搜索,发现一些文章说JDK7现在支持SNI,还有Apache HTTP Components.

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

相关SO文章:证书链不同HTTPSURLconnection 和 Apache(系统) DefaultHttpClient

之间

--

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

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

<代码>KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());StringtrustedCertsPath = System.getenv("JAVA_HOME") + "/jre/lib/security/cacerts";FileInputStream certstream = new FileInputStream(new File(trustedCertsPath));尝试 {trustStore.load(certstream, "changeit".toCharArray());} 最后 {certstream.close();}

//信任自己的 CA 和所有自签名证书SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).建造();//仅允许 TLSv1 协议SSLConnectionSocketFactory sslsf = 新 SSLConnectionSocketFactory(ssl上下文,new String[] { "TLSv1" },空值,SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);CloseableHttpClient httpclient2 = HttpClients.custom().setSSLSocketFactory(sslsf).建造();CloseableHttpClient httpclient = HttpClients.createDefault();HttpGet httpget = new HttpGet(uri);CloseableHttpResponse 响应 = httpclient.execute(httpget);尝试 {HttpEntity entity = response.getEntity();如果(实体!= null){tempFile = File.createTempFile(httpFile.getTempFilePrefix(), httpFile.getTempFilePosfix());FileOutputStream os = new FileOutputStream(tempFile);InputStream instream = entity.getContent();尝试 {IOUtils.copy(instream, os);} 最后 {尝试 { instream.close();} catch (Exception e) {}尝试 { os.close();} catch (Exception e) {}}}} 最后 {response.close();}

当我运行这个时,请求失败.

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

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

我使用的是 httpclient v 4.3.5

感谢任何帮助.

谢谢.

解决方案

SNI 在 Java 1.7 或更高版本上运行时应该完全透明地工作.无需配置.如果出于某种原因与启用 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

I am trying to figure out how to send a successful HTTP GET request to a server requiring SNI.

I searched on SO and other places, and found some articles that said that SNI is now supported in JDK7, as well as Apache HTTP Components.

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

Relevant SO article: Certificate chain different between HTTPSURLconnection and Apache (System) DefaultHttpClient

--

However, I cannot seem to find any docs that show how to get this to work.

Here is the code I am using...


            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(); }

When I run this, the request fails.

The server requires SNI in the request, and without it, it returns an expired cert that has the wrong CommonName, due to which it gets rejected.

If I use the httpclient2 instance, that is setup using a custom SSL context to allow all hTTP certs, then the request succeeds. However, that is not something I want to enable on a server that does a lot of downloads per day against different hosts.

I am using httpclient v 4.3.5

Any help appreciated.

Thanks.

解决方案

SNI should work completely transparently when running on Java 1.7 or newer. No configuration is required. If for whatever reason SSL handshake with SNI enabled server fails you should be able to find out why (and whether or not the SNI extension was properly employed) by turning on SSL debug logging as described here [1] and here [2]

'Debugging SSL/TLS Connections' may look outdated but it can still be useful when troubleshooting SSL related issues.

[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

这篇关于如何使用 Apache HTTPComponents HttpClient 在 HTTP 请求中启用 SNI?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆