2013-04-30 83 views
11

在TLS協商期間,客戶端將支持的密碼列表發送到服務器,服務器選擇一個,然後開始加密。當我使用HttpsURLConnection進行通信時,我想更改由Android發送給服務器的此密碼列表。如何覆蓋Android使用HttpsURLConnection時發送到服務器的密碼列表?

我知道我可以在HttpsURLConnection對象上使用setSSLSocketFactory將其設置爲使用SSLSocketFactory。當我想更改由SSLSocketFactory返回的SSLSocket所使用的信任管理者等時,這非常有用。

我知道一般這個密碼組列表可以使用SSLParameters對象,並使用他們提供的方法將它傳遞給SSlsocketSSLEngine對象進行編輯。

但是SSLSocketFactory似乎沒有公開這樣的方法!

我不能找到一種方法來改變由SSLSocketFactory我傳遞給HttpsURLConnection創建返回SSLSocket對象的SSLParameters

怎麼辦?

我想這也與一般的Java相關,不僅僅是Android。也許有OO的方式來做到這一點(例如擴展SSLSocketFactory並提供到HttpsURLConnection?)

回答

13

這段代碼有點粗糙。請小心使用。

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

當你想使用它。

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

謝謝你。

+0

小精度:這是javax.net.ssl.SSLSocketFactory而不是apatche SSLSocketFactory。 – 2015-03-19 16:44:17

+1

什麼是SSLPinningTrustManager?是「SSLPinningTrustManager」是用戶定義的類,因爲Android Studio無法解決,如果是的話,那麼有什麼可以替代? – Amritesh 2016-06-20 11:28:02

+0

@Amritesh也許'SSLPinningTrustManager'引用[this implementation](https://github.com/duladissa/SSLPinnigExample/blob/master/app/src/main/java/com/litedreamz/sslpinnigexample/util/SSLPinningTrustManager.java) – 2017-09-13 18:09:05

3

我將@ThinkChris的回答1中的技術捆綁到一個簡單的方法調用中。使用Android的HttpsURLConnection時,您可以使用NetCipher庫來獲得現代TLS配置。 NetCipher將HttpsURLConnection實例配置爲使用支持最好的TLS版本,刪除SSLv3支持,併爲該TLS版本配置最佳密碼套件。首先,它添加到您的的build.gradle

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

或者,你可以直接在你的應用程序下載netcipher-1.2.jar和包括它。然後,而不是調用:

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

調用此:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl); 

如果您想特別定製密碼列表,你可以有檢查代碼。但大多數人不應該考慮密碼列表,而應該默認使用常見的最佳實踐。

-1

此代碼爲意想不到的javax.net.ssl.SSLHandshakeException創造了奇蹟。

升級到jdk1.8.0_92和Oracle JCE無限強度策略文件沒有幫助,我試圖將具體的SSLParameters應用於HttpsUrlConnection未成功。

尤其是試圖使用HttpsUrlConnection以下錯誤讀取https://www.adrbnymellon.com結果:

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

本網站之前,大約2016年4月15日工作確定,然後開始失敗。我相信失敗是由於DROWN漏洞造成的,因此網站停止支持SSLv2HelloSSLv3。請參閱this以獲得最佳分析。

訪問網站開始通過修改代碼修改,只是2常數工作:

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

我希望這可以幫助別人。

相關問題