Получено фатальное предупреждение: сбой рукопожатия через 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 .jks, но у меня та же ошибка, без изменений.

2145   16  

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 класс, и выяснить класс/библиотеку, которую он использует, чтобы сделать соединение.

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

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

обновление #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

вам нужно будет искать, если CA сервера-это тема.

процесс рукопожатия будет иметь несколько заметных записей (вам нужно будет знать 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) Unlimited Strength (для JDK7/для JDK8) исправит эту ошибку. Распакуйте файл и следуйте инструкциям readme для его установки.

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


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

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

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

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

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

вы можете проверить подключение к серверу, включив шифры RC4 (сначала попробуйте без enabled аргумент, чтобы увидеть, если триггеры a handshake_failure, затем установите enabled:

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;

import java.util.Arrays;

/** Establish a SSL connection to a host and port, writes a byte and
 * prints the response. See
 * http://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services
 */
public class SSLRC4Poke {
    public static void main(String[] args) {
        String[] cyphers;
        if (args.length < 2) {
            System.out.println("Usage: "+SSLRC4Poke.class.getName()+" <host> <port> enable");
            System.exit(1);
        }
        try {
            SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(args[0], Integer.parseInt(args[1]));

            cyphers = sslsocketfactory.getSupportedCipherSuites();
            if (args.length ==3){
                sslsocket.setEnabledCipherSuites(new String[]{
                    "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                    "SSL_DH_anon_WITH_RC4_128_MD5",
                    "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                    "SSL_RSA_WITH_RC4_128_MD5",
                    "SSL_RSA_WITH_RC4_128_SHA",
                    "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
                    "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
                    "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
                    "TLS_ECDH_RSA_WITH_RC4_128_SHA",
                    "TLS_ECDH_anon_WITH_RC4_128_SHA",
                    "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
                    "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
                    "TLS_KRB5_WITH_RC4_128_MD5",
                    "TLS_KRB5_WITH_RC4_128_SHA"
                });     
            }

            InputStream in = sslsocket.getInputStream();
            OutputStream out = sslsocket.getOutputStream();

            // Write a test byte to get a reaction :)
            out.write(1);

            while (in.available() > 0) {
                System.out.print(in.read());
            }
            System.out.println("Successfully connected");

        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

1 - https://www.java.com/en/download/faq/release_changes.xml

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

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, и подтвердил, что не существует никаких проблем с самими сертификатами, вам может понадобиться укрепить свои алгоритмы безопасности.

как говорится в ответ Vineet, одна из возможных причин, по которой вы получаете эту ошибку, связана с использованием несовместимых наборов шифров. Обновляя мой local_policy и US_export_policy банки в моем JDK security папка с те, которые предусмотрены в Java Cryptography Extension (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();

Comments

    Ничего не найдено.