4

我正在使用 Apache HttpClient 在某个 url 上发送 https POST。

HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);

我得到:

javax.net.ssl.SSLException: hostname in certificate didn't match: <*.*.*.*> != <*.url 

现在经过搜索,我在stackoverflow上找到了解决方案:

HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
HttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

POST 已成功完成。

但我不明白这里发生了什么!我的连接仍然安全吗?这是正确的解决方案吗?如果没有,最好的解决方案是什么?

4

2 回答 2

3

如果您没有验证主机名,那么您根本就没有检查您是否正在与您打算与之交谈的实体交谈:它可能是一个 MITM。例如,这与使用 Curl 禁用 VERIFYHOST的问题相同。您可能也对 Security.SE 上的这个问题感兴趣。

关于您最初的问题,证书中的主机名(或 IP 地址)需要与您打算联系的主机名匹配,即 URL 中的主机名。如果您使用的是 IP 地址,则该 IP 地址需要位于证书的主题备用名称中。(请参阅此问题。)通常,使用名称而不是 IP 地址更容易,即使在 LAN 上也是如此。

编辑:考虑到您使用的是 Apache Http Client 4.0.2,4.0.3 的发行说明说:

这是一个紧急版本,修复了 SSL 连接管理代码中的关键回归。HttpClient 4.0.2 版本包括对多宿主主机的改进支持,不幸的是,它有一个错误导致默认 SSL 主机名验证逻辑失败。尝试与 HttpClient 4.0.2 建立 SSL 连接可能会导致 javax.net.ssl.SSLException: "hostname in certificate didn't match ..." 错误。

于 2013-03-19T11:55:43.970 回答
1

重要 - 如果您允许所有主机(即禁用主机名验证),那么它肯定不安全。你不应该在生产中这样做。

我认为您可能正在使用自签名证书,这可能是此异常的根本原因。如果是,请创建一个主机为 localhost(或您的 IP)的证书,然后尝试。在生产中,只需启用主机名验证,您的代码就会运行良好。

于 2013-03-19T10:54:02.180 回答