2013-04-30 33 views
11

Trong quá trình đàm phán TLS, khách hàng gửi danh sách mật mã được hỗ trợ đến máy chủ, máy chủ chọn một máy chủ và bắt đầu mã hóa. Tôi muốn thay đổi mật mã này được gửi tới máy chủ của Android, khi tôi đang sử dụng HttpsURLConnection để liên lạc.Làm thế nào để ghi đè lên bộ mã hóa được gửi tới máy chủ bằng Android khi sử dụng HttpsURLConnection?

Tôi biết rằng tôi có thể sử dụng setSSLSocketFactory trên đối tượng HttpsURLConnection để thiết lập để sử dụng SSLSocketFactory. Điều này rất hữu ích khi tôi muốn thay đổi máy chủ tin cậy, vv được sử dụng bởi SSLSocket được trả về bởi SSLSocketFactory.

Tôi biết rằng nói chung danh sách ciphersuite này có thể được chỉnh sửa bằng cách sử dụng đối tượng SSLParameters và chuyển đến đối tượng SSlsocket hoặc SSLEngine bằng cách sử dụng các phương pháp mà chúng cung cấp.

NHƯNG SSLSocketFactory dường như không hiển thị các phương pháp như vậy!

Tôi không thể tìm cách thay đổi SSLParameters trong số các đối tượng được trả về SSLSocket được tạo bởi SSLSocketFactory Tôi chuyển đến HttpsURLConnection.

Việc cần làm?

Tôi đoán điều này cũng liên quan đến Java nói chung, không chỉ Android. Có thể có cách OO để thực hiện điều đó (ví dụ: mở rộng SSLSocketFactory và cung cấp cho HttpsURLConnection?)

Trả lời

13

Đoạn mã này hơi thô. hãy sử dụng cẩn thận.

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

Khi bạn muốn sử dụng.

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

Cảm ơn bạn.

+0

độ chính xác nhỏ: đây là javax.net.ssl.SSLSocketFactory và không apatche SSLSocketFactory. –

+1

SSLPinningTrustManager ở đây là gì? là "SSLPinningTrustManager" là lớp do người dùng định nghĩa là Android Studio không thể giải quyết, nếu có, thì điều gì có thể thay thế? – Amritesh

+0

@Amritesh có thể là "SSLPinningTrustManager' đề cập đến [triển khai này] (https://github.com/duladissa/SSLPinnigExample/blob/master/app/src/main/java/com/litedreamz/sslpinnigexample/util/SSLPinningTrustManager.java) –

3

Tôi đưa kỹ thuật vào câu trả lời của @ ThinkChris 1 vào cuộc gọi phương thức đơn giản đã chết. Bạn có thể sử dụng thư viện NetCipher để có cấu hình TLS hiện đại khi sử dụng HttpsURLConnection của Android. NetCipher cấu hình cá thể HttpsURLConnection để sử dụng phiên bản TLS được hỗ trợ tốt nhất, loại bỏ hỗ trợ SSLv3 và định cấu hình bộ mật mã tốt nhất cho phiên bản TLS đó. Đầu tiên, thêm nó vào bạn build.gradle:

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

Hoặc bạn có thể tải về netcipher-1.2.jar và bao gồm nó trực tiếp trong ứng dụng của bạn. Sau đó, thay vì gọi:

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

Gọi này:

HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl); 

Nếu bạn muốn tùy chỉnh đặc biệt mà danh sách mật mã, bạn có thể kiểm tra mã đó. Nhưng hầu hết mọi người không cần phải suy nghĩ về danh sách mã hóa, thay vào đó nó nên sử dụng các phương pháp hay nhất phổ biến theo mặc định.

-1

Mã này đã làm việc kỳ diệu cho một bất ngờ javax.net.ssl.SSLHandshakeException.

Nâng cấp lên jdk1.8.0_92 và các tệp chính sách sức mạnh không giới hạn Oracle JCE không có tác dụng và tôi đã không cố gắng áp dụng SSLParameters cụ thể cho HttpsUrlConnection.

Đặc biệt, cố gắng sử dụng HttpsUrlConnection để đọc https://www.adrbnymellon.com kết quả trong các lỗi sau:

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

trang web này đã làm việc OK trước khi về 2016/04/15, và sau đó bắt đầu thất bại. Tôi tin rằng sự thất bại là do trang web ngừng hỗ trợ cho SSLv2HelloSSLv3 do lỗ hổng DROWN. Xem this để có phân tích tuyệt vời.

Truy cập vào các trang web bắt đầu làm việc bằng cách sửa đổi mã với những thay đổi để chỉ 2 hằng số:

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

Tôi hy vọng điều này sẽ giúp người khác.