Подтвердить что ты не робот

Ошибка JDBC SQL Server на Java 8: драйвер не смог установить безопасное соединение с SQL Server с использованием шифрования Secure Sockets Layer (SSL)

При подключении к базе данных SQL Server с ошибкой Microsoft JDBC появляется следующая ошибка:

com.microsoft.sqlserver.jdbc.SQLServerException: драйвер не смог установить безопасное соединение с SQL Server с использованием шифрования Secure Sockets Layer (SSL). Ошибка: "SQL Server вернул неполный ответ. Соединение было закрыто. ClientConnectionId: 98d0b6f4-f3ca-4683-939e-7c0a0fca5931".

Недавно мы обновили наши приложения с Java 6 и Java 7 до Java 8. На всех системах под управлением Java работают SUSE Linux Enterprise Server 11 (x86_64), VERSION = 11, PATCHLEVEL = 3.

Вот факты, которые я собрал с помощью программы Java, которую я написал, которая просто последовательно открывается и закрывает 1000 соединений с базой данных.

  • Связи отбрасываются с этой ошибкой примерно на 5% -10% времени. Ошибка не возникает при каждом подключении.
  • Проблема ТОЛЬКО возникает с Java 8. Я запускал ту же программу на Java 7, и проблема не воспроизводима. Это соответствует нашему опыту в производстве до обновления. У нас были нулевые проблемы, запущенные под Java 7 в производстве.
  • Проблема не возникает на всех наших Linux-серверах под управлением Java 8, это происходит только на некоторых из них. Это меня озадачивает, но когда я запускаю одну и ту же тестовую программу на той же версии Linux JVM (1.8.0_60, 64 бит) в разных экземплярах Linux, проблема не возникает в одном из экземпляров Linux, но проблема происходит на других. В экземплярах Linux работает одна и та же версия SUSE, и они находятся на одном уровне исправлений.
  • Проблема возникает при подключении к серверам/базам данных BOTH SQL Server 2008 и SQL Server 2014.
  • Проблема возникает независимо от того, использую ли я версию 4.0 JDBC для SQL Server или версию версии 4.1 для версии 4.0.

То, что делает мои наблюдения уникальными по сравнению с другими в Интернете, заключается в том, что хотя проблема происходит ТОЛЬКО на Java 8, я не могу решить эту проблему на одном из кажущихся идентичными Linux-серверов, на которых работает тот же Java 8 JVM. Другие люди также видели эту проблему в более ранних версиях Java, но это не наш опыт.

Любые ввод, предложения или наблюдения, которые вы, возможно, цените.

4b9b3361

Ответ 1

Я включил протоколирование SSL в Java 8 JVM на экземпляре Linux, который воспроизводит проблему. -Djavax.net.debug=ssl:handshake:verbose SSL включается с использованием -Djavax.net.debug=ssl:handshake:verbose. Это выявило некоторую полезную информацию.

Обходной путь, который мы используем в работе и который доказал свою эффективность для нас, заключается в установке этого параметра в JVM:

 -Djdk.tls.client.protocols=TLSv1

Если вы хотите больше подробностей, пожалуйста, читайте дальше.

На сервере, где проблема может быть воспроизведена (опять же, только 5-10% времени), я заметил следующее:

*** 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

Обратите внимание, что TLSv1.2 выбирается сервером базы данных и используется в этом обмене. Я заметил, что при сбое соединения из проблемной службы linux TLSv1.2 ВСЕГДА является выбранным уровнем. Тем не менее, соединения не ВСЕГДА терпят неудачу при использовании TLSv1.2. Они терпят неудачу только 5-10% времени.

Теперь вот обмен с сервера, который не имеет проблемы. Все остальное равно. Т.е., подключение к той же базе данных, той же версии JVM (Java 1.8.0_60), тому же драйверу JDBC и т.д. Обратите внимание, что здесь TLSv1 выбирается сервером базы данных вместо TLSv1.2, как в случае неисправного сервера.

*** 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

Таким образом, когда TLSv1 согласовывается между Linux JVM и SQL Server, соединения ВСЕГДА успешны. Когда согласовывается TLSv1.2, мы получаем спорадические сбои соединения.

(Примечание: Java 7 (1.7.0_51) всегда согласовывает TLSv1, поэтому у нас никогда не возникало проблем с Java 7 JVM.)

У нас остаются открытые вопросы:

  1. ПОЧЕМУ одна и та же Java 8 JVM, запущенная на 2 разных серверах Linux, всегда будет согласовывать TLSv1, но при подключении с другого сервера Linux она всегда согласовывает TLSv1.2.
  2. А также почему согласованные соединения TLSv1.2 успешны большую часть, но не все, времени на этом сервере?

Обновление 10/10/2017: эта публикация от Microsoft описывает проблему и ее предлагаемое решение.

Ресурсы:

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-establish-a-secure-connection-to-sql-server-by-using-secure- розетки слой-SSL-encryption.aspx

Java 8, политика неограниченной силы JCE и SSL рукопожатие по 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

Ответ 2

Похоже, что это было исправлено в версии 4.2 драйвера MS SQL JDBC. Я создал программу, в которой я подключался к серверу 1000 раз, делая паузу в 100 мс между каждой попыткой. С версией 4.1 мне удавалось воспроизвести проблему каждый раз, хотя это происходило лишь время от времени. С версией 4.2 мне не удалось воспроизвести проблему.

Ответ 3

Перед обновлением драйвера SQL JDBC сначала проверьте совместимость:

  • Sqljdbc.jar требует JRE из 5 и поддерживает API JDBC 3.0
  • Sqljdbc4.jar требует JRE 6 и поддерживает API JDBC 4.0
  • Sqljdbc41.jar требует JRE 7 и поддерживает API JDBC 4.1
  • Sqljdbc42.jar требует JRE 8 и поддерживает API JDBC 4.2

Источник: https://www.microsoft.com/en-us/download/details.aspx?id=11774

Ответ 4

Недавно Microsoft открыла свой драйвер. В GitHub можно увидеть mssql-jdbc активность драйвера. Я предполагаю, что последняя версия предварительного просмотра - 6.1.5.

Также вы можете найти все версии предварительного просмотра на maven. Поддержка JDK7 и JDK 8.

Ответ 5

Я также затронул эту проблему в Windows Server 2012 R2, используя драйвер JDBC 4.0 и 4.1 с Java 7. Это статья Microsoft вины в DHE ciphersuites и рекомендует отключать их или уменьшать их приоритет, если вы не можете перейти на драйвер JDBC 4.2

Ответ 6

В моем случае это была java-версия, начиная с java 8 upgrade 171, переходите на java 8 upgrade 111, она также работает с java 8 upgrade 14X

Ответ 7

Ваш URL должен быть как ниже и добавить sql sqljdbc42.jar. Это решит вашу проблему

url = "jdbc:sqlserver://" +serverName + ":1433;DatabaseName=" + dbName + ";encrypt=true;trustServerCertificate=true;