35

我正在開發一個應用程序,用戶必須爲其設置自己的服務器(即nginx)來運行後端應用程序。需要在應用程序中配置相應的域以便連接。我一直在我自己的手機上進行測試(索尼z3c),並開始爲5.1開發。後來我收到了6.0版的更新,但仍在仿真器中保持工作狀態5.1。不久之前,我開始使用7.0版本的AVD工作,並且驚訝地發現它不會連接到我的服務器,告訴我ssl握手失敗。我的nginx配置非常嚴格,但它適用於5.1和6.0,所以....?!SSLHandshakeException:握手在Android N/7.0上失敗

這是我所知道的:

  • 我使用V24支持庫,即我的compileSdkVersion爲24
  • 我使用排v1.0.0
  • 我試過TLSSocketFactory,但它沒有改變任何東西。這似乎在大多數情況下被用於防止老版本的SDK版本使用SSL3。
  • 我試過增加timeout,但它沒有改變任何東西。
  • 我試過直接使用HttpURLConnection,但它並沒有改變任何東西除了堆棧跟蹤(它沒有排除引用,但其他相同)。

如果沒有TLSSocketFactory,請求將通過一個裸請求隊列進行,該請求隊列使用Volley.newRequestQueue(context)實例化。

這是我在Android Studio中看到:

W/System.err: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: Connection closed by peer 
W/System.err:  at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:151) 
W/System.err:  at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:112) 
W/System.err: Caused by: javax.net.ssl.SSLHandshakeException: Connection closed by peer 
W/System.err:  at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) 
W/System.err:  at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357) 
W/System.err:  at com.android.okhttp.Connection.connectTls(Connection.java:235) 
W/System.err:  at com.android.okhttp.Connection.connectSocket(Connection.java:199) 
W/System.err:  at com.android.okhttp.Connection.connect(Connection.java:172) 
W/System.err:  at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:367) 
W/System.err:  at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:130) 
W/System.err:  at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:329) 
W/System.err:  at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:246) 
W/System.err:  at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:457) 
W/System.err:  at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:126) 
W/System.err:  at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:257) 
W/System.err:  at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218) 
W/System.err:  at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java) 
W/System.err:  at com.android.volley.toolbox.HurlStack.addBodyIfExists(HurlStack.java:264) 
W/System.err:  at com.android.volley.toolbox.HurlStack.setConnectionParametersForRequest(HurlStack.java:234) 
W/System.err:  at com.android.volley.toolbox.HurlStack.performRequest(HurlStack.java:107) 
W/System.err:  at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:96) 
W/System.err: ... 1 more 
W/System.err: Suppressed: javax.net.ssl.SSLHandshakeException: Handshake failed 
W/System.err:  at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429) 
W/System.err:  ... 17 more 
W/System.err: Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x7ffef3748040: Failure in SSL library, usually a protocol error 
W/System.err: error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:610 0x7ffeda1d2240:0x00000001) 
W/System.err: error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/s3_clnt.c:764 0x7ffee9d2b70a:0x00000000) 
W/System.err:  at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) 
W/System.err:  at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357) 
W/System.err:  ... 17 more 

因爲它說SSLV3_ALERT_HANDSHAKE_FAILURE由於某種原因,嘗試使用SSLv3的連接和失敗我只能假設,但這樣做沒有任何意義,我任何。這可能是一個密碼問題,但我怎麼知道它試圖使用什麼?我寧願不啓用服務器上的密碼,進行連接嘗試並重復。

我nginx的網站使用的是咱們加密證書,並具有以下配置:

ssl_stapling on; 
ssl_stapling_verify on; 
ssl_trusted_certificate /etc/ssl/certs/lets-encrypt-x1-cross-signed.pem; 
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:!aNULL; 
ssl_dhparam /etc/ssl/certs/dhparam.pem; 
ssl_ecdh_curve secp384r1; 
ssl_prefer_server_ciphers on; 
ssl_protocols TLSv1.2; 

