SSLHandshakeException: Handshake fehlgeschlagen unter Android N / 7.0

Ich arbeite an einer App, für die die (Power-) Benutzer ihren eigenen Server (dh nginx) einrichten müssen, um die Backend-Anwendung auszuführen. Die entsprechende Domäne muss in der App konfiguriert werden, damit eine Verbindung hergestellt werden kann. Ich habe in erster Linie auf meinem eigenen Handy (Sony Z3C) getestet und begann für 5.1 zu entwickeln. Später erhielt ich ein Update für 6.0, behielt aber immer noch einen funktionierenden 5.1 im Emulator. Vor nicht allzu langer Zeit begann ich an einem AVD mit einem Image für 7.0 zu arbeiten und zu meiner Überraschung wird es keine Verbindung zu meinem Server herstellen und mir mitteilen, dass der SSL-Handshake fehlgeschlagen ist. Meine Nginx-Konfiguration ist ziemlich streng, aber es funktioniert sowohl für 5.1 und 6.0, also ….?!

Hier ist, was ich weiß:

  • Ich benutze v24 für Support-Bibliotheken, dh meine compileSdkVersion ist 24.
  • Ich benutze Volley v1.0.0 .
  • Ich habe versucht, die TLSSocketFactory , aber es ändert nichts. Dies scheint die meiste Zeit zu verwenden, um die Verwendung von SSL3 für ältere SDK-Versionen trotzdem zu verhindern.
  • Ich habe versucht, die Zeitüberschreitung zu erhöhen, aber es ändert nichts.
  • Ich habe versucht, HttpURLConnection direkt zu verwenden, aber es ändert nichts außer der Stack-Ablaufverfolgung (es ist ohne die Volley-Verweise, aber ansonsten identisch).

Ohne die TLSSocketFactory erfolgt die Anfrage über eine Volley.newRequestQueue(context) Anfrage-Warteschlange, die mit Volley.newRequestQueue(context) instanziiert wird.

Das ist, was ich in Android Studio sehe:

 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 

Da es SSLV3_ALERT_HANDSHAKE_FAILURE sagt SSLV3_ALERT_HANDSHAKE_FAILURE ich nur annehmen, dass es aus irgendeinem Grund versucht, eine Verbindung mit SSLv3 herzustellen und schlägt fehl, aber das macht für mich überhaupt keinen Sinn. Es könnte ein Chiffre-Problem sein, aber wie kann ich sagen, was es versucht zu verwenden? Ich würde lieber keine Chiffren auf dem Server aktivieren, einen Verbindungsversuch machen und wiederholen.

Meine nginx-Site verwendet ein lets-encrypt-Zertifikat und hat folgende Konfiguration:

 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; 

