Получено фатальное предупреждение: сбой рукопожатия через SSLHandshakeException

у меня проблема с авторизованным SSL-соединением. Я создал действие Struts, которое подключается к внешнему серверу с авторизованным SSL-сертификатом клиента. В моем действии я пытаюсь отправить некоторые данные на сервер банка, но без успеха, потому что у меня в результате с сервера следующая ошибка:

error: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

мой метод из моего класса действий, который отправляет данные на сервер

//Getting external IP from host
    URL whatismyip = new URL("http://automation.whatismyip.com/n09230945.asp");
    BufferedReader inIP = new BufferedReader(new InputStreamReader(whatismyip.openStream()));

    String IPStr = inIP.readLine(); //IP as a String

    Merchant merchant;

    System.out.println("amount: " + amount + ", currency: " + currency + ", clientIp: " + IPStr + ", description: " + description);

    try {

        merchant = new Merchant(context.getRealPath("/") + "merchant.properties");

    } catch (ConfigurationException e) {

        Logger.getLogger(HomeAction.class.getName()).log(Level.INFO, "message", e);
        System.err.println("error: " + e.getMessage());
        return ERROR;
    }

    String result = merchant.sendTransData(amount, currency, IPStr, description);

    System.out.println("result: " + result);

    return SUCCESS;

мой купец.файл свойств:

bank.server.url=https://-servernameandport-/
https.cipher=-cipher-

keystore.file=-key-.jks
keystore.type=JKS
keystore.password=-password-
ecomm.server.version=2.0

encoding.source=UTF-8
encoding.native=UTF-8

впервые я думал, что это проблема с сертификатом, я преобразовал его .pfx to .следующих, но у меня такая же ошибка, без изменений.

16 ответов


сбой рукопожатия мог произойти по разным причинам:

  • несовместимые наборы шифров, используемые клиентом и сервером. Это потребует от клиента использования (или включения) набора шифров, поддерживаемого сервером.
  • несовместимые версии SSL в использовании (сервер может принимать только TLS v1, в то время как клиент способен использовать только SSL v3). Опять же, клиенту может потребоваться убедиться, что он использует совместимую версию SSL/TLS протокол.
  • неполный путь доверия для сертификата сервера; сертификат сервера, вероятно, не доверяет клиенту. Обычно это приводит к более подробной ошибке, но это вполне возможно. Обычно исправление заключается в импорте сертификата CA сервера в хранилище доверия клиента.
  • сертификат выдается для другого домена. Опять же, это привело бы к более подробному сообщению, но я изложу исправление здесь, если это является причиной. Этот разрешение в этом случае будет получить сервер (он не кажется вашим), чтобы использовать правильный сертификат.

поскольку основной сбой не может быть определен, лучше включить -Djavax.net.debug=all флаг для включения отладки установленного SSL-соединения. При включенной отладке вы можете точно определить, какая активность в рукопожатии не удалась.

обновление

основанный на деталях теперь доступных, похоже, что проблема связана с неполным путем доверия сертификата между сертификатом, выданным серверу, и корневым ЦС. В большинстве случаев это происходит потому, что сертификат корневого ЦС отсутствует в хранилище доверия, что приводит к ситуации, когда путь доверия сертификата не может существовать; сертификат по существу не доверяет клиенту. Браузеры могут представлять предупреждение, чтобы пользователи могли игнорировать это, но то же самое не относится к клиентам SSL (например, HttpsURLConnection класс или любая клиентская библиотека HTTP, такая как Apache HttpComponents Client).

большинство этих клиентских классов / библиотек будут полагаться на хранилище доверия, используемое JVM для проверки сертификата. В большинстве случаев, это будет cacerts файл в каталоге JRE_HOME/lib/security. Если местоположение хранилища доверия задано с помощью системного свойства JVM javax.net.ssl.trustStore, тогда магазин в этом пути обычно используется клиентская библиотека. Если вы сомневаетесь, взгляните на ваше Merchant class и выяснить класс / библиотеку, которую он использует для подключения.