爲了測試這些密碼我有一個script,它證實了這些密碼(在喘息VPS服務器的網絡外運行) :

 
Testing ECDHE-RSA-AES256-GCM-SHA384...YES 
Testing ECDHE-ECDSA-AES256-GCM-SHA384...NO (sslv3 alert handshake failure) 
Testing ECDHE-RSA-AES256-SHA384...NO (sslv3 alert handshake failure) 
Testing ECDHE-ECDSA-AES256-SHA384...NO (sslv3 alert handshake failure) 
Testing ECDHE-RSA-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing ECDHE-ECDSA-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-DSS-AES-256-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-RSA-AES-256-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-AES256-GCM-SHA384...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-AES256-GCM-SHA384...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-AES256-SHA256...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-AES256-SHA256...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-CAMELLIA256-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-CAMELLIA256-SHA...NO (sslv3 alert handshake failure) 
Testing AECDH-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-AES-256-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-AES256-GCM-SHA384...NO (sslv3 alert handshake failure) 
Testing ADH-AES256-SHA256...NO (sslv3 alert handshake failure) 
Testing ADH-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-CAMELLIA256-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-AES256-GCM-SHA384...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-AES256-GCM-SHA384...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-AES256-SHA384...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-AES256-SHA384...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-AES256-SHA...NO (sslv3 alert handshake failure) 
Testing AES256-GCM-SHA384...NO (sslv3 alert handshake failure) 
Testing AES256-SHA256...NO (sslv3 alert handshake failure) 
Testing AES256-SHA...NO (sslv3 alert handshake failure) 
Testing CAMELLIA256-SHA...NO (sslv3 alert handshake failure) 
Testing PSK-AES256-CBC-SHA...NO (no ciphers available) 
Testing ECDHE-RSA-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing ECDHE-ECDSA-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-DSS-3DES-EDE-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-RSA-3DES-EDE-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing EDH-RSA-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing EDH-DSS-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing AECDH-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-3DES-EDE-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing DES-CBC3-SHA...NO (sslv3 alert handshake failure) 
Testing PSK-3DES-EDE-CBC-SHA...NO (no ciphers available) 
Testing ECDHE-RSA-AES128-GCM-SHA256...YES 
Testing ECDHE-ECDSA-AES128-GCM-SHA256...NO (sslv3 alert handshake failure) 
Testing ECDHE-RSA-AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing ECDHE-ECDSA-AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing ECDHE-RSA-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing ECDHE-ECDSA-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-DSS-AES-128-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-RSA-AES-128-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-AES128-GCM-SHA256...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-AES128-GCM-SHA256...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-SEED-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-SEED-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-RSA-CAMELLIA128-SHA...NO (sslv3 alert handshake failure) 
Testing DHE-DSS-CAMELLIA128-SHA...NO (sslv3 alert handshake failure) 
Testing AECDH-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing SRP-AES-128-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-AES128-GCM-SHA256...NO (sslv3 alert handshake failure) 
Testing ADH-AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing ADH-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-SEED-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-CAMELLIA128-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-AES128-GCM-SHA256...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-AES128-GCM-SHA256...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-AES128-SHA...NO (sslv3 alert handshake failure) 
Testing AES128-GCM-SHA256...NO (sslv3 alert handshake failure) 
Testing AES128-SHA256...NO (sslv3 alert handshake failure) 
Testing AES128-SHA...NO (sslv3 alert handshake failure) 
Testing SEED-SHA...NO (sslv3 alert handshake failure) 
Testing CAMELLIA128-SHA...NO (sslv3 alert handshake failure) 
Testing PSK-AES128-CBC-SHA...NO (no ciphers available) 
Testing ECDHE-RSA-RC4-SHA...NO (sslv3 alert handshake failure) 
Testing ECDHE-ECDSA-RC4-SHA...NO (sslv3 alert handshake failure) 
Testing AECDH-RC4-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-RC4-MD5...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-RC4-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-RC4-SHA...NO (sslv3 alert handshake failure) 
Testing RC4-SHA...NO (sslv3 alert handshake failure) 
Testing RC4-MD5...NO (sslv3 alert handshake failure) 
Testing PSK-RC4-SHA...NO (no ciphers available) 
Testing EDH-RSA-DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing EDH-DSS-DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing ADH-DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing EXP-EDH-RSA-DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing EXP-EDH-DSS-DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing EXP-ADH-DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing EXP-DES-CBC-SHA...NO (sslv3 alert handshake failure) 
Testing EXP-RC2-CBC-MD5...NO (sslv3 alert handshake failure) 
Testing EXP-ADH-RC4-MD5...NO (sslv3 alert handshake failure) 
Testing EXP-RC4-MD5...NO (sslv3 alert handshake failure) 
Testing ECDHE-RSA-NULL-SHA...NO (sslv3 alert handshake failure) 
Testing ECDHE-ECDSA-NULL-SHA...NO (sslv3 alert handshake failure) 
Testing AECDH-NULL-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-RSA-NULL-SHA...NO (sslv3 alert handshake failure) 
Testing ECDH-ECDSA-NULL-SHA...NO (sslv3 alert handshake failure) 
Testing NULL-SHA256...NO (sslv3 alert handshake failure) 
Testing NULL-SHA...NO (sslv3 alert handshake failure) 
Testing NULL-MD5...NO (sslv3 alert handshake failure 

可以打開模擬器的瀏覽器服務器的URL並獲得一個完美的JSON響應,所以我知道系統本身能。

所以問題是,爲什麼我不能在Android 7上連接?

更新

我已經看了使用tcpdump和Wireshark的捕獲的數據包,並啓用密碼是在客戶問候,所以這不應該是一個問題。

 
Cipher Suites (18 suites) 

Cipher Suite: Unknown (0xcca9) 
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b) 
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) 
Cipher Suite: Unknown (0xcca8) 
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) 
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) 
Cipher Suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009e) 
Cipher Suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f) 
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009) 
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a) 
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013) 
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014) 
Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033) 
Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039) 
Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c) 
Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d) 
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f) 
Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) 

