web-dev-qa-db-fra.com

java.io.IOException: le nom d'hôte n'a pas été vérifié

J'essaie de me connecter à une URL à partir d'une application my Android dans la version 4.1.1 d'Andorid, et j'obtiens l'erreur indiquée dans le titre de ma question, mais lorsque j'ai essayé de me connecter URL d'Andorid version 4.0.4 ou 3.1, tout fonctionne bien.

Le fragment de code:

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

Mais ici, je clarifie une chose est que "Peut-être que le certificat est que les certificats auto-signés et ne les inclut pas dans un magasin de clés.

Je ne comprends pas pourquoi cette exception ne se produit que dans Android Verison 4.1.1 OS Merci.

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)                                
40
Palak

Dans le cas où vous utilisez des certificats qui ne veulent rien dire et que vous souhaitez les contourner, vous devez également ajouter un vérificateur de nom d'hôte nul pour faire fonctionner ce code

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

Et le code de l'hôte:

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

}

Cela doit être exécuté une fois, mais si vous apportez des modifications à votre objet de connexion, vous devrez peut-être le réexécuter.

59
Noam

En plus de la réponse de @ Noam, voici un exemple complet:

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

J'espère que ça aide

20
Matias Elorriaga

Cela peut se produire car le CN (nom commun) que vous avez déclaré sur votre SSL ne correspond pas à l'URL réelle à laquelle vous envoyez votre demande HTTP.

Si c'est le cas, créez un nouveau SSL et entrez le CN actuel. Cela devrait résoudre le problème.

10
Yaki Klein

J'ai rencontré ce problème dans 4.1.1 et 4.1.2, en utilisant HTTPSUrlConnection.

Après quelques fouilles, j'ai découvert que c'était parce que le serveur Apache avec lequel j'ai affaire a plusieurs hôtes virtuels servant du trafic https, ce qui entraîne SNI problèmes dans Android - au moins avant JellyBean (j'ai des rapports non confirmés indiquant que cela fonctionnait dans JB).

Dans mon cas, il y avait 3 hôtes virtuels servant du trafic https:

  • mydomain.com
  • api.mydomain.com (celui avec lequel j'essayais de faire face)
  • admin.mydomain.com

Sonder l'api. * Avec openssl_client comme ceci:

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

... renvoyait toujours le certificat du domaine racine - enterré dans la sortie était quelque chose comme:

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

... en spécifiant le nom du serveur dans la ligne de commande openssl_client:

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

... a renvoyé le certificat que je m'attendais à voir:

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

J'ai pu résoudre le problème en déplaçant l'hôte virtuel du domaine racine vers un hôte physique différent.

Il semble que le Android HostnameVerifier peut vivre avec plusieurs sous-domaines côte à côte en tant qu'hôtes virtuels, mais ayant la racine domaine en tant qu'hôte virtuel dans le même problème causé par Apache.

Je ne suis pas un administrateur système/dev-op et il est donc possible qu'il existe des options de configuration Apache qui auraient pu résoudre le problème que je ne connais pas.

6
Stevie

Veuillez noter que le certificat SSL fonctionne uniquement par domaine et ne fonctionne pas par adresse IP.

si vous utilisez IP, insérez le code ci-dessous

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

Android ne peut pas configurer de connexion SSL, je suppose. Peut-être votre certificat pour un autre nom d'hôte, pas celui auquel vous établissez la connexion. Lisez les documents ici et ici .

2
Leonidos

Cela fonctionne mieux pour moi -> CHANGER StrictHostnameVerifier ()

https://developer.Android.com/reference/org/Apache/http/conn/ssl/StrictHostnameVerifier

Exemple

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

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

Exemple d'utilisation 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);
1
user3826696

il est possible que votre problème soit celui de la résolution de votre URL via "https". vous devez convertir toutes les URL de chaîne en "http" et cela fonctionnera.

MODIFIER:

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 est une classe qui implémente javax.net.ssl.X509TrustManager, mais aucune des méthodes n'effectue réellement de travail, obtenez un exemple [ici] [1].

Bonne chance!

1
jlopez

À partir de la documentation Amazon: Restrictions du compartiment

"Lorsque vous utilisez des compartiments de type hébergé virtuel avec SSL, le certificat générique SSL correspond uniquement aux compartiments qui ne contiennent pas de points. Pour contourner ce problème, utilisez HTTP ou écrivez votre propre logique de vérification de certificat."

La manière la plus simple semble créer un nom de compartiment unique sans points:

Au lieu de "bucketname.mycompany.com", quelque chose comme "bucketnamemycompany" ou tout autre nom de compartiment compatible DNS.

0
Julian