добавление сертификата сервера, выдающего CA, в это хранилище доверия должно решить проблему. Вы можете обратиться к my ответ на вопрос о получении средств для этой цели, но утилита Java keytool достаточно для этой цели.

предупреждение: Хранилище доверия-это, по сути, список всех CAs, которым Вы доверяете. Если вы введете сертификат, не принадлежащий ЦС, которому вы не доверяете, то SSL/TLS-соединения с сайтами, имеющими сертификаты, выданные этой сущностью, могут быть расшифрованы, если доступен закрытый ключ.

Update #2: Понимание выходных данных трассировки JSSE

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

keyStore is : 
keyStore type is : jks
keyStore provider is : 
init keystore
init keymanager of type SunX509
trustStore is: C:\Java\jdk1.6.0_21\jre\lib\security\cacerts
trustStore type is : jks
trustStore provider is : 

если используется неправильный truststore, вам нужно будет повторно импортировать сертификат сервера в правильный или перенастроить сервер для использования указанного (не рекомендуется, если у вас есть несколько JVM, и все они используются для разных нужд).

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

adding as trusted cert:
  Subject: CN=blah, O=blah, C=blah
  Issuer:  CN=biggerblah, O=biggerblah, C=biggerblah
  Algorithm: RSA; Serial number: yadda
  Valid from SomeDate until SomeDate

вам нужно будет искать, если ЦС сервера является субъектом.