正如你可以看到0xc02f0xc030比賽,但接下來的TLSv1.2工作包說:Alert (21), Handshake Failure (40)

更新2

這些是從5.1的Android曲線中的ClientHello:

 
Elliptic curves (25 curves) 

Elliptic curve: sect571r1 (0x000e) 
Elliptic curve: sect571k1 (0x000d) 
Elliptic curve: secp521r1 (0x0019) 
Elliptic curve: sect409k1 (0x000b) 
Elliptic curve: sect409r1 (0x000c) 
Elliptic curve: secp384r1 (0x0018) 
Elliptic curve: sect283k1 (0x0009) 
Elliptic curve: sect283r1 (0x000a) 
Elliptic curve: secp256k1 (0x0016) 
Elliptic curve: secp256r1 (0x0017) 
Elliptic curve: sect239k1 (0x0008) 
Elliptic curve: sect233k1 (0x0006) 
Elliptic curve: sect233r1 (0x0007) 
Elliptic curve: secp224k1 (0x0014) 
Elliptic curve: secp224r1 (0x0015) 
Elliptic curve: sect193r1 (0x0004) 
Elliptic curve: sect193r2 (0x0005) 
Elliptic curve: secp192k1 (0x0012) 
Elliptic curve: secp192r1 (0x0013) 
Elliptic curve: sect163k1 (0x0001) 
Elliptic curve: sect163r1 (0x0002) 
Elliptic curve: sect163r2 (0x0003) 
Elliptic curve: secp160k1 (0x000f) 
Elliptic curve: secp160r1 (0x0010) 
Elliptic curve: secp160r2 (0x0011) 

在服務器問候secp384r1 (0x0018)被返回。

這是從Android的7:

 
Elliptic curves (1 curve) 

Elliptic curve: secp256r1 (0x0017) 

在握手故障而導致的。

