SSLHandshakeException: error de Handshake en Android N / 7.0

Estoy trabajando en una aplicación para la cual los usuarios (de energía) tienen que configurar su propio servidor (es decir, nginx) para ejecutar la aplicación back-end. 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 comencé a desarrollar para 5.1. Más tarde recibí una actualización para 6.0 pero todavía mantenía un 5.1 funcionando dentro del emulador. No hace mucho tiempo, comencé a trabajar en un AVD con una imagen para el 7.0 y, para mi sorpresa, no se conecta a mi servidor, diciéndome que falló el handshake del ssl. Mi configuración nginx es bastante estricta, pero funciona tanto para 5.1 como para 6.0, así que …?!

Esto es lo que sé:

  • Utilizo v24 para libs de soporte, es decir, mi compileSdkVersion es 24.
  • Yo uso Volley v1.0.0 .
  • He probado TLSSocketFactory , pero no cambia nada. Esto parece ser usado la mayoría de las veces para evitar el uso de SSL3 para las versiones antiguas de SDK de todos modos.
  • Intenté boost el tiempo de espera , pero no cambia nada.
  • He intentado usar HttpURLConnection directamente, pero no cambia nada aparte de la traza de la stack (es sin las referencias de voley, pero idéntico de lo contrario).

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

Esto es lo que veo en el estudio de 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 

Como dice SSLV3_ALERT_HANDSHAKE_FAILURE , solo puedo suponer que por alguna razón intente conectarme con SSLv3 y falla, pero esto no tiene ningún sentido para mí en absoluto. Puede ser un problema de cifrado, pero ¿cómo puedo saber qué está tratando de usar? Preferiría no habilitar una cifra en el servidor, hacer un bash de conexión y repetir.

