Les protocoles SSL/TLS et suites de chiffrement avec la AndroidHttpClient

EDIT: toutes mes Excuses si mon message original a été mal formulé. Il conduit à une certaine confusion, représentée par les commentaires du post original. Donc, laissez-moi essayer encore une fois:

J'ai commencé avec une question. Je voulais résoudre un problème sur Android, mais ne sais pas comment. J'ai passé beaucoup de temps à chercher sur le net pour trouver des solutions, mais n'a pas une seule discussion de la question à n'importe où. Néanmoins, un certain nombre de discussions, y compris StackOverflow fils, m'a conduit à une technique qui semble prometteur. J'ai résolu le problème. Mais la solution a été un peu impliquées. J'ai donc décidé de poster la question ici, en pensant a) il doit y avoir une meilleure solution, et j'espère que quelqu'un saura et après la answere ici; ou b) peut-être que c'est une bonne solution, et depuis je n'ai trouvé aucune discussion de la question à n'importe où ailleurs sur le net, peut-être que ma solution au problème serait utile à d'autres d'essayer de faire la même chose. De toute façon, le résultat serait une contribution nouvelle à StackOverflow: une question qui ne fait pas d'ailleurs, avec, éventuellement, la réponse correcte d'une façon ou de l'autre. En fait, StackOverflow même m'a invité à répondre à ma propre question, de partager mes connaissances quand j'ai posté. C'était, en fait, une partie de ma motivation. Même les faits de cette affaire ne sont pas collectées de partout que j'ai trouvé.

Donc:

Q. Lors de l'utilisation de la AndroidHttpClient faire REPOSER les demandes via HTTPS, comment puis-je spécifier les protocoles SSL et les algorithmes à utiliser?

C'est important. Le point est bien pris qu'il ya beaucoup qui peut être fait sur le serveur, mais il y a des limites. Le même serveur doit servir de navigateurs, y compris les anciens, ainsi que les autres clients. Cela signifie que le serveur doit prendre en charge une large gamme de protocoles et d'algorithmes. Même au sein d'Android, si vous avez à l'appui de beaucoup de versions différentes, vous allez avoir à soutenir un certain nombre de différents protocoles et les algorithmes.

Plus important encore, par défaut, OpenSSL honneurs le client de chiffrement de préférence, pas le serveur, à l'occasion de la négociation SSL. Voir ce post, par exemple, qui dit que vous pouvez remplacer ce comportement dans le client par la mise en SSL_OP_CIPHER_SERVER_PREFERENCE. Ce n'est pas tout à fait clair si cette option peut même être réglé sur une SSLSocket en Java. Même si c'est possible, vous pouvez définir l'algorithme de chiffrement de la liste vous-même ou de dire à votre client pour l'honneur de la liste du serveur. Sinon, vous obtenez la Android par défaut, quel qu'il soit pour la version que vous utilisez (pas la version que vous lien contre).

Si vous prenez les valeurs par défaut, la liste de préférence envoyé par le client au serveur par un Jellybean 4.2+ client peut être vu ici, en commençant autour de la ligne 504. La liste par défaut des protocoles commence autour de la ligne 620. Si Jellybean 4.2+ inclut le support pour OpenSSL 1.0.1, en particulier TLSv1.1 et TLSv1.2, ces protocoles ne sont pas activés par défaut. Si vous ne faites pas quelque chose comme ce que j'ai fait, vous ne pouvez pas profiter de TLSv1.2, malgré le fait que le soutien pour TLSv1.2 est annoncé sur les dernières versions d'Android. Et les détails varient un peu comme vous revenir dans les précédentes versions d'Android. Au moins, vous pouvez prendre un coup d'oeil de près les paramètres par défaut sur toutes les versions prises en charge et de voir à ce que votre client est en train de faire. Vous pourriez être surpris.

Il ya beaucoup plus que vous pouvez dire sur la prise en charge pour les différents protocoles et les algorithmes. Le point est, il peut parfois être nécessaire de modifier ces paramètres dans un client.

A. Utiliser un SSLSocketFactory