通過刪除secp384r1或將其替換爲默認值(prime256v1)來更改nginx配置會使其工作。所以我想這個問題仍然存在:我能夠添加橢圓曲線嗎?

使用模擬器時的捕獲數據與使用Android 7.0設備(通用移動4G)時相同。

更新3

小更新,但值得一提的:我把它用在模擬器上工作的Android 7.1.1(!)。它示出了如下的數據(再次使用tcpdump的抓住和使用Wireshark的觀察):

 
Elliptic curves (3 curves) 

Elliptic curve: secp256r1 (0x0017) 
Elliptic curve: secp384r1 (0x0018) 
Elliptic curve: secp512r1 (0x0019) 

它顯示了相同的18個密碼套件。

+1

「我可以在模擬器中的瀏覽器中打開服務器的URL」 - 最有可能的,該瀏覽器不使用HTTP連接Java代碼,所以這只是一個局部測試。網絡,特殊的SSL處理,在Android 7.0中進行了相當大的改革,以支持[網絡安全配置](https://developer.android.com/training/articles/security-config.html)。您是否依賴用戶證書(即用戶通過設置添加的證書)? – CommonsWare

+0

我認爲你的服務器配置可能有點太嚴格。 tt只允許一條曲線和兩條密碼。 Wireshark中的連接和分析的數據包捕獲將顯示ClientHello中客戶端提供哪些密碼和曲線,以便您可以檢查客戶端是否也支持您的服務器支持。不必太在意「** sslv3 **握手失敗」 - 當使用TLS 1.2時,您也會得到這個,因爲協議在這個區域基本相同,因此它從sslv3重用了使用奇怪錯誤消息的功能。 –

+0

@CommonsWare:我不依賴於用戶證書,不。 – Cornelis

回答

2

默認情況下,安全的連接(使用諸如TLS協議和HTTPS)從所有的應用程序信任的預先安裝的系統的CA和應用定位的Android 6.0(API級23),並且還降低信任用戶添加的CA存儲默認。

這意味着在Android Nougat(7.0)上,CA的遊戲完全改變。如果你有你的密鑰證書,你可以添加一個網絡安全配置文件(如果你有你的證書),如下所述: https://developer.android.com/training/articles/security-config.html

或者你可以創建自己的TrustManager,如下所述: https://developer.android.com/training/articles/security-ssl.html#SelfSigned

或者你可以啓用的密碼套件您的服務器的需求但默認不啓用在Android N.例如,這裏有我需要在自己的應用程序添加同一家舊的Windows CE服務器的兩個密碼:

SSLSocket sslsock = (SSLSocket) createSocket(); 
    List<String> cipherSuitesToEnable = new ArrayList<>(); 
    cipherSuitesToEnable.add("SSL_RSA_WITH_RC4_128_SHA"); 
    cipherSuitesToEnable.add("SSL_RSA_WITH_3DES_EDE_CBC_SHA"); 
    sslsock.setEnabledCipherSuites(cipherSuitesToEnable.toArray(new String[cipherSuitesToEnable.size()])); 
+0

您鏈接到的文檔中的示例都非常清楚與CA /證書信任問題有關。我可以通過將'ssl_ecdh_curve'的服務器nginx配置從'secp384r1'改爲'prime256v1'(或將其刪除,因爲這是我的nginx版本的默認值),從而防止出現錯誤。因此SSL證書不是問題。 – Cornelis

+0

那麼你的問題就解決了嗎? –

+0

問題的癥結在於:如何讓Android 7在其ClientHello中提供'secp384r1'。它適用於舊版本的Android操作系統,洙..爲什麼它不能在7上工作? – Cornelis

5

在這裏你工作的解決方案V OLLEY:

創建在單碼隊列前:

public class VolleyServiceSingleton { 

    private RequestQueue mRequestQueue; 
    private HurlStack mStack; 

    private VolleyServiceSingleton(){ 

     SSLSocketFactoryExtended factory = null; 

     try { 
      factory = new SSLSocketFactoryExtended(); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } catch (KeyManagementException e) { 
      e.printStackTrace(); 
     } 


     final SSLSocketFactoryExtended finalFactory = factory; 
     mStack = new HurlStack() { 
      @Override 
      protected HttpURLConnection createConnection(URL url) throws IOException { 
       HttpsURLConnection httpsURLConnection = (HttpsURLConnection) super.createConnection(url); 
       try { 
        httpsURLConnection.setSSLSocketFactory(finalFactory); 
        httpsURLConnection.setRequestProperty("charset", "utf-8"); 

       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
       return httpsURLConnection; 
      } 

     }; 



     mRequestQueue = Volley.newRequestQueue(YourApplication.getContext(), mStack, -1);  

    } 


} 

這裏是SSLSocketFactoryExtended:

public class SSLSocketFactoryExtended extends SSLSocketFactory 
{ 
    private SSLContext mSSLContext; 
    private String[] mCiphers; 
    private String[] mProtocols; 


    public SSLSocketFactoryExtended() throws NoSuchAlgorithmException, KeyManagementException 
    { 
     initSSLSocketFactoryEx(null,null,null); 
    } 

    public String[] getDefaultCipherSuites() 
    { 
     return mCiphers; 
    } 

    public String[] getSupportedCipherSuites() 
    { 
     return mCiphers; 
    } 

    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException 
    { 
     SSLSocketFactory factory = mSSLContext.getSocketFactory(); 
     SSLSocket ss = (SSLSocket)factory.createSocket(s, host, port, autoClose); 

     ss.setEnabledProtocols(mProtocols); 
     ss.setEnabledCipherSuites(mCiphers); 

     return ss; 
    } 

    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException 
    { 
     SSLSocketFactory factory = mSSLContext.getSocketFactory(); 
     SSLSocket ss = (SSLSocket)factory.createSocket(address, port, localAddress, localPort); 

     ss.setEnabledProtocols(mProtocols); 
     ss.setEnabledCipherSuites(mCiphers); 

     return ss; 
    } 

    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException 
    { 
     SSLSocketFactory factory = mSSLContext.getSocketFactory(); 
     SSLSocket ss = (SSLSocket)factory.createSocket(host, port, localHost, localPort); 

     ss.setEnabledProtocols(mProtocols); 
     ss.setEnabledCipherSuites(mCiphers); 

     return ss; 
    } 

    public Socket createSocket(InetAddress host, int port) throws IOException 
    { 
     SSLSocketFactory factory = mSSLContext.getSocketFactory(); 
     SSLSocket ss = (SSLSocket)factory.createSocket(host, port); 

     ss.setEnabledProtocols(mProtocols); 
     ss.setEnabledCipherSuites(mCiphers); 

     return ss; 
    } 

    public Socket createSocket(String host, int port) throws IOException 
    { 
     SSLSocketFactory factory = mSSLContext.getSocketFactory(); 
     SSLSocket ss = (SSLSocket)factory.createSocket(host, port); 

     ss.setEnabledProtocols(mProtocols); 
     ss.setEnabledCipherSuites(mCiphers); 

     return ss; 
    } 

    private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) 
      throws NoSuchAlgorithmException, KeyManagementException 
    { 
     mSSLContext = SSLContext.getInstance("TLS"); 
     mSSLContext.init(km, tm, random); 

     mProtocols = GetProtocolList(); 
     mCiphers = GetCipherList(); 
    } 

    protected String[] GetProtocolList() 
    { 
     String[] protocols = { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}; 
     String[] availableProtocols = null; 

     SSLSocket socket = null; 

     try 
     { 
      SSLSocketFactory factory = mSSLContext.getSocketFactory(); 
      socket = (SSLSocket)factory.createSocket(); 

      availableProtocols = socket.getSupportedProtocols(); 
     } 
     catch(Exception e) 
     { 
      return new String[]{ "TLSv1" }; 
     } 
     finally 
     { 
      if(socket != null) 
       try { 
        socket.close(); 
       } catch (IOException e) { 
       } 
     } 

     List<String> resultList = new ArrayList<String>(); 
     for(int i = 0; i < protocols.length; i++) 
     { 
      int idx = Arrays.binarySearch(availableProtocols, protocols[i]); 
      if(idx >= 0) 
       resultList.add(protocols[i]); 
     } 

     return resultList.toArray(new String[0]); 
    } 

    protected String[] GetCipherList() 
    { 
     List<String> resultList = new ArrayList<String>(); 
     SSLSocketFactory factory = mSSLContext.getSocketFactory(); 
     for(String s : factory.getSupportedCipherSuites()){ 
      Log.e("CipherSuite type = ",s); 
      resultList.add(s); 
     } 
     return resultList.toArray(new String[resultList.size()]); 
    } 

} 

在這個代碼我簡單補充一點,由設備支持的所有加密算法,對我來說這作品),可能會幫助某人)歡呼聲)

