2013-04-30 24 views
11

Durante la negoziazione TLS, i client inviano un elenco di crittografi supportati al server, il server ne sceglie uno e inizia la crittografia. Voglio cambiare questo cifrario inviato al server da Android, quando sto usando HttpsURLConnection per la comunicazione.Come sovrascrivere il cifrario inviato al server da Android quando si utilizza HttpsURLConnection?

So che posso usare setSSLSocketFactory sull'oggetto HttpsURLConnection per configurarlo per utilizzare un SSLSocketFactory. Ciò è utile quando voglio cambiare il trustmanager ecc utilizzato dallo SSLSocket restituito dallo SSLSocketFactory.

So che in generale questa lista ciphersuite può essere modificato utilizzando un oggetto SSLParameters e passarlo al SSlsocket o SSLEngine oggetti utilizzando i metodi che forniscono.

MA il SSLSocketFactory non sembra esporre tali metodi!

non riesco a trovare un modo per cambiare la SSLParameters degli restituiti SSLSocket oggetti creati dal SSLSocketFactory passo a HttpsURLConnection.

Cosa fare?

Credo che questo è anche rilevante per Java in generale, non solo Android. Forse c'è un modo OO per farlo (ad esempio, estendere e fornire quello a HttpsURLConnection?)

risposta

13

Questo pezzo di codice è un po 'grezzo. si prega di usare con cura.

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

Quando si desidera utilizzarlo.

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

Grazie.

+0

piccola precisione: questo è javax.net.ssl.SSLSocketFactory e non apatche SSLSocketFactory. –

+1

Che cos'è SSLPinningTrustManager qui? "SSLPinningTrustManager" è una classe definita dall'utente poiché Android Studio non è in grado di risolverlo, in caso affermativo, quindi quale potrebbe essere la sostituzione? – Amritesh

+0

@Amritesh forse 'SSLPinningTrustManager' fa riferimento a [questa implementazione] (https://github.com/duladissa/SSLPinnigExample/blob/master/app/src/main/java/com/litedreamz/sslpinnigexample/util/SSLPinningTrustManager.java) –

3

ho impacchettato la tecnica di @ ThinkChris risposta 1 in una semplice chiamata al metodo morti. È possibile utilizzare la libreria NetCipher per ottenere una configurazione TLS moderna quando si utilizza Android HttpsURLConnection. NetCipher configura l'istanza HttpsURLConnection per utilizzare la versione TLS supportata, rimuove il supporto SSLv3 e configura la migliore suite di crittografie per quella versione TLS. In primo luogo, aggiungerlo al tuo build.gradle:

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

Oppure si può scaricare il netcipher-1.2.jar e includerlo direttamente nella vostra app. Poi invece di chiamare:

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

chiamata questo:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl); 

Se si desidera personalizzare specificamente quella lista di cifratura, è possibile controllare il codice lì. Ma la maggior parte delle persone non dovrebbe pensare alla lista di codici, invece dovrebbe usare le migliori pratiche comuni per impostazione predefinita.

-1

Questo codice ha funzionato meraviglie per un inaspettato javax.net.ssl.SSLHandshakeException.

L'aggiornamento a jdk1.8.0_92 e ai file di politica di forza illimitata di Oracle JCE non ha aiutato e non è stato possibile tentare di applicare lo specifico SSLParameters allo HttpsUrlConnection.

In particolare, il tentativo di utilizzare HttpsUrlConnection per leggere https://www.adrbnymellon.com risultati il ​​seguente errore:

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

Questo sito funzionato bene prima su 2016/04/15, e poi ha iniziato fallendo. Credo che l'errore sia causato dal fatto che il sito Web interrompe il supporto per SSLv2Hello e SSLv3 a causa della vulnerabilità di DROWN. Vedi this per una grande analisi.

accesso al sito web ha iniziato a lavorare modificando il codice con i cambiamenti a soli 2 costanti:

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

Spero che questo aiuta a qualcun altro.