web-dev-qa-db-fra.com

Erreur "Aucun certificat homologue" dans Android 2.3 mais PAS dans 4

Obtenir le "javax.net.ssl.SSLPeerUnverifiedException: No peer certificate error" dans un émulateur fonctionnant sous Android 2.3 mais PAS dans 4. Dans 4, cela fonctionne parfaitement. J'essaie de me connecter à un serveur en direct via https. Il utilise un certificat Thawte valide, fonctionne correctement dans tous les navigateurs et Android 3 et 4.

Si quelqu'un a l'aide du code, SVP et merci. En outre, si quelqu'un a des suggestions sur une solution de contournement sécurisée, je l'apprécierais. J'apprends encore et je suis sur ce problème depuis une semaine. Cela doit cesser pour que je puisse continuer à travailler et à apprendre. Urgh.

Voici le code HttpCLient, avec la permission de Antoine Hauck ( http://blog.antoine.li/2010/10/22/Android-trusting-ssl-certificates/ ):

 import Java.io.InputStream;
    import Java.security.KeyStore;
    import Java.security.cert.CertificateException;

    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    import javax.security.cert.X509Certificate;

    import org.Apache.http.conn.ClientConnectionManager;
    import org.Apache.http.conn.scheme.PlainSocketFactory;
    import org.Apache.http.conn.scheme.Scheme;
    import org.Apache.http.conn.scheme.SchemeRegistry;
    import org.Apache.http.conn.ssl.SSLSocketFactory;
    import org.Apache.http.impl.client.DefaultHttpClient;
    import org.Apache.http.impl.conn.SingleClientConnManager;

    import Android.content.Context;

    public class MyHttpClient extends DefaultHttpClient {

    final Context context;

    public MyHttpClient(Context context) {
        this.context = context;
    }

    @Override
    protected ClientConnectionManager createClientConnectionManager() {
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        // Register for port 443 our SSLSocketFactory with our keystore
        // to the ConnectionManager
        registry.register(new Scheme("https", newSslSocketFactory(), 443));
        return new SingleClientConnManager(getParams(), registry);
    }

    private SSLSocketFactory newSslSocketFactory() {
         try {
             // Get an instance of the Bouncy Castle KeyStore format
             KeyStore trusted = KeyStore.getInstance("BKS");
             // Get the raw resource, which contains the keystore with
             // your trusted certificates (root and any intermediate certs)
             InputStream in = context.getResources().openRawResource(R.raw.my_cert);
             try {
                 // Initialize the keystore with the provided trusted certificates
                 // Also provide the password of the keystore
                 trusted.load(in, "my_pass".toCharArray());
             } finally {
                 in.close();
             }

            // Pass the keystore to the SSLSocketFactory. The factory is responsible
            // for the verification of the server certificate.
            SSLSocketFactory sf = new SSLSocketFactory(trusted);
            // Hostname verification from certificate
            // http://hc.Apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

Et voici le code qui l'instancie:

DefaultHttpClient client = new MyHttpClient(getApplicationContext());

           HttpPost post = new HttpPost(server_login_url);
           List <NameValuePair> parameters = new ArrayList <NameValuePair>();
           parameters.add(new BasicNameValuePair("username", user));
           parameters.add(new BasicNameValuePair("password", pass));

            try {
               post.setEntity(new UrlEncodedFormEntity(parameters, HTTP.UTF_8));
            } catch (UnsupportedEncodingException e2) {
                // TODO Auto-generated catch block
                Log.d(DEBUG_TAG, "in  UnsupportedEncodingException - " + e2.getMessage());
                e2.printStackTrace();
            }
                // Execute the GET call and obtain the response
           HttpResponse getResponse = null;

            try {
                getResponse = client.execute(post);
            } catch (ClientProtocolException e) {
                // TODO Auto-generated catch block
                // Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show();
                Log.d(DEBUG_TAG, "in ClientProtocolException - " + e.getMessage());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                // Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show();
                Log.d(DEBUG_TAG, "in  client.execute IOException - " + e.getMessage());
                e.printStackTrace();
            }

L'erreur est interceptée dans le bloc IOException. Voici la pile:

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
org.Apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.Java:258)
org.Apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.Java:93)
org.Apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.Java:381)
org.Apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.Java:164)
org.Apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.Java:164)
org.Apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.Java:119)
org.Apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.Java:359)
org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:555)
org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:487)
org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:465)
org.ffb.tools.SplashActivity$LoginTask.makeConnection(SplashActivity.Java:506)
org.ffb.tools.SplashActivity$LoginTask.doLogin(SplashActivity.Java:451)
org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.Java:439)
org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.Java:1)
Android.os.AsyncTask$2.call(AsyncTask.Java:185)
Java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.Java:306)
Java.util.concurrent.FutureTask.run(FutureTask.Java:138)
Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1088)
Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:581)
Java.lang.Thread.run(Thread.Java:1019)

Voici l'ordre de la chaîne (à partir de la commande openssl):

La chaîne a l'air bien, je pense.

    i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
  1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
  i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized      use only/CN=thawte Primary Root CA
  2 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For      authorized use only/CN=thawte Primary Root CA
  i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services      Division/CN=Thawte Premium Server CA/[email protected]
22
user1214401

Ce thread était vraiment utile lorsque j'ai débogué un problème similaire.