Um diese Chiffren zu testen, habe ich ein Skript und es bestätigt diese Chiffren (laufen auf einem keepy vps außerhalb des Server-Netzwerkes):

 Testen von ECDHE-RSA-AES256-GCM-SHA384 ... JA
 Testen von ECDHE-ECDSA-AES256-GCM-SHA384 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDHE-RSA-AES256-SHA384 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von ECDHE-ECDSA-AES256-SHA384 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDHE-RSA-AES256-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDHE-ECDSA-AES256-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 SRP-DSS-AES-256-CBC-SHA ... NO testen (sslv3-Alert-Handshake-Fehler)
 Testen von SRP-RSA-AES-256-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-AES256-GCM-SHA384 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-AES256-GCM-SHA384 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-AES256-SHA256 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-AES256-SHA256 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-AES256-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-AES256-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-CAMELLIA256-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-CAMELLIA256-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von AECDH-AES256-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von SRP-AES-256-CBC-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 ADH-AES256-GCM-SHA384 ... NO testen (sslv3-Alert-Handshake-Fehler)
 ADH-AES256-SHA256 ... NO testen (sslv3 alert handshake failure)
 Testen von ADH-AES256-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ADH-CAMELLIA256-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-AES256-GCM-SHA384 ... NEIN (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-AES256-GCM-SHA384 ... NEIN (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-AES256-SHA384 ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-AES256-SHA384 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-AES256-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-AES256-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von AES256-GCM-SHA384 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von AES256-SHA256 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von AES256-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von CAMELLIA256-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von PSK-AES256-CBC-SHA ... NEIN (keine Verschlüsselung verfügbar)
 Testen von ECDHE-RSA-DES-CBC3-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDHE-ECDSA-DES-CBC3-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von SRP-DSS-3DES-EDE-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von SRP-RSA-3DES-EDE-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von EDH-RSA-DES-CBC3-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von EDH-DSS-DES-CBC3-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von AECDH-DES-CBC3-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von SRP-3DES-EDE-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ADH-DES-CBC3-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-DES-CBC3-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-DES-CBC3-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von DES-CBC3-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von PSK-3DES-EDE-CBC-SHA ... NEIN (keine Verschlüsselung verfügbar)
 Testen von ECDHE-RSA-AES128-GCM-SHA256 ... JA
 Testen von ECDHE-ECDSA-AES128-GCM-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDHE-RSA-AES128-SHA256 ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDHE-ECDSA-AES128-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDHE-RSA-AES128-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDHE-ECDSA-AES128-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von SRP-DSS-AES-128-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von SRP-RSA-AES-128-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-AES128-GCM-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-AES128-GCM-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-AES128-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-AES128-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-AES128-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-AES128-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-SEED-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-SEED-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-RSA-CAMELLIA128-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von DHE-DSS-CAMELLIA128-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von AECDH-AES128-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von SRP-AES-128-CBC-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 ADH-AES128-GCM-SHA256 ... NO testen (SSLV3-Alert-Handshake-Fehler)
 ADH-AES128-SHA256 ... NO testen (sslv3-Alert-Handshake-Fehler)
 Testen von ADH-AES128-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von ADH-SEED-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von ADH-CAMELLIA128-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-AES128-GCM-SHA256 ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-AES128-GCM-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-AES128-SHA256 ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-AES128-SHA256 ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-AES128-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-AES128-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von AES128-GCM-SHA256 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von AES128-SHA256 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von AES128-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 SEED-SHA ... NO testen (SSLV3-Alert-Handshake-Fehler)
 CAMELLIA128-SHA wird getestet ... Nein (sslv3 alert handshake failure)
 Testen von PSK-AES128-CBC-SHA ... NEIN (keine Verschlüsselung verfügbar)
 Testen von ECDHE-RSA-RC4-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDHE-ECDSA-RC4-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 AECDH-RC4-SHA ... NO testen (SSLV3-Alert-Handshake-Fehler)
 Testen von ADH-RC4-MD5 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-RC4-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-RC4-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von RC4-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von RC4-MD5 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von PSK-RC4-SHA ... NEIN (keine Verschlüsselungen verfügbar)
 Testen von EDH-RSA-DES-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von EDH-DSS-DES-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von ADH-DES-CBC-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von DES-CBC-SHA ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von EXP-EDH-RSA-DES-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von EXP-EDH-DSS-DES-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von EXP-ADH-DES-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von EXP-DES-CBC-SHA ... NEIN (sslv3-Alert-Handshake-Fehler)
 Testen von EXP-RC2-CBC-MD5 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von EXP-ADH-RC4-MD5 ... NO (sslv3-Alert-Handshake-Fehler)
 Testen von EXP-RC4-MD5 ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDHE-RSA-NULL-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 ECDHE-ECDSA-NULL-SHA ... NO testen (SSLV3-Alert-Handshake-Fehler)
 Testen von AECDH-NULL-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-RSA-NULL-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von ECDH-ECDSA-NULL-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von NULL-SHA256 ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von NULL-SHA ... NO (SSLV3-Alert-Handshake-Fehler)
 Testen von NULL-MD5 ... NO (sslv3-Alert-Handshake-Fehler

Ich kann die Server-URL im Emulator-Browser öffnen und bekomme eine perfekte JSON-Antwort, so dass ich weiß, dass das System selbst in der Lage ist.

Die Frage ist also, warum kann ich nicht auf Android 7 verbinden?

Aktualisierung :

Ich habe ein gefangenes Paket mit tcpdump und wireshark betrachtet und die aktivierten Chiffren sind im ClientHello, also sollte das kein Problem sein.

 Cipher Suites (18 Suiten)

 Cipher Suite: Unbekannt (0xcca9)
 Verschlüsselungssuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
 Verschlüsselungssuite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
 Cipher Suite: Unbekannt (0xcca8)
 Cipher-Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
 Verschlüsselungssuite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
 Cipher-Suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009e)
 Verschlüsselungssuite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f)
 Verschlüsselungssuite: 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)
 Verschlüsselungs-Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
 Cipher-Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
 Verschlüsselungssuite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
 Cipher-Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
 Verschlüsselungssuite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)

