Получено фатальное предупреждение: handshake_failure через SSLHandshakeException - PullRequest
112 голосов
/ 15 июня 2011

У меня проблема с авторизованным соединением SSL.Я создал действие Struts, которое подключается к внешнему серверу с помощью сертификата Client Authorized 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;

Мой файл merchant.properties:

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 в .jks, но у меня естьта же ошибка, без изменений.

Ответы [ 17 ]

223 голосов
/ 15 июня 2011

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

  • Несовместимые комплекты шифров, используемые клиентом и сервером.Это потребует от клиента использования (или включения) набора шифров, поддерживаемого сервером.
  • Используются несовместимые версии 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 .

Предупреждение : хранилище доверенных сертификатов - это список всех доверенных центров сертификации.Если вы вставите сертификат, который не принадлежит ЦС, которому вы не доверяете, то соединения 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 : 

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

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

Обратите внимание на используемые комплекты шифров. Для этого может потребоваться согласие с записью в вашем файле merchant.properties, поскольку такое же соглашение может быть использовано библиотекой банка. Если используется другое соглашение, нет причин для беспокойства, так как 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, поскольку рукопожатие обычно завершается на этом этапе (не совсем, но последующие этапы рукопожатия обычно не вызывают сбой рукопожатия). Вам необходимо выяснить, на каком этапе произошел сбой, и опубликовать соответствующее сообщение в качестве обновления вопроса (если вы уже не поняли сообщение и не знаете, что делать, чтобы его решить).

19 голосов
/ 08 ноября 2016

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

15 голосов
/ 17 июля 2015

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


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

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

RC4 теперь считается взломанным шифром.

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

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

Вы можете проверить подключение к серверу, включающему шифры RC4 (сначала попробуйте без аргумента enabled проверить, запускает ли 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

15 голосов
/ 13 июня 2013

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

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, ... }
11 голосов
/ 28 сентября 2016

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

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

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

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

8 голосов
/ 10 декабря 2015

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

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

3 голосов
/ 17 июня 2016

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

Как упоминалось в ответе Vineet , одна из возможных причин, по которой вы получаете эту ошибку, связана с использованием несовместимых наборов шифров. Обновив мои jar-файлы local_policy и US_export_policy в папке security моего JDK с помощью файлов, предусмотренных в Java Cryptography Extension (JCE) , я смог успешно завершить рукопожатие.

3 голосов
/ 04 июля 2018

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

2 голосов
/ 25 января 2018

Я обнаружил 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" и без нее

2 голосов
/ 05 ноября 2015

Сегодня я сталкиваюсь с той же проблемой с клиентом 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 client case:
@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();
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...