java.io.IOException: Hostname was not verified [duplicate]
Asked Answered
C

9

44

I am trying to connect to a URL from a my Android app in Andorid Version 4.1.1, and I get the error indicated in the Title of my question, but when I tried to connect the same URL from Andorid Version 4.0.4 or 3.1, all works fine.

The Code fragment :

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

But here i clear one thing is that "Maybe certificate is that self-signed certificates and is not including them in a KeyStore.

I do not understand why this excepton occure only in Android Verison 4.1.1 OS Thanks.

FULL STACK TRACE

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)                                
Chlor answered 31/1, 2013 at 6:32 Comment(4)
What is the URL? http or https? domain name or id address?Hsu
do you have multiple virtual hosts with different certificates? I am currently seeing this problem in 4.1.1 and 4.1.2 - I think because the server I am dealing with has multiple virtual hosts serving https traffic and possibly because of issues with Android and SNI - see also this bug code.google.com/p/android/issues/detail?id=36599Umbelliferous
I suffered this problem few day ago,Chlor
My application can not download some file in Android 4.1.1 OS, but same code working in other Android OS(3.x, 4.0.x, 4.1.2). And I got solution, In my app one method trustAllHosts() do trust the certificate using TrustManager[] class. In this function I added 'HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY);' And DO_NOT_VERIFY is object of HostnameVerifier class final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } };Chlor
R
65

In case you are running with certificates that doesn't mean anything and you want to bypass them you also need to add a null host name verifier to make this code work

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

And the code for the host:

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

}

This needs to run once, but if you are making changes to your connection object you might need to run it again.

Renounce answered 6/3, 2013 at 16:2 Comment(3)
what is NullX509TrustManager it's display error so.Hydrargyrum
Yes, it is worked.. But when i publish the apk in playstore reject the app with below message "Your app(s) are using an unsafe implementation of the HostnameVerifier interface".Amontillado
How to set this on OkHttp?Inclinometer
F
20

In addition to @Noam's answer, this is a complete example:

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

Hope it helps

Fassett answered 19/10, 2014 at 22:29 Comment(2)
Its very bad to dsiable all SSL checking, its better to strip SSL completely and use HTTP, since you hardly have any security this way.Rhiana
Works. Thanks Matias, valued hack to get by with dev unsigned certs,Monitorial
A
12

This might happen because the CN (Common Name) you have declared on your SSL does not mach the actual URL you are sending your HTTP request too.

If so, create a new SSL and enter the currect CN. That should fix the problem.

Aflcio answered 4/11, 2014 at 15:21 Comment(3)
Is there anyway to solve this without creating a new ssl cert? I want to maintain the cert, but i can only use the currently different hostname. Perhaps there is a way to configure the server to serve that?Meninges
From making a fast google search seems like it is not possible to change the cn after the certificate was issude. Take a look at this support.globalsign.com/customer/portal/articles/…Aflcio
Wrong CN, because the stupid keytool asks for "what is your first name and last name" but it should be "what the * is your host name". see https://mcmap.net/q/375038/-keytool-set-hostnameViridescent
M
9

Please note SSL Certificate work only by Domain not work by IP address.

if you use IP ,insert below code

HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
        {
            @Override
            public boolean verify(String hostname, SSLSession session)
            {
                if(hostname.equals("127.0.0.1"))
                     return true;
            }
        });
Memoried answered 15/5, 2018 at 13:52 Comment(1)
Note that in the emulator the localhost is 10.0.2.2.Apps
U
7

I experienced this problem in 4.1.1 and 4.1.2, using HTTPSUrlConnection.

After some poking around I discovered that it was because the Apache server I am dealing with has multiple virtual hosts serving https traffic, resulting in SNI issues in Android - at least prior to JellyBean (I have unconfirmed reports that it was working in JB).

In my case there were 3 virtual hosts serving https traffic:

  • mydomain.com
  • api.mydomain.com (the one I was trying to deal with)
  • admin.mydomain.com

Probing api.* with openssl_client like this:

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

... always returned the root domain's certificate - buried in the output was something like:

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

... specifying the server name in the openssl_client command-line:

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

... returned the certificate I was expecting to see:

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

I was able to resolve the problem by moving the root domain virtual-host to a different physical host.

It seems that the Android HostnameVerifier can live with multiple sub-domain's side-by-side as virtual hosts, but having the root domain as a virtual-host in the same apache caused issues.

I am not a sys-admin/dev-ops and so it is possible that there are Apache config options that could have resolved the problem that I am not aware of.

Umbelliferous answered 8/7, 2013 at 17:13 Comment(0)
P
1

Android can't set up SSL connection, I suppose. Maybe your certificate for other host name, not the one you establish connection to. Read docs here and here.

Punish answered 31/1, 2013 at 6:44 Comment(2)
I already set up SSL connection but here i clear one thing is that "Maybe certificate is that self-signed certificates and is not including them in a KeyStore.Chlor
android will not accept self-signed sertificates in any android version without proper coding. You can sniff traffic and see what went wrong during handshake. And test it on different android versions. Maybe it will help you to solve your problem.Punish
O
1

This works better for me --> CHANGING StrictHostnameVerifier()

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

Example

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

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

Use Example 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);
Ostraw answered 19/9, 2018 at 17:1 Comment(0)
P
0

From Amazon documentation: Bucket Restrictions

"When using virtual hosted–style buckets with SSL, the SSL wild card certificate only matches buckets that do not contain periods. To work around this, use HTTP or write your own certificate verification logic."

The easiest way seems to create a unique bucket name without periods:

Instead of "bucketname.mycompany.com", something like "bucketnamemycompany" or any other DNS-compliant bucket name.

Prokopyevsk answered 8/10, 2015 at 21:36 Comment(0)
L
0

In Kotlin:

fun HttpsURLConnection.trustCert() {
            try {
                //Accepts every hostname
                this.hostnameVerifier = HostnameVerifier { hostname, _ ->
                    println(hostname) //To be hardcoded/as needed
                    true
                }
                val trustMgr:Array<TrustManager> = arrayOf(object : X509TrustManager {
                    override fun checkClientTrusted(certs: Array<out X509Certificate>?, authType: String?) {}
                    override fun checkServerTrusted(certs: Array<out X509Certificate>?, authType: String?) {}
                    override fun getAcceptedIssuers(): Array<X509Certificate>? = null
                })
                this.sslSocketFactory = SSLContext.getInstance("TLS").also {
                    it.init(null, trustMgr, SecureRandom())
                }.socketFactory
            } catch (e: Exception) {
                prinntln("SSL self-signed certificate processing error due to ${e.message}")
            }
        }

Usage:

val conn = URL(Uri.Builder().also { 
    it.scheme("https")
    it.encodedAuthority("$serverIp:$serverPort")
}.build().toString()).openConnection() as HttpsURLConnection
conn.trustCert()
val respCode = conn.responseCode
if(respCode == 200) {
    //do something (eg: read inputStream)
}
Linton answered 17/11, 2020 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.