Cela a bien fonctionné pour moi, et en fin de compte, il n'a pas beaucoup de code. Mais il était un peu épineux pour un couple de raisons:

  • Il existe deux types de SSLSocketFactory classes. Le client a besoin d'une
    org.apache.http.conn.le protocole ssl.SSLSocketFactory, mais OpenSSL renvoie une
    javax.net.le protocole ssl.SSLSocketFactory. C'est certainement de la confusion. J'ai utilisé
    délégation de faire un appel à l'autre, sans trop de problème.
  • Regarder la différence entre la OpenSSLContextImpl et la
    SSLContextImpl. Un juste encapsule les autres, mais ils ne sont pas
    interchangeables. Quand j'ai utilisé l'
    SSLContextImpl.engineGetSocketFactory méthode—je oublier ce qu'est exactement
    qui s'est passé, mais quelque chose tranquillement a échoué. Assurez-vous d'utiliser un
    OpenSSLContextImpl pour obtenir votre prise en usine, pas un SSLContextImpl.
    Vous pourriez également être en mesure d'utiliser
    javax.net.le protocole ssl.SSLSocketFactory.getDefault(), mais je ne suis pas sûr.
  • Vous ne pouvez pas facilement sous-classe AndroidHttpClient, parce que son constructeur
    privé. C'est malheureux, car il fournit quelques autres belles
    goodies, comme s'assurer que vous l'arrêter correctement au lieu de
    des fuites de ressources. Le DefaultHttpClient fonctionne très bien. J'ai emprunté
    la méthode newInstance de AndroidHttpClient (autour de la ligne 105).

Les points clés:

public class SecureSocketFactory extends SSLSocketFactory {
    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        //order should not matter here; the server should select the highest
        //one from this list that it supports
        s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1" });

        //order matters here; specify in preference order
        s.setEnabledCipherSuites(new String[] { "ECDHE-RSA-RC4-SHA", "RC4-SHA" });

Alors:

//when creating client
HttpParams params;
SchemeRegistry schemeRegistry = new SchemeRegistry();

//use custom socket factory for https
SSLSocketFactory sf = new SecureSocketFactory();
schemeRegistry.register(new Scheme("https", sf, 443));

//use the default for http
schemeRegistry.register(new Scheme("http",
            PlainSocketFactory.getSocketFactory(), 80));

ClientConnectionManager manager =
            new ThreadSafeClientConnManager(params, schemeRegistry);

HttpClient client = new DefaultHttpClient(manager, params);

Ci-dessous Android 3.0 (Honeycomb/SDK 11), le chiffrement de choix plus limité, et il y a moins de motivation pour remplacer les valeurs par défaut. Sur FROYO/SDK 8, mon SecureSocketFactory coups pour une certaine raison, et le jury est sur 10. Mais il semble fonctionner pour les 11 à la hausse tout simplement parfait.

La solution est dans un lieu public github repo.

Une autre solution pourrait être d'utiliser un HttpsUrlConnection, ce qui le rend facile à utiliser un socket personnalisé usine, mais j'imagine que vous aurez probablement perdre encore plus de la commodité d'un haut niveau client HTTP. Je n'ai pas d'expérience avec HttpsUrlConnection.

Stackoverflow n'est pas vraiment destiné aux discussions, reportez-vous à cette citation de la page du centre d'aide: If your motivation for asking the question is “I would like to participate in a discussion about ______”, then you should not be asking here. Sous "Quels sont les types de questions devrais-je éviter de demander?"
La motivation pour la poser, la question est d'apprendre la bonne réponse. Désolé si vous avez mal compris.
Je ne peut certainement pas prétendre savoir ce qui motive votre a été. mais ce "but I think StackOverflow is a better place for this discussion." permet de s'assurer de paraître comme vous êtes la suite d'une discussion. Par les définitions figurant sur la page d'aide votre message est hors-sujet pour. Je peux comprendre que c'est une discussion à avoir, mais comme il est maintenant, ce n'est vraiment pas un endroit approprié pour que ça arrive.
La Question est certainement hors sujet, mais si vous avez le serveur de fin de droit, vous n'avez pas à faire quoi que ce soit le client final.
EJP - pourquoi voudriez-vous que le serveur est correctement configuré? Je ne peux pas vous dire combien de fois j'ai entendu "nous suivons les meilleures pratiques", et puis savoir qu'ils ne prennent pas en charge la renégociation sécurisée, ne prennent pas en charge le protocole TLS 1.2, utilisez une clé de 512 bits, autoriser les connexions anonymes protocoles, l'utilisation d'exportation de qualité algorithmes, etc. Ubuntu 12.024 LTS est un brillant exemple - pas de TLS 1.2, car son à l'aide d'OpenSSL 1.0.0 (et tant que support à long terme). openssl s_client et wireshark sont tattle tales 🙂

OriginalL'auteur Jimmy Dee | 2013-08-30