SSLHandshakeException: Handshake falló en Android N / 7.0

Estoy trabajando en una aplicación para la que los usuarios (de energía) tienen que configurar su propio servidor (es decir, nginx) para ejecutar la aplicación de backend. El dominio correspondiente debe configurarse en la aplicación para que pueda conectarse. He estado probando principalmente en mi propio teléfono (sony z3c) y comenzó a desarrollar para 5.1. Posteriormente recibí una actualización para 6.0, pero todavía mantenía un 5.1 de trabajo dentro del emulador. No hace mucho tiempo, empecé a trabajar en un AVD con una imagen para 7.0 y para mi sorpresa no se conectará a mi servidor, diciéndome que el apretón de manos de ssl falló. Mi configuración de nginx es bastante estricta, pero funciona tanto para 5.1 y 6.0, así que ….?!

Esto es lo que sé:

  • Utilizo v24 para las librerías de soporte, es decir, mi compileSdkVersion es 24.
  • Yo uso Volley v1.0.0 .
  • He intentado el TLSSocketFactory , pero no cambia nada. Esto parece ser utilizado la mayoría de las veces para evitar el uso de SSL3 para las versiones más antiguas de SDK de todos modos.
  • He intentado aumentar el tiempo de espera , pero no cambia nada.
  • He intentado usar HttpURLConnection directamente, pero no cambia nada aparte del rastro de la pila (es sin las referencias del volley, pero idéntico de otra manera).

Sin la TLSSocketFactory la solicitud se realiza a través de una cola de solicitud desnuda, instanciada con Volley.newRequestQueue(context) .

Esto es lo que veo en el estudio android:

 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 

Dado que dice SSLV3_ALERT_HANDSHAKE_FAILURE sólo puedo asumir que por alguna razón intenta conectarse usando SSLv3 y falla, pero esto no tiene ningún sentido para mí en absoluto. Podría ser un problema de cifrado, pero ¿cómo puedo saber lo que está tratando de usar? Prefiero no activar una cifra en el servidor, hacer un intento de conexión y repetir.

Mi sitio nginx utiliza un certificado de encriptación y tiene la siguiente configuración:

 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; 

