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

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

问题描述

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

我在SO和其他地方进行了搜索,发现一些文章说JDK7和Apache HTTP组件现在都支持SNI.

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

相关SO文章:证书链不同HTTPSURLconnection和Apache(系统)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,如果没有SNI,它将返回具有错误CommonName的过期证书,由于该证书,它会被拒绝.

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

我正在使用httpclient v 4.3.5

任何帮助表示赞赏.

谢谢.

解决方案

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

调试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天全站免登陆