java.io.IOException的:主机名未经过验证(java.io.IOException:

2019-07-19 01:45发布

我试图连接到在的Andorid版本4.1.1我的Android应用程序的URL,而我得到了我的问题的标题所示的错误,但是当我试图对同一URL的Andorid版本4.0.4或3.1连接,一切工作正常。

代码片断:

    try {
        .
        .
        .
        URL url = new URL(urlStr);
        Log.i(TAG,"[ URL ] " + urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        int size = conn.getContentLength();
        int responsecode = conn.getResponseCode();
        Log.d(TAG, "Responsecode: " + responsecode);
        .
        .
        .
        } catch (Exception e) {
        e.printStackTrace();
        }


private static void trustAllHosts() {

        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return new java.security.cert.X509Certificate[] {};
            }

            public void checkClientTrusted(X509Certificate[] chain,
                            String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain,
                            String authType) throws CertificateException {
            }
        } };

        try {
                SSLContext sc = SSLContext.getInstance("TLS");
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
                HttpsURLConnection
                                .setDefaultSSLSocketFactory(sc.getSocketFactory());
        } catch (Exception e) {
                System.out.println("IOException : HTTPSRequest::trustAllHosts");
                e.printStackTrace();
        }
    }

但在这里我清楚的一件事是,“也许证书是自签名证书,不包括他们在一个密钥库中。

我不明白为什么这excepton中的Android 4.1.1 Verison OS只感谢occure。

FULL堆栈跟踪

01-31 10:26:08.348: W/System.err(3158): java.io.IOException: Hostname <URL> was not verified
01-31 10:26:08.348: W/System.err(3158):     at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
01-31 10:26:08.348: W/System.err(3158):     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
01-31 10:26:08.348: W/System.err(3158):     at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:289)
01-31 10:26:08.348: W/System.err(3158):     at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:239)
01-31 10:26:08.348: W/System.err(3158):     at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:273)
01-31 10:26:08.348: W/System.err(3158):     at libcore.net.http.HttpURLConnectionImpl.getHeaderField(HttpURLConnectionImpl.java:130)
01-31 10:26:08.348: W/System.err(3158):     at java.net.URLConnection.getHeaderFieldInt(URLConnection.java:544)
01-31 10:26:08.348: W/System.err(3158):     at java.net.URLConnection.getContentLength(URLConnection.java:316)
01-31 10:26:08.348: W/System.err(3158):     at libcore.net.http.HttpsURLConnectionImpl.getContentLength(HttpsURLConnectionImpl.java:191)
01-31 10:26:08.348: W/System.err(3158):     at com.ih.util.HelpVideoServices$downloadTask.run(HelpVideoServices.java:172)                                

Answer 1:

如果您正在使用证书运行并不意味着什么,你想绕过他们,你还需要添加一个空主机名验证,以使此代码工作

HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new NullX509TrustManager()}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

而对于主机的代码:

import javax.net.ssl.HostnameVerifier ;
import javax.net.ssl.SSLSession;

public class NullHostNameVerifier implements HostnameVerifier {

    @Override   
    public boolean verify(String hostname, SSLSession session) {
        Log.i("RestUtilImpl", "Approving certificate for " + hostname);
        return true;
    }

}

这需要运行一次,但如果你正在改变你的连接对象,您可能需要再次运行。



Answer 2:

除了@诺姆的答案,这是一个完整的例子:

/**
 * Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
 * aid testing on a local box, not for use on production.
 */
private static void disableSSLCertificateChecking() {
    TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {

            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
                // not implemented
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
                // not implemented
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

        }
    };

    try {

        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }

        });
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
}

希望能帮助到你



Answer 3:

这可能发生,因为你有你的SSL声明的CN(通用名)不马赫你太发送您的HTTP请求的实际URL。

如果是这样,创建一个新的SSL和进入当期的CN。 这应该解决这个问题。



Answer 4:

我在4.1.1和4.1.2遇到过这个问题,使用HttpsURLConnection的。

之后,我周围的一些戳发现,那是因为我处理与Apache服务器有多个虚拟主机服务HTTPS流量,导致SNI Android中的问题-之前,豆形软糖,至少(我有未经证实的报告说,它在JB工作)。

