2013-04-30 14 views
11

Während der TLS-Aushandlung senden Clients eine Liste mit unterstützten Verschlüsselungscodes an den Server, der Server wählt eine aus und die Verschlüsselung wird gestartet. Ich möchte diese Verschlüsselungsliste ändern, die von Android an den Server gesendet wird, wenn ich HttpsURLConnection für die Kommunikation verwende.Wie überschreibt man die Verschlüsselungsliste, die von Android bei Verwendung von HttpsURLConnection an den Server gesendet wird?

Ich weiß, dass ich setSSLSocketFactory auf dem HttpsURLConnection Objekt verwenden kann, um es einzurichten, ein SSLSocketFactory zu verwenden. Dies ist nützlich, wenn ich den Trustmanager ändern möchte, der von SSLSocket verwendet wird, der von SSLSocketFactory zurückgegeben wird.

Ich weiß, dass im Allgemeinen diese Ciphersuite Liste bearbeitet werden kann mit einem SSLParameters Objekt und übergibt es an oder SSLEngine Objekte mit den Methoden, die sie bereitstellen.

ABER die SSLSocketFactory scheint solche Methoden nicht zu belichten!

Ich kann nicht einen Weg finden, die SSLParameters der zurück SSLSocket Objekte durch die SSLSocketFactory ich HttpsURLConnection Pass erstellt zu ändern.

Was ist zu tun?

Ich denke, das ist auch relevant für Java im Allgemeinen, nicht nur Android. Vielleicht gibt es einen OO Weg, es zu tun (z. B. extend SSLSocketFactory und das zu HttpsURLConnection?)

Antwort

13

Dieses Stück Code ist ein bisschen roh. Bitte verwenden Sie vorsichtig.

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()]); 
} 
} 

Wenn Sie wollen, es zu benutzen.

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

Vielen Dank.

+0

kleine Genauigkeit: Dies ist javax.net.ssl.SSLSocketFactory und nicht eine SSLSocketFactory. –

+1

Was ist SSLPinningTrustManager hier? ist "SSLPinningTrustManager" ist eine benutzerdefinierte Klasse wie Android Studio nicht in der Lage ist zu lösen, wenn ja, was könnte dann Ersatz sein? – Amritesh

+0

@Amritesh vielleicht 'SSLPinningTrustManager' bezieht sich auf [diese Implementierung] (https://github.com/duladissa/SSLPinnigExample/blob/master/app/src/main/java/com/litereamz/sslpinnigexample/util/SSLPinningTrustManager.java) –

3

Ich bündelte die Technik in @ ThinkChris Antwort 1 in einen toten einfachen Methodenaufruf. Sie können die Bibliothek NetCipher verwenden, um eine moderne TLS-Konfiguration zu erhalten, wenn Sie HttpsURLConnection von Android verwenden. NetCipher konfiguriert die HttpsURLConnection Instanz für die Verwendung der besten unterstützten TLS-Version, entfernt die SSLv3-Unterstützung und konfiguriert die beste Verschlüsselungsgruppe für diese TLS-Version. Zuerst fügen Sie es zu Ihrem build.gradle:

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

Oder können Sie die netcipher-1.2.jar und schließen Sie es direkt in der App herunterladen. Dann statt Aufruf:

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

Rufen Sie:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl); 

Wenn Sie wollen, um speziell diese Chiffre Liste anpassen, können Sie den Code dort überprüfen. Aber die meisten Leute sollten nicht über die Verschlüsselungsliste nachdenken müssen, stattdessen sollten sie die üblichen Best Practices standardmäßig verwenden.

-1

Dieser Code wirkte Wunder für eine unerwartete javax.net.ssl.SSLHandshakeException.

Aktualisierung auf jdk1.8.0_92 und Oracle JCE unbegrenzte Stärke Richtliniendateien hat nicht geholfen, und ich war nicht erfolgreich versuchen, spezifische SSLParameters auf die HttpsUrlConnection anzuwenden.

Insbesondere versucht HttpsUrlConnection zu verwenden https://www.adrbnymellon.com Ergebnisse in den folgenden Fehlern zu lesen:

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

Diese Webseite arbeitete OK vor etwa 2016.04.15, und begann dann scheitern. Ich glaube, dass der Fehler dadurch verursacht wird, dass die Website aufgrund der DROWN-Sicherheitslücke die Unterstützung für und SSLv3 beendet. Eine gute Analyse finden Sie unter this.

Zugriff auf die Website gestartet durch Arbeiten Sie den Code mit Änderungen nur 2 Konstanten ändern:

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

Ich hoffe, das jemand anderes hilft.