Wie Sie sehen können, 0xc02f und 0xc030 überein, aber das nächste TLSv1.2-Paket sagt: Alert (21), Handshake Failure (40) .

Update 2 :

Das sind die Kurven von Android 5.1 im ClientHello:

 Elliptische Kurven (25 Kurven)

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

Im ServerHello wird secp384r1 (0x0018) zurückgegeben.

Und das ist von Android 7:

 Elliptische Kurven (1 Kurve)

 Elliptische Kurve: secp256r1 (0x0017)

Dies führt zum Handshake-Fehler.

Wenn Sie die nginx-Konfiguration ändern, indem Sie secp384r1 entfernen oder sie durch die Standardeinstellung (prime256v1) ersetzen, funktioniert sie. Ich denke, die Frage bleibt: Kann ich elliptische Kurven hinzufügen?

Die erfassten Daten sind bei Verwendung des Emulators dieselben wie bei Verwendung eines Android 7.0-Geräts (General Mobile 4G).

Update 3 :

Kleines Update, aber erwähnenswert: Ich habe es im Emulator mit Android 7.1.1 (!) Arbeiten lassen. Es zeigt die folgenden Daten (wiederum mit tcpdump gegriffen und mit wireshark angezeigt):

 Elliptische Kurven (3 Kurven)

 Elliptische Kurve: secp256r1 (0x0017)
 Elliptische Kurve: secp384r1 (0x0018)
 Elliptische Kurve: secp512r1 (0x0019)

