2017-02-03 28 views
3

我正在構建一個應用程序,該應用程序通過HTTPS獲取一些JSON數據,使用自定義OkHttp客戶端進行改裝。它在KitKat上正常工作。一旦我轉移到Android 5,6或7,SSL握手失敗。改裝:在API21 +設備上啓動時,Android應用程序失敗SSL握手

服務器支持TLSv1,沒有別的。它還使用了一個古老的,過期的自簽名證書。測試它與Qualys的SSL工具,它告訴我所有版本的Android應該能夠連接。下面是我得到了什麼:

OkHttp客戶端:

public class HTTPClient { 

public static OkHttpClient getUnsafeOkHttpClient() { 
    try { 
     // Create a trust manager that does not validate certificate chains 

     final TrustManager[] trustAllCerts = new TrustManager[] { 
       new X509TrustManager() { 
        @Override 
        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) { 
        } 

        @Override 
        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) { 
        } 

        @Override 
        public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
         return new java.security.cert.X509Certificate[]{}; 
        } 
       } 
     }; 

     // Install the all-trusting trust manager 
     final SSLContext sslContext = SSLContext.getInstance("TLSv1"); 
     sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); 
     // Create a ssl socket factory with our all-trusting manager 
     final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); 
     //URL url = new URL(ApiIntentService.getHostAddress()); 
     //final SSLSocketFactory sslSocketFactory = new NoSSLv3SocketFactory(url); 

//   ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS) 
//     .tlsVersions(TlsVersion.TLS_1_0) 
//     .cipherSuites(
//       CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 
//       CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 
//       CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 
//       CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 
//       CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 
//       CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 
//       CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 
//       CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 
//       CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA, 
//       CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA) 
//     .build(); 

//   String hostname = "api.server.domain"; 
//   CertificatePinner certificatePinner = new CertificatePinner.Builder() 
//     .add(hostname, "sha256/3Iiwgs3a0qjPCnBQzW/GeHhPbZvhaJtxKvMJJVO5KdU=") 
//     .build(); 

     final OkHttpClient.Builder builder = new OkHttpClient.Builder(); 
//   builder.connectionSpecs(Collections.singletonList(spec)); 
//   builder.certificatePinner(certificatePinner); 
      builder.sslSocketFactory(sslSocketFactory); 
      builder.hostnameVerifier(new HostnameVerifier() { 
       @Override 
       public boolean verify(String hostname, SSLSession session) { 
        return true; 
       } 
      }); 
      builder.authenticator(new Authenticator() { 
       @Override 
       public Request authenticate(Route route, Response response) throws IOException { 
        String credential = Credentials.basic("user", "pass"); 
        return response.request().newBuilder() 
          .header("Authorization", credential) 
          .build(); 
       } 
      }); 
     OkHttpClient okHttpClient = builder.build(); 
     return okHttpClient; 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

我知道如何可怕在安全性方面這個代碼是屠殺。我的主管要求這樣做,我告訴他這有多糟糕。

我已經嘗試了兩件事情來解決我的問題,他們在那段代碼 - 證書固定和請求TLSv1註釋掉了一部分密碼。在其他問題中找到這兩個問題,但他們沒有任何改變(堆棧跟蹤完全相同)。

堆棧跟蹤

這裏是堆棧跟蹤的有趣的位:

I/RETROFIT: Data retrieval failed! javax.net.ssl.SSLHandshakeException: Handshake failed 
*snip* 
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb43eb200: Failure in SSL library, usually a protocol error 
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:770 0xac6fedd4:0x00000000) 
       at  com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) 

看起來好像Android的嘗試使用SSLv3突然,但Wireshark的顯示通過TLSv1通信。它應該從Client Hello開始,但服務器立即以Handshake Failure (40)回答。

所有的幫助都非常容易理解,因爲我全都沒有想法。如果需要,請要求澄清。

謝謝。

+0

您是否嘗試過註釋掉'ConnectionSpec'件與'ConnectionSpec.MODERN_TLS'取代'ConnectionSpec.COMPATIBLE_TLS' –

+0

是的,我有。它最初是在那裏,我在閱讀文檔後改變了它。雖然沒有效果。 –

回答

2

告訴您的主管您的HTTP客戶端無法連接到致命的不安全HTTPS服務器。你可以說這是計算機的限制,你唯一的選擇就是更新服務器。

一旦你完成了,你可以添加一個只開發調試模式。要做到這一點,請啓用服務器支持的密碼套件。您可以從Qualys工具中獲取列表。

+0

得到它在RPi託管的測試服務器上工作(使用TLSv1.2),並在具有不同Android版本的多個設備上進行測試。這顯然不是應用程序方面的問題。感謝您的答覆。 –

1

對於我來說,解決方案是增加更多的密碼作爲OkHttpClient可接受的。從API 21開始,Android的某些TLS證書已棄用。這可能幫助:

ConnectionSpec spec = new 
ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 
      .tlsVersions(TlsVersion.TLS_1_2) 
      .cipherSuites(  
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,      
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) 
      .build(); 

OkHttpClient client = new OkHttpClient.Builder() 
     .connectionSpecs(Collections.singletonList(spec)) 
     .build(); 

欲瞭解更多信息,請訪問:https://github.com/square/okhttp/wiki/HTTPS

+0

沒有爲我工作。 –

+0

據我記得,我嘗試添加很多不同的密碼,它不會工作。更新服務器軟件是最好的選擇。 –