2013-08-30 10 views
8

EDYCJA: Przepraszam, jeśli mój pierwotny wpis był źle sformułowany. Doprowadziło to do pewnego zamieszania, reprezentowanego przez komentarze do oryginalnego postu. Pozwól mi spróbować ponownie:Protokoły i zestawy szyfrów SSL/TLS z AndroidHttpClient

Zacząłem od pytania. Chciałem rozwiązać problem na Androida, ale nie wiedziałem jak. Spędziłem dużo czasu na szukaniu rozwiązań, ale nie znalazłem ani jednej dyskusji na ten temat. Niemniej jednak wiele dyskusji, w tym wątki StackOverflow, doprowadziło mnie do techniki, która wyglądała obiecująco. Rozwiązałem problem. Ale rozwiązanie było trochę zaangażowane. Postanowiłem więc zadać to pytanie, myśląc: a) musi istnieć lepsze rozwiązanie, i mam nadzieję, że ktoś będzie znał i zamieszczał odpowiedź tutaj; lub b) może to dobre rozwiązanie, a ponieważ nie znalazłem nigdzie indziej dyskusji w tej sprawie, być może moje rozwiązanie tego problemu przydałoby się innym próbującym zrobić to samo. Tak czy inaczej, wynik byłby nowym wkładem do StackOverflow: pytanie, na które nie ma odpowiedzi w innym miejscu, z, ewentualnie, poprawną odpowiedzią w ten czy w inny sposób. W istocie StackOverflow zaprosił mnie nawet do odpowiedzi na moje własne pytanie, dzieląc się moją wiedzą, kiedy ją pierwotnie opublikowałem. To w rzeczywistości było częścią mojej motywacji. Nawet fakty w tej sprawie nie są zbierane nigdzie, co znalazłem.

Więc:

Q. Przy użyciu AndroidHttpClient do żądań REST przez HTTPS, w jaki sposób mogę określić, które protokoły SSL i szyfrów w użyciu?

To jest ważne. Chodzi o to, że na serwerze można wiele zrobić, ale są granice. Ten sam serwer musi obsługiwać przeglądarki, w tym stare, a także innych klientów. Oznacza to, że serwer musi obsługiwać szeroki zakres protokołów i szyfrów. Nawet w systemie Android, jeśli musisz obsługiwać wiele różnych wersji, będziesz musiał obsługiwać wiele różnych protokołów i szyfrów.

Co ważniejsze, domyślnie OpenSSL honoruje preferencje szyfru klienta, , a nie na serwerze podczas uzgadniania SSL. Zobacz na przykład: post, która mówi, że można zastąpić to zachowanie w kliencie, ustawiając SSL_OP_CIPHER_SERVER_PREFERENCE. Nie jest całkowicie jasne, czy ta opcja może być ustawiona na SSLSocket w Javie. Nawet jeśli tak, możesz ustawić listę szyfrów samodzielnie lub powiedzieć klientowi, aby uszanował listę serwerów. W przeciwnym razie uzyskujesz wartość domyślną Androida, niezależnie od wersji, z której korzystasz (a nie wersji, z którą łączysz się).

Jeśli wybierzesz ustawienia domyślne, lista preferencji wysłana przez klienta do serwera przez klienta Jellybean 4.2+ będzie widoczna jako here, począwszy od linii 504. Domyślna lista protokołów zaczyna się wokół linii 620. Chociaż Jellybean 4.2+ obejmuje obsługę OpenSSL 1.0.1, w szczególności TLSv1.1 i TLSv1.2, protokoły te nie są domyślnie włączone. Jeśli nie zrobisz czegoś podobnego, co zrobiłem, nie możesz skorzystać z obsługi TLSv1.2, mimo że obsługa TLSv1.2 jest reklamowana w najnowszych wersjach Androida. Szczegóły różnią się nieco od poprzednich wersji Androida. Przynajmniej możesz przyjrzeć się wartościom domyślnym we wszystkich obsługiwanych wersjach i zobaczyć, co faktycznie robi twój klient. Możesz być zaskoczony.

Istnieje wiele innych informacji na temat obsługi różnych protokołów i szyfrów. Chodzi o to, że czasami może być konieczna zmiana tych ustawień w kliencie.

A. użyć niestandardowego SSLSocketFactory

Ten pracował dobrze dla mnie, a na koniec, to nie było bardzo dużo kodu.Ale było trochę cierniste z kilku powodów:

  • Istnieją dwie różne klasy SSLSocketFactory. Klient potrzebuje org.apache.http.conn.ssl.SSLSocketFactory, ale OpenSSL zwraca javax.net.ssl.SSLSocketFactory. To jest zdecydowanie mylące. Użyłem delegacji , aby jedno połączenie było drugie, bez większego problemu.
  • Uważaj na różnicę między OpenSSLContextImpl a SSLContextImpl. Jeden tylko owija drugi, ale nie są wymienne. Kiedy użyłem metody SSLContextImpl.engineGetSocketFactory - zapomniałem, co dokładnie stało się , ale coś po cichu zawiodło. Należy użyć OpenSSLContextImpl, aby uzyskać fabrykę gniazda, a nie SSLContextImpl. Możesz również móc użyć javax.net.ssl.SSLSocketFactory.getDefault(), ale nie jestem pewien.
  • Nie można łatwo podklasować AndroidHttpClient, ponieważ jego konstruktorem jest prywatny. To niefortunne, ponieważ zapewnia inne fajne gadżety, takie jak upewnienie się, że zamknąłeś je prawidłowo, zamiast przeciekać zasoby . DefaultHttpClient działa dobrze. Pożyczyłem metodę newInstance z AndroidHttpClient (około linii 105).