Es zeigt die gleichen 18 Cipher Suites.

    Dies ist eine bekannte Regression in Android 7.0, die von Google anerkannt und irgendwann vor der Veröffentlichung von Android 7.1.1 behoben wurde. Hier ist der Fehlerbericht: https://code.google.com/p/android/issues/detail?id=224438 .

    Um es klar zu sagen, der Bug hier ist, dass 7.0 nur EINE elliptische Kurve unterstützt: prime256v1 alias secp256r1 alias NIST P-256, wie Cornelis in der Frage aufzeigt. Wenn Ihre Benutzer mit diesem Problem konfrontiert sind, sind dies die Workarounds, die Ihnen zur Verfügung stehen (ungeachtet der Tatsache, dass Ihre Benutzer idealerweise nur auf Android 7.1.1 aktualisieren sollten):

    • Konfigurieren Sie Ihren Server, um die elliptische Kurve prime256v1 . Zum Beispiel in Nginx 1.10 tun Sie dies, indem Sie ssl_ecdh_curve prime256v1; .

    • Wenn das nicht funktioniert, verwenden Sie ältere Cipher-Suites, die nicht auf Ellipsenkurven-Kryptographie angewiesen sind (z. B. DHE-RSA-AES256-GCM-SHA384 ) (stellen Sie sicher, dass Sie verstehen, was Sie hier in Bezug auf Datensicherheit tun )

    HINWEIS: Ich bin kein Experte für Elliptische-Kurven-Kryptografie, stellen Sie sicher, dass Sie selbst die Sicherheitsimplikationen meiner Vorschläge untersuchen. Hier sind einige andere Links, auf die ich beim Schreiben dieser Antwort hingewiesen habe:

    Ich hatte das Problem mit einem selbstsignierten Zertifikat und das Problem lag in der Verschlüsselung, die von Android 7.0 nicht akzeptiert wurde

    Ich habe ausgeführt: openssl s_client -showcerts -connect :

    Im Ergebnis habe ich gefunden:

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

    Ich suchte nach dem Android Equivalent des Cipher und fügte es meinem Retrofit Restadapter hinzu:

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

    Von hier aus wurde jede Verbindung mit dem korrekten Zertifikats-Pinning oder einem korrekten Zertifikat aber mit der “falschen” Verschlüsselung nach Android 7.0 akzeptiert.

    Wenn ich ein Jahr später auf diese Antwort zurückblicke, muss ich zugeben, dass ich immer noch glücklich bin, dass ich es gepostet habe. Andererseits, wenn Sie in der Lage sind, das Zertifikat in die richtige Cypher-Suite zu ändern, tun Sie dies immer über “Downgrading”. Te akzeptiert Suiten in Ihrer App. Wenn Sie mit einem selbstsignierten Zertifikat arbeiten müssen (zum Beispiel für Embedded), ist dies eine funktionierende Lösung für Sie.

    Hier geht es zur Arbeitslösung für Volley:

    bevor Sie eine Warteschlange in Singleton-Codes erstellen:

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

    und hier ist 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 resultList = new ArrayList(); 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 resultList = new ArrayList(); SSLSocketFactory factory = mSSLContext.getSocketFactory(); for(String s : factory.getSupportedCipherSuites()){ Log.e("CipherSuite type = ",s); resultList.add(s); } return resultList.toArray(new String[resultList.size()]); } } 

    In diesem Code füge ich einfach alle Ciphers hinzu, die vom Gerät unterstützt werden, für mich funktioniert das), kann jemand helfen) Cheers)

    ps keine Notwendigkeit, Sicherheitsnetzwerkkonfigurationsparameter im Manifest hinzuzufügen.

    Standardmäßig vertrauen sichere Verbindungen (mit Protokollen wie TLS und HTTPS) aus allen Apps den vorinstallierten Systemzertifizierungsstellen und Apps, die auf Android 6.0 (API-Stufe 23) und niedriger abzielen, vertrauen standardmäßig auch dem vom Benutzer hinzugefügten CA-Speicher.

    Das bedeutet, dass sich bei Android Nougat (7.0) das Spiel für CAs komplett geändert hat. Wenn Sie Ihr Schlüsselzertifikat haben, können Sie eine Netzwerksicherheitskonfigurationsdatei hinzufügen (wenn Sie Ihr Zertifikat haben), wie hier beschrieben: https://developer.android.com/training/articles/security-config.html

    Oder Sie können Ihren eigenen TrustManager erstellen, wie hier beschrieben: https://developer.android.com/training/articles/security-ssl.html#SelfSigned

    Oder Sie können Cipher-Suites aktivieren, die Ihr Server fordert, die aber auf Android N standardmäßig nicht aktiviert sind. Hier sind beispielsweise zwei Ziffern, die ich in meiner eigenen Anwendung hinzufügen musste, um mit einem alten Windows CE-Server zu sprechen:

      SSLSocket sslsock = (SSLSocket) createSocket(); List 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()])); 

    Hier gilt das gleiche. Mein Nginx-Server verwendet die Einstellung sll_ecdh_curve prime384v1. Leider erlaubte mir der Backend-Typ nicht, den Nginx-Server nach den statementen von Vicky Chijwani zu konfigurieren. Ich wurde versucht, Valley und die neueste Version der OkHttp-Bibliothek zu verwenden, aber es hat nicht geholfen. Um diesen Fehler zu umgehen, musste ich WebView verwenden, um mit dem API-Dienst auf Adroid 7.0-Geräten zu kommunizieren. Hier ist meine Adapterklasse. Ich hoffe, dass jemand anderes es nützlich finden wird.

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