Encendí el registro SSL en Java 8 JVM en una instancia de Linux que reproduce el problema. El registro SSL se activa con -Djavax.net.debug=ssl:handshake:verbose
. Esto reveló información útil.
La solución alternativa que estamos usando en producción y ha demostrado que funciona para nosotros es establecer este parámetro en la JVM:
-Djdk.tls.client.protocols=TLSv1
Si desea obtener más detalles, siga leyendo.
En un servidor donde se puede reproducir el problema (nuevamente, solo entre el 5 y el 10 % de las veces), observé lo siguiente:
*** ClientHello, TLSv1.2
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Handshake, length = 195
main, READ: TLSv1.2 Handshake, length = 1130
*** ServerHello, TLSv1.2
--- 8<-- SNIP -----
%% Initialized: [Session-79, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256]
** TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
--- 8<-- SNIP -----
Algorithm: [SHA1withRSA]
--- 8<-- SNIP -----
*** Diffie-Hellman ServerKeyExchange
--- 8<-- SNIP -----
*** ServerHelloDone
*** ClientKeyExchange, DH
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Handshake, length = 133
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data: { 108, 116, 29, 115, 13, 26, 154, 198, 17, 125, 114, 166 }
***
main, WRITE: TLSv1.2 Handshake, length = 40
main, called close()
main, called closeInternal(true)
main, SEND TLSv1.2 ALERT: warning, description = close_notify
main, WRITE: TLSv1.2 Alert, length = 26
main, called closeSocket(true)
main, waiting for close_notify or alert: state 5
main, received EOFException: ignored
main, called closeInternal(false)
main, close invoked again; state = 5
main, handling exception: java.io.IOException: SQL Server returned an incomplete response. The connection has been closed. ClientConnectionId:12a722b3-d61d-4ce4-8319-af049a0a4415
Tenga en cuenta que TLSv1.2 es seleccionado por el servidor de la base de datos y utilizado en este intercambio. He observado que, cuando fallan las conexiones del servicio problemático de Linux, TLSv1.2 SIEMPRE es el nivel que se seleccionó. Sin embargo, las conexiones no SIEMPRE fallan cuando se usa TLSv1.2. Solo fallan del 5 al 10 % de las veces.
Ahora aquí hay un intercambio de un servidor que NO tiene el problema. Todo lo demás es igual. Es decir, conectarse a la misma base de datos, la misma versión de JVM (Java 1.8.0_60), el mismo controlador JDBC, etc. Tenga en cuenta que, aquí, TLSv1 es seleccionado por el servidor de la base de datos en lugar de TLSv1.2 como en el caso del servidor defectuoso.
*** ClientHello, TLSv1.2
--- 8<-- SNIP -----
main, WRITE: TLSv1.2 Handshake, length = 207
main, READ: TLSv1 Handshake, length = 604
*** ServerHello, TLSv1
--- 8<-- SNIP -----
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
--- 8<-- SNIP -----
%% Initialized: [Session-79, TLS_RSA_WITH_AES_128_CBC_SHA]
** TLS_RSA_WITH_AES_128_CBC_SHA
--- 8<-- SNIP -----
Algorithm: [SHA1withRSA]
--- 8<-- SNIP -----
***
*** ServerHelloDone
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
--- 8<-- SNIP -----
main, WRITE: TLSv1 Handshake, length = 134
main, WRITE: TLSv1 Change Cipher Spec, length = 1
*** Finished
verify_data: { 26, 155, 166, 89, 229, 193, 126, 39, 103, 206, 126, 21 }
***
main, WRITE: TLSv1 Handshake, length = 48
main, READ: TLSv1 Change Cipher Spec, length = 1
main, READ: TLSv1 Handshake, length = 48
*** Finished
Entonces, cuando se negocia TLSv1 entre Linux JVM y SQL Server, las conexiones SIEMPRE son exitosas. Cuando se negocia TLSv1.2, tenemos fallas de conexión esporádicas.
(Nota:Java 7 (1.7.0_51) siempre negocia TLSv1, razón por la cual nunca se nos presentó el problema con una JVM de Java 7).
Las preguntas abiertas que aún tenemos son:
- POR QUÉ es que la misma Java 8 JVM ejecutada desde 2 servidores Linux diferentes siempre negociará TLSv1, pero cuando se conecta desde otro servidor Linux siempre negocia TLSv1.2.
- Y también, ¿por qué las conexiones negociadas TLSv1.2 tienen éxito la mayor parte del tiempo, pero no todo, en ese servidor?
Actualización 10/06/2017: Esta publicación de Microsoft describe el problema y la solución propuesta.
Recursos:
http://www.infoworld.com/article/2849292/operating-systems/more-patch-problems-reported-with-the-ms14-066-kb-2992611-winshock-mess.html
http://www.infoworld.com/article/2849292/operating-systems/more-patch-problems-reported-with-the-ms14-066-kb-2992611-winshock-mess.html
http://blogs.msdn.com/b/jdbcteam/archive/2008/09/09/the-driver-could-not-establecer-a-secure-connection-to-sql-server-by-using-secure- sockets-layer-ssl-encryption.aspx
Java 8, política de fuerza ilimitada de JCE y protocolo de enlace SSL sobre TLS
http://blogs.msdn.com/b/saponsqlserver/archive/2013/05/10/analyzing-jdbc-connection-issues.aspx
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#descPhase2
https://blogs.oracle.com/java-platform-group/entry/java_8_will_use_tls
Esto parece haberse solucionado en la versión 4.2 del controlador MS SQL JDBC. Creé un programa donde me conecté al servidor 1000 veces, pausando 100 ms entre cada intento. Con la versión 4.1 pude reproducir el problema cada vez, aunque solo ocurría esporádicamente. Con la versión 4.2 no pude reproducir el problema.
Antes de actualizar el controlador JDBC de SQL, compruebe primero la compatibilidad:
- Sqljdbc.jar requiere un JRE de 5 y es compatible con la API JDBC 3.0
- Sqljdbc4.jar requiere un JRE de 6 y es compatible con la API JDBC 4.0
- Sqljdbc41.jar requiere un JRE de 7 y es compatible con la API JDBC 4.1
- Sqljdbc42.jar requiere un JRE de 8 y es compatible con la API JDBC 4.2
Fuente:https://www.microsoft.com/en-us/download/details.aspx?id=11774