Para probar estos cifrados tengo un script y confirma estos cifrados (ejecutar en un vps wheezy fuera de la red del servidor):

 Pruebas ECDHE-RSA-AES256-GCM-SHA384 ... SÍ
 Prueba ECDHE-ECDSA-AES256-GCM-SHA384 ... NO (fallo del apretón de manos de alerta sslv3)
 Pruebas ECDHE-RSA-AES256-SHA384 ... NO (fallo del apretón de manos de sslv3)
 Prueba ECDHE-ECDSA-AES256-SHA384 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-RSA-AES256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-ECDSA-AES256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de SRP-DSS-AES-256-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de SRP-RSA-AES-256-CBC-SHA ... NO (fallo de handshake de alerta sslv3)
 Pruebas DHE-DSS-AES256-GCM-SHA384 ... NO (fallo del apretón de manos sslv3)
 Prueba DHE-RSA-AES256-GCM-SHA384 ... NO (error de apretón de manos de alerta sslv3)
 Prueba DHE-RSA-AES256-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Pruebas DHE-DSS-AES256-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba DHE-RSA-AES256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba DHE-DSS-AES256-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Pruebas DHE-RSA-CAMELLIA256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Pruebas DHE-DSS-CAMELLIA256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba AECDH-AES256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de SRP-AES-256-CBC-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba ADH-AES256-GCM-SHA384 ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba ADH-AES256-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ADH-AES256-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba ADH-CAMELLIA256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-RSA-AES256-GCM-SHA384 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-AES256-GCM-SHA384 ... NO (fallo del apretón de manos de sslv3)
 Prueba de ECDH-RSA-AES256-SHA384 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-AES256-SHA384 ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba de ECDH-RSA-AES256-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-AES256-SHA ... NO (fallo del apretón de manos de sslv3)
 Prueba AES256-GCM-SHA384 ... NO (error de apretón de manos de alerta sslv3)
 Prueba AES256-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba AES256-SHA ... NO (fallo de handshake de alerta sslv3)
 Pruebas CAMELLIA256-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Pruebas PSK-AES256-CBC-SHA ... NO (no se dispone de cifras)
 Prueba ECDHE-RSA-DES-CBC3-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-ECDSA-DES-CBC3-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba SRP-DSS-3DES-EDE-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de SRP-RSA-3DES-EDE-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba EDH-RSA-DES-CBC3-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de EDH-DSS-DES-CBC3-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba AECDH-DES-CBC3-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba de SRP-3DES-EDE-CBC-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba ADH-DES-CBC3-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-RSA-DES-CBC3-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-DES-CBC3-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba DES-CBC3-SHA ... NO (fallo de handshake de alerta sslv3)
 Pruebas PSK-3DES-EDE-CBC-SHA ... NO (no se dispone de cifras)
 Pruebas ECDHE-RSA-AES128-GCM-SHA256 ... SÍ
 Prueba ECDHE-ECDSA-AES128-GCM-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-RSA-AES128-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-ECDSA-AES128-SHA256 ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba ECDHE-RSA-AES128-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-ECDSA-AES128-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de SRP-DSS-AES-128-CBC-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba de SRP-RSA-AES-128-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba DHE-DSS-AES128-GCM-SHA256 ... NO (fallo de apretón de aviso sslv3)
 Pruebas DHE-RSA-AES128-GCM-SHA256 ... NO (fallo de apretón de aviso sslv3)
 Pruebas DHE-RSA-AES128-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Pruebas DHE-DSS-AES128-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba de DHE-RSA-AES128-SHA ... NO (fallo de apretón de aviso sslv3)
 Pruebas DHE-DSS-AES128-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba de DHE-RSA-SEED-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de DHE-DSS-SEED-SHA ... NO (fallo de apretón de manos de alerta sslv3)
 Prueba de DHE-RSA-CAMELLIA128-SHA ... NO (error de apretón de manos de alerta sslv3)
 Pruebas DHE-DSS-CAMELLIA128-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba AECDH-AES128-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba de SRP-AES-128-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ADH-AES128-GCM-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ADH-AES128-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ADH-AES128-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba de ADH-SEED-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba ADH-CAMELLIA128-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba ECDH-RSA-AES128-GCM-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-AES128-GCM-SHA256 ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba ECDH-RSA-AES128-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Pruebas ECDH-ECDSA-AES128-SHA256 ... NO (fallo del apretón de manos de sslv3)
 Prueba ECDH-RSA-AES128-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-AES128-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba AES128-GCM-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba AES128-SHA256 ... NO (fallo de apretón de aviso sslv3)
 Prueba AES128-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba de SEED-SHA ... NO (fallo de handshake de alerta sslv3)
 Pruebas CAMELLIA128-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Pruebas PSK-AES128-CBC-SHA ... NO (no se dispone de cifras)
 Prueba ECDHE-RSA-RC4-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-ECDSA-RC4-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba AECDH-RC4-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de ADH-RC4-MD5 ... NO (error de apretón de manos de alerta sslv3)
 Prueba de ECDH-RSA-RC4-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-RC4-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba RC4-SHA ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba RC4-MD5 ... NO (error de apretón de manos de alerta sslv3)
 Prueba PSK-RC4-SHA ... NO (no hay cifras disponibles)
 Prueba de EDH-RSA-DES-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de EDH-DSS-DES-CBC-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba de ADH-DES-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba DES-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de EXP-EDH-RSA-DES-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de EXP-EDH-DSS-DES-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de EXP-ADH-DES-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de EXP-DES-CBC-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de EXP-RC2-CBC-MD5 ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba EXP-ADH-RC4-MD5 ... NO (error de apretón de manos de alerta sslv3)
 Prueba EXP-RC4-MD5 ... NO (fallo del apretón de manos de alerta sslv3)
 Prueba de ECDHE-RSA-NULL-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDHE-ECDSA-NULL-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba de AECDH-NULL-SHA ... NO (fallo de apretón de manos de alerta sslv3)
 Prueba de ECDH-RSA-NULL-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba ECDH-ECDSA-NULL-SHA ... NO (error de apretón de manos de alerta sslv3)
 Prueba NULL-SHA256 ... NO (error de apretón de manos de alerta sslv3)
 Prueba de NULL-SHA ... NO (fallo de handshake de alerta sslv3)
 Prueba de NULL-MD5 ... NO (error de apretón de manos de alerta sslv3

Puedo abrir la url del servidor en el navegador del emulador y conseguir una respuesta de json perfecta así que sé que el sistema sí mismo es capaz.

Así que la pregunta es, ¿por qué no puedo conectar en Android 7?

Actualización :

He mirado un paquete capturado usando tcpdump y wireshark y las cifras habilitadas están en el ClientHello, por lo que no debería ser un problema.

 Cipher Suites (18 suites)

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

Como se puede ver 0xc02f y 0xc030 coinciden, pero el siguiente paquete TLSv1.2 dice: Alert (21), Handshake Failure (40) .

Actualización 2 :

Estas son las curvas de Android 5.1 en el ClientHello:

 Curvas elípticas (25 curvas)

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

En ServerHello se devuelve secp384r1 (0x0018) .

Y esto es de Android 7:

 Curvas elípticas (1 curva)

 Curva elíptica: secp256r1 (0x0017)

Resultando en la falla de apretón de manos.

El cambio de la configuración de nginx mediante la eliminación de secp384r1 o su sustitución por el valor predeterminado (prime256v1) lo hace funcionar. Así que supongo que la pregunta sigue siendo: ¿soy capaz de añadir curvas elípticas?

Los datos capturados son los mismos cuando se utiliza el emulador como cuando se utiliza un dispositivo Android 7.0 (General Mobile 4G).

Actualización 3 :

Pequeña actualización, pero vale la pena mencionar: Tengo que trabajar en el emulador con Android 7.1.1 (!). Muestra los datos siguientes (otra vez, agarrado usando tcpdump y visto usando wireshark):

 Curvas elípticas (3 curvas)

 Curva elíptica: secp256r1 (0x0017)
 Curva elíptica: secp384r1 (0x0018)
 Curva elíptica: secp512r1 (0x0019)

Muestra las mismas 18 Cipher Suites.

Esta es una conocida regresión en Android 7.0, reconocida por Google y arreglada en algún momento antes del lanzamiento de Android 7.1.1. Aquí está el informe de errores: https://code.google.com/p/android/issues/detail?id=224438 .

Para ser claro, el error aquí es que 7.0 sólo soporta una curva elíptica: prime256v1 aka secp256r1 alias NIST P-256, como señala Cornelis en la pregunta. Así que si los usuarios se enfrentan a este problema, estas son las soluciones disponibles para usted (ignorando el hecho de que sus usuarios idealmente sólo debe actualizar a Android 7.1.1):

  • Configure su servidor para usar la curva elíptica prime256v1 . Por ejemplo, en Nginx 1.10 lo hace estableciendo ssl_ecdh_curve prime256v1; .

  • Si eso no funciona, utilice antiguos conjuntos de cifrado que no dependan de la criptografía de curva elíptica (por ejemplo, DHE-RSA-AES256-GCM-SHA384 ) (asegúrese de entender lo que está haciendo aquí en términos de seguridad de datos )

NOTA: No soy un experto en criptografía de curva elíptica, asegúrese de hacer su propia investigación sobre las implicaciones de seguridad de mis sugerencias. Aquí hay otros enlaces a los que me referí al escribir esta respuesta:

Tuve el problema con un certificado autofirmado y el problema estaba en la cifra que no fue aceptada por Android 7.0

openssl s_client -showcerts -connect <domain>:<port> : openssl s_client -showcerts -connect <domain>:<port>

En el resultado que encontré:

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

Busqué el equivalente Android de la cifra y lo añadí a mi Retrofit 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)); 