ps無需在清單中添加安全網絡配置參數。

10

我有一個自簽名證書的問題,問題是這是不是由Android 7.0

接受了密碼,我跑:openssl s_client -showcerts -connect <domain>:<port>

的結果,我發現:

Protocol : TLSv1 
Cipher : DHE-RSA-AES256-SHA 

我搜索了密碼的Android的等效,並把它添加到我的改造Restadapter:

ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 
      .tlsVersions(TlsVersion.TLS_1_2) 
      .cipherSuites(

CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA) 
      .build(); 

clientBuilder.connectionSpecs(Collections.singletonList(spec)); 

從這裏,我們接受了根據Android 7.0的每個連接都帶有正確的證書固定或正確的證書,但帶有「錯誤」的密碼。

+1

這個確切的解決方案爲我們工作 - AT&T的一羣Galaxy S7用戶最近得到了7.0更新,並在重大活動期間徹底打破了我們的應用程序。糟糕的谷歌,不好。 – DiscDev

+0

@DiskDev我是否理解你的問題,在AT&T 7.0版更新之後,問題仍然得到解決或再次崩潰? –

+1

您的解決方法解決了我們的問題。 AT&T Galaxy S7用戶在一週或兩週前獲得了7.0版本的更新,並且由於我們不知道這個問題,它導致我們的應用在用戶使用該設備時非常不合時宜地崩潰。我們在一週內有另一個事件,並且有了解決方法(因爲我們正在使用改進功能,並且不想對服務器端的密碼進行更改),我們正在備份並運行。謝謝! – DiscDev