процесс рукопожатия будет иметь несколько заметных записей (вам нужно будет знать SSL, чтобы понять их подробно, но для отладки текущей проблемы достаточно знать, что handshake_failure обычно сообщается в ServerHello.

1. ClientHello

серия записей будет сообщена, когда соединение инициализируется. Первое сообщение, отправленное клиентом в SSL/TLS настройка соединения-это сообщение ClientHello, обычно сообщаемое в журналах как:

*** ClientHello, TLSv1
RandomCookie:  GMT: 1291302508 bytes = { some byte array }
Session ID:  {}
Cipher Suites: [SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA]
Compression Methods:  { 0 }
***

Примечание используемого шифра. Это возможно, придется согласиться с записью в вашем торговце.файл свойств для того же соглашения может использоваться библиотекой банка. Если используемая конвенция отличается, нет причин для беспокойства, поскольку ServerHello будет утверждать это, если набор шифров несовместим.

2. ServerHello

сервер отвечает с ServerHello, который будет указывать, если установка соединения может продолжаться. Записи в журналах обычно имеют следующий тип:

*** ServerHello, TLSv1
RandomCookie:  GMT: 1291302499 bytes = { some byte array}
Cipher Suite: SSL_RSA_WITH_RC4_128_SHA
Compression Method: 0
***

обратите внимание на набор шифров, который он выбрал; это лучший набор, доступный как для сервера, так и для клиента. Обычно набор шифров не указывается при возникновении ошибки. Сертификат сервера (и, возможно, всей цепочки) отправляется сервером и будет найден в записи как:

*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: CN=server, O=server's org, L=server's location, ST =Server's state, C=Server's country
  Signature Algorithm: SHA1withRSA, OID = some identifer

.... the rest of the certificate
***

если проверка сертификата прошла успешно, вы найдете запись, похожую на:

Found trusted certificate:
[
[
  Version: V1
  Subject: OU=Server's CA, O="Server's CA's company name", C=CA's country
  Signature Algorithm: SHA1withRSA, OID = some identifier

один из вышеперечисленных шагов не удался бы, в результате чего handshake_failure, поскольку рукопожатие обычно завершено на этом этапе (не совсем, но последующие этапы рукопожатия обычно не вызывают сбоя рукопожатия). Вам нужно будет выяснить, какой шаг не удался, и опубликовать соответствующее сообщение в качестве обновления вопрос (если вы уже не поняли сообщение, и вы знаете, что делать, чтобы его решить).


установка Java Cryptography Extension (JCE) неограниченная сила (для JDK7 | для JDK8) исправит эту ошибку. Распакуйте файл и следуйте за readme, чтобы установить его.


Я не думаю, что это решает проблему первому вопрошающему, но для гуглеров, приходящих сюда за ответами:


в обновлении 51 java 1.8 запрещены [1] шифры RC4 по умолчанию, как мы видим на странице заметок о выпуске:

Исправлена Ошибка: запретить набор шифров RC4

RC4 теперь рассматривается как скомпрометированный шифр.

наборы шифров RC4 были удалены как с клиента, так и с сервера по умолчанию список наборов шифров в реализации Oracle JSSE. Эти комплекты шифров все еще могут быть включены SSLEngine.setEnabledCipherSuites() и SSLSocket.setEnabledCipherSuites() методы. См. JDK-8077109 (не является общедоступным).

если ваш сервер имеет сильное предпочтение этому шифру (или использует только этот шифр), это может вызвать handshake_failure на java.

вы можете проверить подключение к серверу позволяет шифры RC4 (сначала попробуйте без


Это также может произойти, когда клиенту необходимо представить сертификат. После того, как сервер перечисляет цепочку сертификатов, может произойти следующее:

3. Запрос Сертификата Сервер выдаст запрос на сертификат от клиента. В запросе будут перечислены все сертификаты, которые принимает сервер.

*** CertificateRequest
Cert Types: RSA
Cert Authorities:
<CN=blah, OU=blah, O=blah, L=blah, ST=blah, C=blah>
<CN=yadda, DC=yadda, DC=yadda>
<CN=moreblah, OU=moreblah, O=moreblah, C=moreblah>
<CN=moreyada, OU=moreyada, O=moreyada, C=moreyada>
... the rest of the request
*** ServerHelloDone

4. Цепочка Сертификатов Клиента Это сертификат, который клиент отправляет на сервер.

*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: EMAILADDRESS=client's email, CN=client, OU=client's ou, O=client's Org, L=client's location, ST=client's state, C=client's Country
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
  ... the rest of the certificate
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1    
... key exchange info 

если в цепочке нет сертификата, и сервер требует его, здесь вы получите ошибку рукопожатия. Вероятной причиной является то, что путь к вашему сертификату не найден.

5. Сертификат Проверить Клиент просит сервер проверить сертификат

*** CertificateVerify
... payload of verify check

этот шаг произойдет, только если вы отправляете сертификат.

6. Кончено Сервер ответит проверяющим ответом

*** Finished
verify_data:  { 345, ... }

сбой рукопожатия может быть ошибочной реализацией протокола TLSv1.

в нашем случае это помогло с Java 7:

java -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1 

jvm будет вести переговоры в этом порядке. Серверы с последним обновлением будут делать 1.2, багги будут спускаться до v1, и это работает с аналогичным v1 в java 7.


У меня есть эта ошибка, когда я пытался использовать JDK 1.7. Когда я модернизировал свой JDK для jdk1.8.Все 0_66 начал работать нормально.

таким образом, самым простым решением этой проблемы может быть - обновите JDK и это может начать работать хорошо.


предполагая, что вы используете правильные протоколы SSL/TLS, правильно настроили ваш keyStore и trustStore, и подтвердил, что нет никаких проблем с самими сертификатами, вам может потребоваться укрепление алгоритмов безопасности.

как говорится в Винеет по, одна из возможных причин, по которой вы получаете эту ошибку, связана с использованием несовместимых наборов шифров. Путем обновления моего local_policy и US_export_policy банки в моем JDK's security папка с те, которые предусмотрены в расширение криптографии Java (JCE), я смог успешно завершить рукопожатие.


я встречаю ту же проблему сегодня с клиентом OkHttp, чтобы получить url-адрес на основе https. Это было вызвано несоответствием версии протокола Https и метода шифрования между стороной сервера и стороной клиента.

1) проверьте версию протокола https вашего сайта и метод шифрования.

openssl>s_client -connect your_website.com:443 -showcerts

вы получите много подробной информации, ключевая информация отображается следующим образом:

SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-SHA
2) настройте свой http-клиент, например, в клиент OkHttp случай:
@Test()
public void testHttpsByOkHttp() {
    ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
            .tlsVersions(TlsVersion.TLS_1_0) //protocol version
            .cipherSuites(
                    CipherSuite.TLS_RSA_WITH_RC4_128_SHA, //cipher method
                    CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                    CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                    CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
            .build();

    OkHttpClient client = new OkHttpClient();
    client.setConnectionSpecs(Collections.singletonList(spec));
    Request request = new Request.Builder().url("https://your_website.com/").build();
    try {
        Response response = client.newCall(request).execute();
        if(response.isSuccessful()){
            logger.debug("result= {}", response.body().string());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Это получит то, что мы хотим.


я нашел сервер HTTPS, который потерпел неудачу таким образом, если мой клиентский процесс Java был настроен с

-Djsse.enableSNIExtension=false

соединение не удалось с handshake_failure после ServerHello был успешно завершен, но до начала потока данных.

не было четкого сообщения об ошибке, которое определило проблему, ошибка просто выглядела как

main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1.2 ALERT:  fatal, handshake_failure
%% Invalidated:  [Session-3, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

я изолировал проблему, пытаясь С и без "-Djsse.enableSNIExtension=false" опции


- моя TLS версия несовместима ошибки.

ранее было TLSv1 Я изменил его TLSV1.2 это решило мою проблему.


Я использую com.гуглить.api http клиент. Когда я общаюсь с внутренним сайтом компании, я получил эту проблему, когда я ошибочно использовал https вместо http.

main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1.2 ALERT:  fatal, handshake_failure
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
main, IOException in getSession():  javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
main, called close()
main, called closeInternal(true)
262 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection  - Connection shut down
main, called close()
main, called closeInternal(true)
263 [main] DEBUG org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager  - Released connection is not reusable.
263 [main] DEBUG org.apache.http.impl.conn.tsccm.ConnPoolByRoute  - Releasing connection [HttpRoute[{s}->https://<I-replaced>]][null]
263 [main] DEBUG org.apache.http.impl.conn.tsccm.ConnPoolByRoute  - Notifying no-one, there are no waiting threads
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:431)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:339)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:123)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:147)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:108)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:415)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)
    at com.google.api.client.http.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:67)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:960)

У меня была аналогичная проблема; обновление до Apache HTTPClient 4.5.3 исправило ее.


в моем случае сертификат импортируется, ошибка остается, решается это путем добавления System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3"); прежде чем подключить


угги! Это оказалось просто проблемой версии Java для меня. Я получил ошибку рукопожатия с помощью JRE 1.6, и все отлично работало с помощью JRE 1.8.0_144.


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

Я получал эту ошибку при использовании Parasoft SOATest для отправки запроса XML (SOAP) .

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


в моем случае веб-сайт просто может использовать TLSv1.2. и я использую apache httpclient 4.5.6, я использую этот код и устанавливаю jce для решения этого (JDK1.7):

jce

jdk7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

jdk 8 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

код:

SSLContext sslContext = SSLContext.getDefault();

  SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(
      sslContext,
      new String[]{"TLSv1.2"}, // important
      null,
      NoopHostnameVerifier.INSTANCE);

  Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
      .register("https", sslConnectionFactory)
      .register("http", PlainConnectionSocketFactory.INSTANCE)
      .build();

  HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager(registry);
  httpclient = HttpClientBuilder.create().
      .setSSLSocketFactory(sslConnectionFactory)
      .setConnectionManager(ccm)
      .build();