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.
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:
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
des chiffrements disponibles sur le serveur (aucun redéploiement d'application requis).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!
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);
}