29

這是Android 7.0中的一個已知迴歸,得到了Google的承認,並在Android 7.1.1發佈之前有所修復。這是錯誤報告:https://code.google.com/p/android/issues/detail?id=224438

爲了清楚起見,這裏的錯誤是7.0只支持一個橢圓曲線:prime256v1又名secp256r1又名NIST P-256,正如Cornelis在問題中指出的那樣。所以,如果你的用戶都面臨這個問題,這些都可以給你解決方法(忽略了一個事實,你的用戶應該非常只是升級至Android 7.1.1):

  • 配置您的服務器使用橢圓曲線prime256v1 。例如,在Nginx 1.10中,您可以通過設置ssl_ecdh_curve prime256v1;來完成此操作。

  • 如果不起作用,使用不依賴於橢圓曲線加密算法舊的密碼套件(如DHE-RSA-AES256-GCM-SHA384)(確保你理解你在這裏做什麼在數據安全性方面的)

注:在橢圓曲線加密方面的專家,確保做自己的研究,對我的建議的安全隱患。下面是我提到在寫這個答案的一些其他鏈接:

+0

有人有解決上述問題嗎? – Kels

+0

@Kels這個Android 7.0錯誤並沒有真正的解決方案,只有我上面列出的解決方法。 –

+0

感謝您的回覆。但是當我在7.1.1中測試並仍然有相同的錯誤。 – Kels

0

