Android HTTPS(3) IOException: Hostname 解决方案

Common Problems with Hostname Verification

  As mentioned at the beginning of this article, there are two key parts to verifying an SSL connection. The first is to verify the certificate is from a trusted source, which was the focus of the previous section. The focus of this section is the second part: making sure the server you are talking to presents the right certificate. When it doesn't, you'll typically see an error like this:

java.io.IOException: Hostname 'example.com' was not verified
        at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
        at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
        at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
        at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
        at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
        at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)

  One reason this can happen is due to a server configuration error. The server is configured with a certificate that does not have a subject or subject alternative name fields that match the server you are trying to reach. It is possible to have one certificate be used with many different servers. For example, looking at the google.comcertificate with openssl s_client -connect google.com:443 | openssl x509 -text you can see that a subject that supports *.google.com but also subject alternative names for *.youtube.com*.android.com, and others. The error occurs only when the server name you are connecting to isn't listed by the certificate as acceptable.

  Unfortunately this can happen for another reason as well: virtual hosting. When sharing a server for more than one hostname with HTTP, the web server can tell from the HTTP/1.1 request which target hostname the client is looking for. Unfortunately this is complicated with HTTPS, because the server has to know which certificate to return before it sees the HTTP request. To address this problem, newer versions of SSL, specifically TLSv.1.0 and later, support Server Name Indication (SNI), which allows the SSL client to specify the intended hostname to the server so the proper certificate can be returned.

  Fortunately, HttpsURLConnection supports SNI since Android 2.3. Unfortunately, Apache HTTP Client does not, which is one of the many reasons we discourage its use. One workaround if you need to support Android 2.2 (and older) or Apache HTTP Client is to set up an alternative virtual host on a unique port so that it's unambiguous which server certificate to return.

  The more drastic alternative is to replace HostnameVerifier with one that uses not the hostname of your virtual host, but the one returned by the server by default.

  Caution: Replacing HostnameVerifier can be very dangerous if the other virtual host is not under your control, because a man-in-the-middle attack could direct traffic to another server without your knowledge.

  If you are still sure you want to override hostname verification, here is an example that replaces the verifier for a single URLConnection with one that still verifies that the hostname is at least on expected by the app:

 1 // Create an HostnameVerifier that hardwires the expected hostname.
 2 // Note that is different than the URL's hostname:
 3 // example.com versus example.org
 4 HostnameVerifier hostnameVerifier = new HostnameVerifier() {
 5     @Override
 6     public boolean verify(String hostname, SSLSession session) {
 7         HostnameVerifier hv =
 8             HttpsURLConnection.getDefaultHostnameVerifier();
 9         return hv.verify("example.com", session);
10     }
11 };
12 
13 // Tell the URLConnection to use our HostnameVerifier
14 URL url = new URL("https://example.org/");
15 HttpsURLConnection urlConnection =
16     (HttpsURLConnection)url.openConnection();
17 urlConnection.setHostnameVerifier(hostnameVerifier);
18 InputStream in = urlConnection.getInputStream();
19 copyInputStreamToOutputStream(in, System.out);

  But remember, if you find yourself replacing hostname verification, especially due to virtual hosting, it's still very dangerous if the other virtual host is not under your control and you should find an alternative hosting arrangement that avoids this issue.

 

转载于:https://www.cnblogs.com/sjjg/p/4705597.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值