在我的情况有服务HTTPS流量3个虚拟主机:

  • mydomain.com
  • api.mydomain.com(一个我试图处理)
  • admin.mydomain.com

探测API *像这样openssl_client:

openssl s_client -debug -connect api.mydomain.com:443

...总是返回根域的证书 - 埋在输出是这样的:

Certificate chain
 0 s:/OU=Domain Control Validated/CN=mydomain.com
 ...

......在openssl_client命令行中指定的服务器名称:

openssl s_client -debug -servername api.mydomain.com -connect api.mydomain.com:443

......回到我期待看到证书:

Certificate chain
 0 s:/OU=Domain Control Validated/CN=api.mydomain.com

我可以通过根域名虚拟主机移动到不同的物理主机来解决问题。

看来,在Android的HostnameVerifier可以与多个子域的并排侧虚拟主机活着,但具有域在同一个Apache虚拟主机造成的问题。

我不是一个SYS管理员的/ dev-OPS,所以它可能是有一些可能已经解决了,我不知道这个问题的Apache配置选项。



Answer 5:

请注意:SSL证书只能由域工作不是由IP地址的工作。

如果你使用的IP,将以下代码

HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
        {
            @Override
            public boolean verify(String hostname, SSLSession session)
            {
                if(hostname.equals("127.0.0.1"))
                     return true;
            }
        });


Answer 6:

安卓不能建立SSL连接,我想。 也许你对其他主机名的证书,而不是一个你建立连接。 阅读文档这里和这里 。



Answer 7:

它可能是你的问题是你的网址是通过“https”开头解决。 你必须将所有字符串的URL转换为“HTTP”,它会工作。

编辑:

SchemeRegistry schemeRegistry = new SchemeRegistry ();

schemeRegistry.register (new Scheme ("http",
    PlainSocketFactory.getSocketFactory (), 80));
schemeRegistry.register (new Scheme ("https",
    new CustomSSLSocketFactory (), 443));

ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager (
    params, schemeRegistry);

return new DefaultHttpClient (cm, params);

CustomSSLSocketFactory:

public class CustomSSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory
{
    private SSLSocketFactory FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory ();

    public CustomSSLSocketFactory ()
    {
        super(null);
        try
        {
            SSLContext context = SSLContext.getInstance ("TLS");
            TrustManager[] tm = new TrustManager[] { new FullX509TrustManager () };
            context.init (null, tm, new SecureRandom ());

            FACTORY = context.getSocketFactory ();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public Socket createSocket() throws IOException
    {
        return FACTORY.createSocket();
    }

    // TODO: add other methods like createSocket() and getDefaultCipherSuites().
    // Hint: they all just make a call to member FACTORY 
}

FullX509TrustManager是实现javax.net.ssl.X509TrustManager一类,却没有一个方法实际执行任何工作,[这里]获得的样本[1]。

祝好运!



Answer 8:

这对我更好地工作 - >更改StrictHostnameVerifier()

https://developer.android.com/reference/org/apache/http/conn/ssl/StrictHostnameVerifier

    HostnameVerifier hostnameVerifier = new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        HostnameVerifier hv = new StrictHostnameVerifier();

        return hv.verify("example.com", session);
       }
    };

使用例https://developer.android.com/training/articles/security-ssl#java

    // Tell the URLConnection to use our HostnameVerifier
    URL url = new URL("https://example.org/");
    HttpsURLConnection urlConnection = 
   (HttpsURLConnection)url.openConnection();
   urlConnection.setHostnameVerifier(hostnameVerifier);


Answer 9:

从亚马逊的文档: 斗限制

“当使用虚拟托管式水桶SSL中,SSL通配符证书只匹配不包含句桶。要解决此问题,使用HTTP或写自己的证书验证的逻辑。”

最简单的方法似乎没有时间来创造一个独特的斗名称:

相反,“bucketname.mycompany.com”的,像“bucketnamemycompany”或其它任何符合DNS-bucket名称。



文章来源: java.io.IOException: Hostname was not verified
标签: android http