web-dev-qa-db-fra.com

HttpClient échoue avec la poignée de main a échoué dans Android 5.0 Lollipop

DefaultHttpClient dans Android 5.0 Lollipop semble être cassé. Il ne peut pas configurer la connexion à certains sites qui ont été définis avec succès par les versions précédentes d'Android.

Par exemple, j'essaie de me connecter à https://uralsg.megafon.ru

//Create httpclient like in https://stackoverflow.com/questions/18523784/ssl-tls-protocols-and-cipher-suites-with-the-androidhttpclient
HttpClient client = new DefaultHttpClient(manager, params);
HttpGet httpGet = new HttpGet("https://uralsg.megafon.ru");
HttpResponse client = httpclient.execute(httpGet);

Ce code fonctionne sous Android 2.3-4.4, mais échoue sous Android 5.0 (appareils et émulateur) avec une erreur de connexion fermée par l'homologue . Bien entendu, cela est compréhensible, car Android 5.0 tente de connecter cet ancien serveur avec TLSv1.2 et des chiffrements modernes. et cela ne les soutient pas.

Ok, en utilisant l'exemple de code dans Les protocoles SSL/TLS et les suites de chiffrement avec AndroidHttpClient , nous limitons le protocole et le chiffrement à TLSv1 et SSL_RSA_WITH_RC4_128_MD5. Maintenant, il échoue avec une erreur différente: 

javax.net.ssl.SSLHandshakeException: Handshake failed
caused by 
    error:140943FC:SSL routines:SSL3_READ_BYTES:sslv3 alert bad record mac 
    (external/openssl/ssl/s3_pkt.c:1286 0x7f74c1ef16e0:0x00000003) 
    at com.Android.org.conscrypt.NativeCrypto.SSL_do_handshake

Et bien sûr, ce code fonctionne bien sur Android 2.3-4.4.

J'ai examiné le trafic avec Wireshark:

302 4002.147873000  192.168.156.30  83.149.32.13    TLSv1   138 Client Hello
303 4002.185362000  83.149.32.13    192.168.156.30  TLSv1   133 Server Hello
304 4002.186700000  83.149.32.13    192.168.156.30  TLSv1   1244    Certificate
305 4002.186701000  83.149.32.13    192.168.156.30  TLSv1   63  Server Hello Done
307 4002.188117000  192.168.156.30  83.149.32.13    TLSv1   364 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
308 4002.240695000  83.149.32.13    192.168.156.30  TLSv1   61  Alert (Level: Fatal, Description: Bad Record MAC)

Vous pouvez voir que la connexion a été établie, mais le serveur a été alerté car il ne pouvait probablement pas décoder le message de prise de contact crypté.

Je n'ai pas réussi à me connecter à https://uralsg.megafon.ru en utilisant HttpClient sur Android 5.0. Stock navigateur ne le connecte pas si. Android 2.3-4.4 connecte ce site de quelque manière que ce soit sans aucune difficulté.

Existe-t-il un moyen de permettre à HttpClient de connecter de tels sites? Ce n’est qu’un exemple. Je suis certain que de nombreux serveurs hérités ne pourraient pas être connectés avec Android 5.0 et HttpClient.

15
Dmitry Kochin

mise à jour: il s’est avéré que c’est un bogue dans le back-end, pas Android 5}, bien que ce fût bien avec le chiffrement en question.

J'ai eu le même problème. Pour moi, c’est le chiffre TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 qui a été choisi parmi l’ensemble de chiffrements par défaut (mis à jour) d’Android 5.

Dès que je l'ai supprimé de la liste des chiffrements acceptables des clients, les connexions ont à nouveau fonctionné.

Le Journal des modifications Android 5 mentions:

  • Les suites de chiffrement AES-GCM (AEAD) sont maintenant activées.

Je suis à peu près sûr que c'est le coupable. Dès que TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 est préféré (par le serveur), la connexion échouera. 

Notez que TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 fonctionne.

Mon hypothèse est que l'implémentation de TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 dans Android est boguée ou celle du serveur avec lequel vous parlez.

Solutions:

  1. Supprimez TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 des chiffrements disponibles sur le serveur (aucun redéploiement d'application requis).
  2. Supprimez TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 de la liste des chiffrements proposés par le client (au cours du CLIENT_HELLO).

Vous pouvez le faire côté client en implémentant votre propre SSLSocketFactory et en appelant

sslSocket.setEnabledCipherSuites(String[] suites);

sur la création de SSLSocket.

_ {edit: Notez que ce n'est pas nécessairement un bug d'Android, il est possible que l'implémentation du serveur soit défectueuse. si votre problème est effectivement causé par le chiffrement, laissez un commentaire sur le gestionnaire de bogues Android] ( https://code.google.com/p/Android/issues/detail?id=81603 ). Je vous remercie!

5
stefs

J'ai essayé de changer les suites de chiffrement dans une fabrique de prises personnalisée, mais cela n'a pas aidé. Dans mon cas, j'ai dû supprimer les protocoles TLSv1.1 et TLSv1.2 des EnabledProtocols du socket. Il semble que certains serveurs plus anciens ne gèrent pas très bien la négociation de protocole pour les nouveaux protocoles. Il existe différents exemples de création d'une fabrique de sockets personnalisée, tels que Comment remplacer la liste de chiffrement envoyée au serveur par Android lorsque vous utilisez HttpsURLConnection? , et d'autres pour les sockets Apache. Ceci étant fait, je viens d'appeler la méthode AdjustSocket suivante pour supprimer les protocoles. 

private void AdjustSocket(Socket socket)
{
    String[] protocols = ((SSLSocket) socket).getSSLParameters().getProtocols();
    ArrayList<String> protocolList = new ArrayList<String>(Arrays.asList(protocols));

    for (int ii = protocolList.size() - 1; ii >= 0; --ii )
        {
        if ((protocolList.get(ii).contains("TLSv1.1")) || (protocolList.get(ii).contains("TLSv1.2")))
            protocolList.remove(ii);
        }

    protocols = protocolList.toArray(new String[protocolList.size()]);
    ((SSLSocket)socket).setEnabledProtocols(protocols);
}
0
Mike M