A partir de aquí, se aceptó cada conexión con el pinning correcto del certificado o un certificado correcto pero con el cifrado "incorrecto" según Android 7.0.

Aquí usted trabaja la solución para Volley:

Antes de crear cola en códigos singleton:

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

Y aquí está 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()]); } } 

En este código Simplemente agrego todas las cifras que son compatibles con el dispositivo, para mí esto funciona), puede ayudar a alguien) Saludos)

Ps no es necesario agregar el parámetro de configuración de red de seguridad en el manifiesto.

De forma predeterminada, las conexiones seguras (utilizando protocolos como TLS y HTTPS) de todas las aplicaciones confían en las CA del sistema preinstaladas y las aplicaciones orientadas a Android 6.0 (API nivel 23) e inferiores también confían en la tienda de CA agregada por usuarios de forma predeterminada.

Lo que significa que en Android Nougat (7.0), el juego para CA cambió por completo. Si tiene su cert clave, puede agregar un archivo de configuración de seguridad de red (si tiene su cert), como se describe aquí: https://developer.android.com/training/articles/security-config.html

O puede crear su propio TrustManager, como se describe aquí: https://developer.android.com/training/articles/security-ssl.html#SelfSigned

O puede habilitar conjuntos de cifrado que su servidor requiera pero no están habilitados de forma predeterminada en Android N. Por ejemplo, aquí hay dos cifras que necesitaba agregar en mi propia aplicación hablando con un servidor antiguo de 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()])); 

Igual que aquí. Mi servidor Nginx que utiliza la configuración sll_ecdh_curve prime384v1. Por desgracia, el tipo backend no me permitió configurar el servidor Nginx siguiendo las instrucciones de Vicky Chijwani debido a las políticas de secirity del cliente. Me trataron de usar Valley y la última versión de la biblioteca OkHttp, pero no ayudó. Para evitar ese error, tuve que usar WebView para comunicarse con el servicio de API en los dispositivos de Adroid 7.0. Aquí está mi clase de adaptador. Espero que alguien lo encuentre útil.

 /** * 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(); } } } 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.