Получено фатальное предупреждение: сбой рукопожатия через 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, но у меня та же ошибка, без изменений.
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. Если расположение хранилища доверия было указано с помощью системного свойства JVMjavax.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, поскольку рукопожатие обычно завершается на этом этапе (не совсем, но последующие этапы рукопожатия обычно не вызывают сбоя рукопожатия). Вам нужно будет выяснить, какой шаг не удался, и опубликовать соответствующее сообщение в качестве обновления вопрос (если вы уже поняли сообщение, и вы знаете, что делать, чтобы решить ее).
Я не думаю, что это решает проблему для первого вопрошающего, но для гуглеров, приходящих сюда за ответами:
на обновление 51, java 1.8 запрещены [1] RC4 шифры по умолчанию, как мы можем видеть на странице примечания к выпуску:
Исправлена Ошибка: запретить RC4 cipher suites
RC4 теперь рассматривается как скомпрометированный шифр.
комплекты шифров RC4 были удалены как с клиента, так и с сервера по умолчанию включены список наборов шифров в реализации Oracle JSSE. Эти наборы шифров все еще могут быть включены с помощью
SSLEngine.setEnabledCipherSuites()иSSLSocket.setEnabledCipherSuites()методы. См. JDK-8077109 (не публичный).если ваш сервер имеет сильное предпочтение этого шифра (или использовать только этот шифр), это может спровоцировать
handshake_failureна java.вы можете проверить подключение к серверу, включив шифры RC4 (сначала попробуйте без
enabledаргумент, чтобы увидеть, если триггеры ahandshake_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 *** ServerHelloDone4. Цепочка Сертификатов Клиента Это сертификат, который клиент отправляет на сервер.
*** 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,TLSv1jvm будет вести переговоры в этом порядке. Серверы с последним обновлением будут делать 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банки в моем JDKsecurityпапка с те, которые предусмотрены в Java Cryptography Extension (JCE), я смог успешно завершить рукопожатие.
я встречаю ту же проблему сегодня с клиентом OkHttp, чтобы получить url-адрес на основе https. Это было вызвано несоответствием версии протокола Https и метода шифрования между стороной сервера и стороной клиента.
1) проверьте версию протокола https вашего сайта и метод шифрования.
openssl>s_client -connect your_website.com:443 -showcertsвы получите много подробной информации, ключевая информация отображается следующим образом:
2) сконфигурируйте свой http-клиент, например, в клиент OkHttp случай:SSL-Session: Protocol : TLSv1 Cipher : RC4-SHA@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)
в моем случае сертификат импортируется, ошибка остается, решается это путем добавления
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