Kluczowe punkty:

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

wówczas:

// 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); 

Poniżej Android 3.0 (Honeycomb/SDK 11), wspierane wybory szyfrów stają się bardziej ograniczone, a tam mniej motywacji do ręcznego wartości domyślne. Na FROYO/SDK 8, mój SecureSocketFactory z jakiegoś powodu wysadza się w powietrze, a jury wychodzi na 10. Ale wygląda na to, że działa od 11 wzwyż.

Pełne rozwiązanie znajduje się w publicznym githubie repo.

Innym rozwiązaniem może być użycie HttpsUrlConnection, co ułatwia korzystanie z niestandardowej fabryki gniazd, ale wyobrażam sobie, że prawdopodobnie stracisz jeszcze więcej wygody klienta HTTP wysokiego poziomu. Nie mam żadnego doświadczenia z HttpsUrlConnection.

+1

Stackoverflow nie jest przeznaczony do dyskusji. Odwołaj się do cytatu z [strona centrum pomocy] (http://stackoverflow.com/help): "Jeśli twoja motywacja do zadawania pytania brzmi:" Chciałbym wziąć udział w dyskusji o ______ ", wtedy nie powinieneś pytać tutaj." W sekcji "Jakich pytań powinienem unikać?" sekcja – FoamyGuy

+1

Motywacją do postawienia pytania jest poznanie właściwej odpowiedzi. Przepraszam, jeśli źle to zrozumiałeś. –

+1

Z pewnością nie mogę twierdzić, że wiem, jaka była Twoja motywacja. ale to "" ale myślę, że StackOverflow jest lepszym miejscem do tej dyskusji. "" Oczywiście sprawia, że ​​wydaje się, jakbyś po dyskusji.Przez definicje wymienione na [stronie pomocy] (http://stackoverflow.com/help/dont-ask) twój post jest nie na temat dla SO. Mogę docenić, że jest to dyskusja warta posiadania, ale jak to teraz jest, to naprawdę nie jest odpowiednie miejsce, aby to się stało. – FoamyGuy

Odpowiedz

1

Podczas korzystania z AndroidHttpClient do tworzenia żądań REST za pośrednictwem protokołu HTTPS, w jaki sposób mogę określić, które protokoły SSL i szyfry mają być używane?

Nie wierzę, że można to zrobić za pomocą AndroidHttpClient. Wszystko, co zrobiłem, aby wzmocnić kanał (takie jak listy szyfrów, przypinanie certyfikatów i przypinanie do kluczy publicznych) wymagało gdzieś niestandardowej klasy, niezależnie od tego, czy była to SSLSocketFactory czy X509TrustManager. To jest Java i to jest Android. Zobacz How to override the cipherlist sent to the server by Android when using HttpsURLConnection?.

+0

Masz rację, noloader. Ale byłem zdecydowanie zdezorientowany. EJP było poprawne, ponieważ jeśli ustawisz serwer, aby używał własnych preferencji, kolejność określona przez klienta nie jest ważna. Nie jest to ustawienie domyślne dla klientów lub serwerów openssl, ale jest to powszechna praktyka. To rozwiązało większość moich problemów. Najważniejszą rzeczą, jakiej się nauczyłem, jest to, że nie otrzymasz domyślnie OpenSSL 1.0.1 (a więc i wsparcia dla TLSv1.1 lub nowszego) na Androida 4.1 lub nowszym, gdzie jest on dostępny, bez niestandardowego kodu, co najmniej jeśli używasz AndroidHttpClient. –

+0

Jimmy - "nie dostaniesz OpenSSL 1.0.1 ... jeśli używasz AndroidHttpClient". Tak, Android jest całkowicie zakorkowany. Używają niższej wersji OpenSSL (chyba 0.9.8). Plus, oni wkręcili swoją listę szyfrów ("RC4-MD5", jak sądzę). Został odkorkowany od wersji Androida 2.3. Zobacz na przykład [Dlaczego Androida SSL została obniżona z AES256-SHA do RC4-MD5 pod koniec 2010 r.] (Http://op-co.de/blog/posts/android_ssl_downgrade/). – jww

+0

Jimmy - Jeśli chodzi o wybór 'RC4-SHA': nie rób tego. Bernstein, AlFardan i inni pokazali, jak złamane 'RC4' jest w TLS. Nic nie może zrobić Ron ani RSA. Plus, ataki BEAST (wyskakujące wyrocznie od 2011 r.), Które spowodowały użycie 'RC4' (mniejszego zła) zostały naprawione. IETF ma swoje zbiorowe głowy w swoich osłów. Zobacz na przykład [Na temat bezpieczeństwa RC4 w TLS i WPA] (http://cr.yp.to/streamciphers/rc4biases-20130708.pdf). – jww

Powiązane problemy