2013-04-30 23 views
11

Podczas negocjowania TLS klienci wysyłają listę obsługiwanych szyfrów do serwera, serwer wybiera jeden i rozpoczyna szyfrowanie. Chcę zmienić listę szyfrowaną wysyłaną na serwer przez Androida, gdy używam komunikacji HttpsURLConnection.Jak zastąpić listę szyfrowaną wysyłaną na serwer przez system Android podczas korzystania z funkcji HttpsURLConnection?

Wiem, że mogę użyć obiektu setSSLSocketFactory na obiekcie HttpsURLConnection, aby skonfigurować go do korzystania z SSLSocketFactory. Jest to przydatne, gdy chcę zmienić menedżera zaufania itp. Używanego przez SSLSocket zwróconego przez SSLSocketFactory.

Wiem, że na ogół tę listę szyfrów można edytować przy użyciu obiektu SSLParameters i przekazywać do obiektów SSlsocket lub SSLEngine przy użyciu metod, które udostępniają.

ALE SSLSocketFactory wydaje się nie eksponować takich metod!

nie mogę znaleźć sposób na zmianę SSLParameters zwróconych SSLSocket obiektów stworzonych przez SSLSocketFactory I przechodzą do HttpsURLConnection.

Co robić?

Podejrzewam, że dotyczy to również Javy, nie tylko Androida. Może istnieje sposób OO (np. Rozszerzenie SSLSocketFactory i dostarczenie go do HttpsURLConnection?)

Odpowiedz

13

Ten kawałek kodu jest nieco surowy. proszę używać z ostrożnością.

public class PreferredCipherSuiteSSLSocketFactory extends SSLSocketFactory { 


private static final String PREFERRED_CIPHER_SUITE = "TLS_RSA_WITH_AES_128_CBC_SHA"; 

private final SSLSocketFactory delegate; 

public PreferredCipherSuiteSSLSocketFactory(SSLSocketFactory delegate) { 

    this.delegate = delegate; 
} 

@Override 
public String[] getDefaultCipherSuites() { 

    return setupPreferredDefaultCipherSuites(this.delegate); 
} 

@Override 
public String[] getSupportedCipherSuites() { 

    return setupPreferredSupportedCipherSuites(this.delegate); 
} 

@Override 
public Socket createSocket(String arg0, int arg1) throws IOException, 
     UnknownHostException { 

    Socket socket = this.delegate.createSocket(arg0, arg1); 
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate); 
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites); 

    return socket; 
} 

@Override 
public Socket createSocket(InetAddress arg0, int arg1) throws IOException { 

    Socket socket = this.delegate.createSocket(arg0, arg1); 
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate); 
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites); 

    return socket; 
} 

@Override 
public Socket createSocket(Socket arg0, String arg1, int arg2, boolean arg3) 
     throws IOException { 

    Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3); 
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate); 
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites); 

    return socket; 
} 

@Override 
public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3) 
     throws IOException, UnknownHostException { 

    Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3); 
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate); 
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites); 

    return socket; 
} 

@Override 
public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, 
     int arg3) throws IOException { 

    Socket socket = this.delegate.createSocket(arg0, arg1, arg2, arg3); 
    String[] cipherSuites = setupPreferredDefaultCipherSuites(delegate); 
    ((SSLSocket)socket).setEnabledCipherSuites(cipherSuites); 

    return socket; 
} 

private static String[] setupPreferredDefaultCipherSuites(SSLSocketFactory sslSocketFactory) { 

    String[] defaultCipherSuites = sslSocketFactory.getDefaultCipherSuites(); 

    ArrayList<String> suitesList = new ArrayList<String>(Arrays.asList(defaultCipherSuites)); 
    suitesList.remove(PREFERRED_CIPHER_SUITE); 
    suitesList.add(0, PREFERRED_CIPHER_SUITE); 

    return suitesList.toArray(new String[suitesList.size()]); 
} 