Liste de contrôle récapitulative Android 2.3 HTTPS/SSL:

  • Si votre autorité de certification se trouve dans la liste 2.3 d'Android d'Android - et que Thawte l'est - il n'est pas nécessaire d'inclure le certificat dans l'application.
  • Android 2.3 ne prend pas en charge Indication du nom du serveur si votre serveur utilise ce protocole pour établir une liaison SSL, Android risque de ne pas recevoir les certificats que vous attendez.
  • Avez-vous une chaîne de certificats installée sur le serveur et est-elle commandée correctement? La plupart des navigateurs traitent des chaînes de certificats hors service, contrairement à Android 2.3. La réponse de bdc dans le fil de discussion que j'ai mentionné ci-dessus explique comment vérifier la validité de votre certificat SSL et de sa chaîne avec "openssl s_client -connect yourserver.com:443".
  • Lorsque vous déterrez votre ancien appareil 2.3 dans le tiroir du bas, assurez-vous que la date et l'heure sont correctement réglées après une impuissance prolongée.
28
Ed Holzwarth

J'ai eu exactement le même problème que vous. Tout fonctionnait bien avec Android> 3.X, mais lorsque j'ai essayé avec certains appareils 2.3.X (mais pas tous!), J'ai obtenu cette fameuse exception "Aucune erreur de certificat d'homologue".

J'ai beaucoup creusé dans stackoverflow et d'autres blogs, mais je n'ai rien trouvé qui fonctionne sur ces périphériques "non autorisés" (dans mon cas: utilisation correcte du fichier de clés certifiées; pas de sni requis; ordre correct des chaînes de certificats sur le serveur; etc ...) .

Il semblerait que l'Apache HttpClient d'Android ne soit que ne fonctionne pas correctement sur certains périphériques 2.3.X. L'exception "aucun certificat homologue" se produisait trop tôt pour atteindre même un code de vérificateur de nom d'hôte personnalisé, des solutions telles que celle-là ne fonctionnaient pas pour moi.

Voici mon code:

KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream is = this.getAssets().open("discretio.bks");
trustStore.load(is, "discretio".toCharArray());
is.close();

SSLSocketFactory sockfacto = new SSLSocketFactory(trustStore);
sockfacto.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);

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

SingleClientConnManager mgr = new SingleClientConnManager(httpParameters, schemeRegistry);

HttpClient client = new DefaultHttpClient(mgr, httpParameters);
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);

J'ai donc tout réécrit en utilisant javax.net.ssl.HttpsURLConnection et maintenant cela fonctionne sur tous les périphériques que j'ai testés (de la version 2.3.3 à la version 4.X).

Voici mon nouveau code:

KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream is = this.getAssets().open("discretio.bks");
trustStore.load(is, "discretio".toCharArray());
is.close();

TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

URL request = new URL(url);
HttpsURLConnection urlConnection = (HttpsURLConnection) request.openConnection();

//ensure that we are using a StrictHostnameVerifier
urlConnection.setHostnameVerifier(new StrictHostnameVerifier());
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setConnectTimeout(15000);

InputStream in = urlConnection.getInputStream();
//I don't want to change my function's return type (laziness) so I'm building an HttpResponse
BasicHttpEntity res = new BasicHttpEntity();
res.setContent(in);
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, urlConnection.getResponseCode(), "");
resp.setEntity(res);

La validation de la chaîne de certificats et du nom d’hôte fonctionne (je les ai testés). Si quelqu'un souhaite mieux examiner le changement, voici un diff

Les commentaires sont les bienvenus, j'espère que cela aidera certaines personnes.

3
Nikita Kozlov

Une autre source de ce message peut être un paramètre de date/heure non valide, par exemple. lors de l'utilisation d'un appareil éteint depuis quelques mois. Assez trivial, mais il peut être difficile à repérer.

1
mvds

La logique de vérification de certificat (ou plus précisément de création de chaîne) dans (au moins) Android 2.3 est défectueuse.

Voici ce que j'ai observé:

  • Si le serveur TLS de sa chaîne de certificats ne fournit que le certificat du serveur (non auto-signé ou auto-signé), vous pouvez alors mettre le certificat du serveur dans le magasin de clés et la vérification aboutira.

  • Si le serveur TLS de sa chaîne de certificats fournit également un certificat d'autorité de certification intermédiaire, vous ne devez placer que le certificat d'autorité de certification racine dans le magasin de clés et vous assurer qu'il ne contient PAS les certificats d'autorité de certification serveur et intermédiaire (sinon la vérification échouera de manière aléatoire).

  • Si le serveur TLS de sa chaîne de certificats fournit des certificats d'autorité de certification intermédiaire et racine dans le bon ordre, vous devez simplement vous assurer que le certificat d'autorité de certification racine est dans le magasin de clés (peu importe si les certificats d'autorité de certification serveur et intermédiaire sont présents).

Par conséquent, la manière "correcte/fiable" de gérer cela consiste à inclure dans le magasin de clés uniquement les certificats de l'autorité de certification racine et la configuration du serveur responsable du "Aucun certificat homologue" - dans le cas où la chaîne de certificats du serveur ne fournit pas de certificats d'autorité de certification intermédiaires. . Vous pouvez tester le serveur en utilisant https://www.ssllabs.com/ssltest/ .

0
FaST4

Quels certificats chargez-vous depuis R.raw.my_cert? Cette erreur indique soit un serveur mal configuré que vous n'utilisez pas les autorités de certification intermédiaires principales et secondaires de Thawte - ou _ vous ne chargez pas et ne faites pas confiance à la chaîne de certificats correcte.

0
Brian Dupuis