同樣在這裏。我的Nginx服務器使用sll_ecdh_curve prime384v1設置。不幸的是,由於客戶的secirity策略,後端人員不允許我按照Vicky Chijwani的指示配置Nginx服務器。我曾嘗試使用Valley和最新版本的OkHttp庫,但它沒有幫助。 要繞過該錯誤,我必須使用WebView與Adroid 7.0設備上的API服務進行通信。這是我的Adapter類。我希望別人會覺得它有用。

/** 
* Connection to API service using WebView (for Android 7.0 devices) 
* 
* Created by fishbone on 09.08.17. 
*/ 
@RequiresApi(api = Build.VERSION_CODES.N) 
class WebViewHttpsConnection extends ApiConnection { 

    private static final long TIMEOUT = 30000; 
    private static final String POST_DATA_SCRIPT = "javascript:(function(){" + 
      "var xhr = new XMLHttpRequest();\n" + 
      "xhr.open(\"POST\", \"%1$s\", true);\n" + 
      "xhr.setRequestHeader(\"Content-type\", \"application/json\");\n" + 
      "xhr.onreadystatechange = function() {\n" + 
      " if (xhr.readyState === 4) {\n" + 
      "  listener.onResult(xhr.status, xhr.responseText);\n" + 
      " }\n" + 
      "};\n" + 
      "xhr.send('%2$s');\n" + 
      "})();"; 

    WebViewHttpsConnection(Context context) { 
     super(context); 
    } 
    /** 
    * Send data to API Service. 
    * 
    * @param url URL of API Service 
    * @param request JSON Object serialized into String 
    * @return API response 
    * @throws IOException errors 
    */ 
    @Override 
    public String sendData(final URL url, final String request) throws IOException { 
     // We should escape backslashes in JSON because JS unescape it back 
     final String javaScript = String.format(POST_DATA_SCRIPT, url.toString(), 
       request.replace("\\", "\\\\")); 
     final RequestResultListener listener = new RequestResultListener(); 
     // We must use WebView only from 'main' Thread, therefore I using Handler with Application context 
     Handler handler = new Handler(getContext().getApplicationContext().getMainLooper()); 
     handler.post(new Runnable() { 

      @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"}) // JavaScript is only for me and I'll use it only on Android 7.0 devices, so not scary 
      @Override 
      public void run() { 
       // WebView must be created, configured and called from the same Thread 
       final WebView webView = new WebView(getContext(), null); 
       webView.getSettings().setJavaScriptEnabled(true); 
       webView.addJavascriptInterface(listener, "listener"); 
       webView.setWebViewClient(new WebViewClient() { 

        @Override 
        public void onPageFinished(WebView view, String url) { 
         // As soon as loaded any page from target domain, we call JS-script that will make POST request 
         webView.loadUrl(javaScript); 
        } 
       }); 
       // I cant use postUrl() method because WebView doesn't allow to define 'Content-type' header, but my API service accepts only 'application/json' content type 
       // To complete CORS requirements we make any GET request to lets WebView navigate to the target domain, otherwise it will send 'null' as 'Origin' in headers 
       webView.loadUrl(url.toString()); 
      } 
     }); 
     // And further we waiting for response of API service 
     try { 
      if (!listener.latch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 
       throw new IOException("Timeout connection to server"); 
      } 
     } catch (InterruptedException e) { 
      throw new IOException("Connection to server was interrupted"); 
     } 
     if (listener.code != HttpURLConnection.HTTP_OK) { 
      throw new HttpRetryException("Server return error code " + listener.code, 
        listener.code); 
     } 
     if (TextUtils.isEmpty(listener.result)) { 
      throw new HttpRetryException("Service return empty response", listener.code); 
     } 
     return listener.result; 
    } 
    /** 
    * Callback interface for receiving API Service response from JavaScript inside WebView 
    */ 
    private static class RequestResultListener { 

     CountDownLatch latch = new CountDownLatch(1); 
     String result = null; 
     int code; 

     @JavascriptInterface 
     public void onResult(int code, String result) { 
      this.result = result; 
      this.code = code; 
      latch.countDown(); 
     } 
    } 
}