private static String[] setupPreferredSupportedCipherSuites(SSLSocketFactory sslSocketFactory) { 

    String[] supportedCipherSuites = sslSocketFactory.getSupportedCipherSuites(); 

    ArrayList<String> suitesList = new ArrayList<String>(Arrays.asList(supportedCipherSuites)); 
    suitesList.remove(PREFERRED_CIPHER_SUITE); 
    suitesList.add(0, PREFERRED_CIPHER_SUITE); 

    return suitesList.toArray(new String[suitesList.size()]); 
} 
} 

Jeśli chcesz z niego korzystać.

  HttpsURLConnection connection = (HttpsURLConnection) (new URL(url)) 
       .openConnection(); 
     SSLContext context = SSLContext.getInstance("TLS"); 
     TrustManager tm[] = {new SSLPinningTrustManager()}; 
     context.init(null, tm, null); 
     SSLSocketFactory preferredCipherSuiteSSLSocketFactory = new PreferredCipherSuiteSSLSocketFactory(context.getSocketFactory()); 
     connection.setSSLSocketFactory(preferredCipherSuiteSSLSocketFactory); 
        connection.connect(); 

Dzięki.

+0

Mała precyzja: to jest javax.net.ssl.SSLSocketFactory, a nie apatche SSLSocketFactory. –

+1

Co to jest SSLPinningTrustManager? to "SSLPinningTrustManager" jest klasą zdefiniowaną przez użytkownika, ponieważ Android Studio nie jest w stanie rozwiązać, jeśli tak, to co może zastąpić? – Amritesh

+0

@Arritesh może "SSLPinningTrustManager" odnosi się do [tej implementacji] (https://github.com/duladissa/SSLPinnigExample/blob/master/app/src/main/java/com/litedreamz/sslpinnigexample/util/SSLPinningTrustManager.java) –

3

Dołączyłem technikę w odpowiedzi @ ThinkChris na 1 do martwego, prostego wywołania metody. Możesz skorzystać z biblioteki NetCipher, aby uzyskać nowoczesną konfigurację TLS podczas korzystania z Androida HttpsURLConnection. NetCipher konfiguruje instancję HttpsURLConnection, aby korzystać z najlepiej obsługiwanej wersji TLS, usuwa obsługę SSLv3 i konfiguruje najlepszy pakiet szyfrów dla tej wersji TLS. Po pierwsze, dodaj go do swojej build.gradle:

compile 'info.guardianproject.netcipher:netcipher:1.2' 

Albo można pobrać netcipher-1.2.jar i umieścić go bezpośrednio w aplikacji. Wtedy zamiast dzwonić:

HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection(); 

nazywają to:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl); 

Jeśli chcesz specjalnie dostosować tę listę szyfr, można sprawdzić kod tam. Ale większość ludzi nie powinna myśleć o liście szyfrów, zamiast tego powinna używać domyślnie najlepszych praktyk.

-1

Ten kod zadziwił nieoczekiwanym javax.net.ssl.SSLHandshakeException.

Aktualizacja do jdk1.8.0_92 i plików zasad dotyczących nieograniczonej siły Oracle JCE nie pomogły, a ja nie udało mi się zastosować konkretnej SSLParameters do HttpsUrlConnection.

W szczególności próby użycia HttpsUrlConnection czytać https://www.adrbnymellon.com wyniki w następujący błąd:

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

Ta strona działało OK przed około 4/15/2016, a następnie rozpoczęła się niepowodzeniem. Sądzę, że niepowodzenie spowodowane jest przez przerwanie obsługi strony internetowej dla SSLv2Hello i SSLv3 z powodu usterki DROWN. Zobacz this dla wspaniałej analizy.

Dostęp do strony internetowej rozpoczął pracę przez modyfikację kodu ze zmianami do zaledwie 2 stałych:

private static final String PREFERRED_CIPHER_SUITE = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; 
SSLContext context = SSLContext.getInstance("TLSv1.2"); 

Mam nadzieję, że to pomaga kogoś innego.