Mi sitio nginx usa un certificado de encriptación de let 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 sistemas de cifrado, tengo un script que confirma estas cifras (se ejecuta en un vps wheezy fuera de la red del servidor):

 Prueba ECDHE-RSA-AES256-GCM-SHA384 ... SÍ
 Comprobando ECDHE-ECDSA-AES256-GCM-SHA384 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDHE-RSA-AES256-SHA384 ... NO (error de handshake de alerta de sslv3)
 Comprobando ECDHE-ECDSA-AES256-SHA384 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDHE-RSA-AES256-SHA ... NO (error de handshake de alerta de sslv3)
 Comprobando ECDHE-ECDSA-AES256-SHA ... NO (error de handshake de alerta sslv3)
 Comprobando SRP-DSS-AES-256-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba de SRP-RSA-AES-256-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Probando DHE-DSS-AES256-GCM-SHA384 ... NO (fallo de handshake de alerta sslv3)
 Prueba DHE-RSA-AES256-GCM-SHA384 ... NO (falla de comunicación de alerta sslv3)
 Probando DHE-RSA-AES256-SHA256 ... NO (fallo de handshake de alerta sslv3)
 Probando DHE-DSS-AES256-SHA256 ... NO (fallo de handshake de alerta sslv3)
 Prueba DHE-RSA-AES256-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-DSS-AES256-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-RSA-CAMELLIA256-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-DSS-CAMELLIA256-SHA ... NO (error de handshake de alerta sslv3)
 Prueba AECDH-AES256-SHA ... NO (error de handshake de alerta sslv3)
 Comprobando SRP-AES-256-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba de ADH-AES256-GCM-SHA384 ... NO (error de handshake de alerta de sslv3)
 Prueba ADH-AES256-SHA256 ... NO (falla de comunicación de alerta sslv3)
 Prueba ADH-AES256-SHA ... NO (error de handshake de alerta sslv3)
 Prueba de ADH-CAMELLIA256-SHA ... NO (error de handshake de alerta de sslv3)
 Comprobando ECDH-RSA-AES256-GCM-SHA384 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-ECDSA-AES256-GCM-SHA384 ... NO (error de handshake de alerta de sslv3)
 Prueba ECDH-RSA-AES256-SHA384 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-ECDSA-AES256-SHA384 ... NO (sslv3 alert handshake failure)
 Comprobando ECDH-RSA-AES256-SHA ... NO (error de handshake de alerta de sslv3)
 Comprobando ECDH-ECDSA-AES256-SHA ... NO (error de handshake de alerta de sslv3)
 Prueba de AES256-GCM-SHA384 ... NO (error de handshake de alerta de sslv3)
 Probando AES256-SHA256 ... NO (fallo de handshake de alerta sslv3)
 Probar AES256-SHA ... NO (error de handshake de alerta sslv3)
 Prueba de CAMELLIA256-SHA ... NO (error de handshake de alerta sslv3)
 Prueba PSK-AES256-CBC-SHA ... NO (no hay cifras disponibles)
 Prueba ECDHE-RSA-DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Prueba ECDHE-ECDSA-DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Prueba SRP-DSS-3DES-EDE-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba SRP-RSA-3DES-EDE-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba EDH-RSA-DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Prueba EDH-DSS-DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Prueba de AECDH-DES-CBC3-SHA ... NO (error de handshake de alerta de sslv3)
 Probar SRP-3DES-EDE-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba ADH-DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-RSA-DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Prueba ECDH-ECDSA-DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DES-CBC3-SHA ... NO (error de handshake de alerta sslv3)
 Prueba PSK-3DES-EDE-CBC-SHA ... NO (no hay cifras disponibles)
 Prueba ECDHE-RSA-AES128-GCM-SHA256 ... SÍ
 Comprobando ECDHE-ECDSA-AES128-GCM-SHA256 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDHE-RSA-AES128-SHA256 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDHE-ECDSA-AES128-SHA256 ... NO (sslv3 alert handshake failure)
 Comprobando ECDHE-RSA-AES128-SHA ... NO (error de handshake de alerta de sslv3)
 Comprobando ECDHE-ECDSA-AES128-SHA ... NO (error de handshake de alerta de sslv3)
 Comprobando SRP-DSS-AES-128-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba de SRP-RSA-AES-128-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-DSS-AES128-GCM-SHA256 ... NO (error de handshake de alerta sslv3)
 Prueba DHE-RSA-AES128-GCM-SHA256 ... NO (falla de protocolo de alerta de sslv3)
 Prueba DHE-RSA-AES128-SHA256 ... NO (error de handshake de alerta sslv3)
 Probando DHE-DSS-AES128-SHA256 ... NO (fallo de handshake de alerta sslv3)
 Prueba DHE-RSA-AES128-SHA ... NO (falla de protocolo de alerta de sslv3)
 Prueba DHE-DSS-AES128-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-RSA-SEED-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-DSS-SEED-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-RSA-CAMELLIA128-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DHE-DSS-CAMELLIA128-SHA ... NO (error de handshake de alerta sslv3)
 Prueba AECDH-AES128-SHA ... NO (error de handshake de alerta sslv3)
 Prueba SRP-AES-128-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Probar ADH-AES128-GCM-SHA256 ... NO (error de handshake de alerta sslv3)
 Prueba ADH-AES128-SHA256 ... NO (error de handshake de alerta sslv3)
 Probar ADH-AES128-SHA ... NO (error de handshake de alerta sslv3)
 Probar ADH-SEED-SHA ... NO (sslv3 alert handshake failure)
 Prueba de ADH-CAMELLIA128-SHA ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-RSA-AES128-GCM-SHA256 ... NO (error de handshake de alerta de sslv3)
 Comprobando ECDH-ECDSA-AES128-GCM-SHA256 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-RSA-AES128-SHA256 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-ECDSA-AES128-SHA256 ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-RSA-AES128-SHA ... NO (error de handshake de alerta sslv3)
 Comprobando ECDH-ECDSA-AES128-SHA ... NO (error de handshake de alerta de sslv3)
 Prueba de AES128-GCM-SHA256 ... NO (error de handshake de alerta de sslv3)
 Prueba AES128-SHA256 ... NO (falla de comunicación de alerta sslv3)
 Probar AES128-SHA ... NO (error de handshake de alerta sslv3)
 Probando SEED-SHA ... NO (fallo de handshake de alerta de sslv3)
 Prueba de CAMELLIA128-SHA ... NO (error de handshake de alerta sslv3)
 Prueba PSK-AES128-CBC-SHA ... NO (no hay cifras disponibles)
 Comprobando ECDHE-RSA-RC4-SHA ... NO (error de handshake de alerta sslv3)
 Prueba ECDHE-ECDSA-RC4-SHA ... NO (falla de comunicación de alerta sslv3)
 Prueba AECDH-RC4-SHA ... NO (error de handshake de alerta sslv3)
 Prueba ADH-RC4-MD5 ... NO (falla de protocolo de alerta de sslv3)
 Comprobando ECDH-RSA-RC4-SHA ... NO (error de handshake de alerta sslv3)
 Prueba ECDH-ECDSA-RC4-SHA ... NO (falla de comunicación de alerta sslv3)
 Probando RC4-SHA ... NO (fallo de handshake de alerta sslv3)
 Probando RC4-MD5 ... NO (fallo de handshake de alerta sslv3)
 Prueba PSK-RC4-SHA ... NO (no hay cifras disponibles)
 Prueba EDH-RSA-DES-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Probando EDH-DSS-DES-CBC-SHA ... NO (error de handshake de alerta de sslv3)
 Prueba ADH-DES-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba DES-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba EXP-EDH-RSA-DES-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba EXP-EDH-DSS-DES-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba EXP-ADH-DES-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba EXP-DES-CBC-SHA ... NO (error de handshake de alerta sslv3)
 Prueba de EXP-RC2-CBC-MD5 ... NO (error de handshake de alerta de sslv3)
 Prueba EXP-ADH-RC4-MD5 ... NO (error de handshake de alerta sslv3)
 Prueba EXP-RC4-MD5 ... NO (falla de comunicación de alerta sslv3)
 Comprobando ECDHE-RSA-NULL-SHA ... NO (error de handshake de alerta sslv3)
 Prueba ECDHE-ECDSA-NULL-SHA ... NO (error de handshake de alerta sslv3)
 Probando AECDH-NULL-SHA ... NO (fallo de handshake de alerta sslv3)
 Comprobando ECDH-RSA-NULL-SHA ... NO (error de handshake de alerta de sslv3)
 Comprobando ECDH-ECDSA-NULL-SHA ... NO (error de handshake de alerta de sslv3)
 Prueba NULL-SHA256 ... NO (fallo de handshake de alerta sslv3)
 Prueba NULL-SHA ... NO (error de handshake de alerta sslv3)
 Prueba NULL-MD5 ... NO (error de handshake de alerta sslv3

Puedo abrir la URL del servidor en el navegador del emulador y obtener una respuesta JSON perfecta, así sé que el sistema en sí mismo es capaz.

Entonces la pregunta es, ¿por qué no puedo conectarme a Android 7?

Actualización :

He observado un paquete capturado usando tcpdump y wireshark y los cifrados habilitados se encuentran en ClientHello, por lo que no debería ser un problema.

 Cipher Suites (18 suites)

 Cipher Suite: Desconocido (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)
 Cipher Suite: Desconocido (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 puede ver, 0xc02f y 0xc030 , pero el siguiente paquete de TLSv1.2 dice: Alert (21), Handshake Failure (40) .

Actualización 2 :

Estas son las curvas de Android 5.1 en 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 del apretón de manos.

Cambiar la configuración de nginx eliminando secp384r1 o reemplazándola por la predeterminada (prime256v1) hace que funcione. Así que supongo que queda la pregunta: ¿puedo agregar curvas elípticas?

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

Actualización 3 :

Pequeña actualización, pero vale la pena mencionar: lo tengo para trabajar en el emulador usando Android 7.1.1 (!). Muestra los siguientes datos (nuevamente, tomados usando tcpdump y vistos 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 regresión conocida en Android 7.0, reconocida por Google y corregida 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 claros, el error aquí es que 7.0 solo admite UNA curva elíptica: prime256v1 también conocido como secp256r1, también conocido como NIST P-256, como señala Cornelis en la pregunta. Por lo tanto, si sus usuarios se enfrentan a este problema, estas son las soluciones disponibles para usted (ignorando el hecho de que sus usuarios deberían idealmente simplemente actualizar a Android 7.1.1):

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

  • Si eso no funciona, utilice suites de cifrado más antiguas que no se basan en 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 los 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 algunos otros enlaces a los que me referí al escribir esta respuesta:

Tuve el problema con un certificado autofirmado y el problema estaba en el cifrado que no fue aceptado por Android 7.0

openssl s_client -showcerts -connect : : openssl s_client -showcerts -connect :

en el resultado que encontré:

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

Busqué el Equivalente de Android del Cifrador y lo agregué a mi Readaptador de Restablecimiento:

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

Desde aquí, se aceptó cada conexión con el acuse de certificado correcto o un certificado correcto, pero con el cifrado “incorrecto” según Android 7.0.

Al revisar esta respuesta un año después, tengo que admitir que todavía estoy contento de haberla publicado, por otro lado, si está en posición de cambiar el certificado al conjunto de Cypher correcto, hágalo siempre sobre la “degradación”. te aceptó suites en tu aplicación. Si tiene que trabajar con un certificado autofirmado (por ejemplo, para incrustado), esta es una solución de trabajo para usted.

Aquí tu solución de trabajo 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 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()]); } } 

en estos códigos, simplemente agrego todos los Cifrados que son compatibles con el dispositivo, para mí esto funciona), puede ser que ayude a alguien) Saludos)

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

De forma predeterminada, las conexiones seguras (que usan 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 (nivel de API 23) y inferiores también confían en la tienda de CA agregada por el usuario de forma predeterminada.

Lo que significa que en Android Nougat (7.0), el juego para CA cambió por completo. Si tiene el certificado de 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 las suites de cifrado que su servidor exige pero que no están habilitadas de manera predeterminada en Android N. Por ejemplo, aquí hay dos cifras que necesito agregar en mi propia aplicación para hablar con un servidor anterior de Windows CE:

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

Igual que aquí. Mi servidor Nginx usando la configuración sll_ecdh_curve prime384v1. Desafortunadamente, backend no me permitió configurar el servidor Nginx siguiendo las instrucciones de Vicky Chijwani debido a las políticas de segmentación del Cliente. Me trataron de usar Valley y la última versión de la biblioteca OkHttp, pero no me ayudó. Para eludir ese error, tuve que usar WebView para comunicarme con el Servicio de API en los dispositivos de Adroid 7.0. Aquí está mi clase de Adaptador. Espero que alguien más 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(); } } }