Java SSL SSLHandshakeException handshake_failure

Posted

技术标签:

【中文标题】Java SSL SSLHandshakeException handshake_failure【英文标题】: 【发布时间】:2015-05-16 21:06:26 【问题描述】:

我正在使用 java 8 并使用 SSLSocket 请求此 url: https://www.flixbus.de/sites/default/files/6_jetzt_buchen_button_tiny.png

我总是收到握手错误。 socket.setEnabledProtocols() 强制使用特定协议没有帮助。

顺便说一句,所有其他网络服务器都可以正常工作。所以它可能与我的代码无关。

在没有任何 ssl/tls 协议限制的情况下也会发生此错误。

有什么想法吗?

选项“javax.net.debug=all”的输出如下:

DEBG getSecureScocket: Supported protocols:
- SSLv2Hello
- SSLv3
- TLSv1
- TLSv1.1
- TLSv1.2
DEBG getSecureScocket: Enabled protocols:
- TLSv1.2
Http Client, setSoTimeout(120000) called
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false

%% No cached client session
*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1426330417 bytes =  229, 161, 185, 11, 136, 85, 35, 88, 166, 144, 191, 126, 10, 196, 215, 241, 43, 190, 221, 246, 240,
217, 82, 29, 180, 106, 35, 253 
Session ID:  
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_
AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH
_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_A
ES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WIT
H_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WIT
H_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_12
8_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:   0 
Extension elliptic_curves, curve names: secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1,
secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r
2, secp224k1, sect239k1, secp256k1
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA25
6withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
***
[write] MD5 and SHA1 hashes:  len = 193
[Raw write]: length = 198
[Raw read]: length = 5
[Raw read]: length = 2
Http Client, READ: TLSv1.2 Alert, length = 2
Http Client, RECV TLSv1.2 ALERT:  fatal, handshake_failure
Http Client, called closeSocket()
Http Client, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Http Client, called close()
Http Client, called closeInternal(true)
Http Client, called close()
Http Client, called closeInternal(true)

Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
        at sun.security.ssl.Alerts.getSSLException(Unknown Source)
        at sun.security.ssl.Alerts.getSSLException(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.recvAlert(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
        at sun.security.ssl.SSLSocketImpl.writeRecord(Unknown Source)
        at sun.security.ssl.AppOutputStream.write(Unknown Source)
        at java.io.OutputStream.write(Unknown Source)
        [...]

【问题讨论】:

您是否尝试过启用所有支持的协议而不仅仅是 TLSv1.2?只是看看这是否是问题所在(因为您可能已经知道,SSLvX 不再推荐用于生产) 是的。首先,我使用启用所有协议的默认值。也不行。我找到了这个解决方案here。这就是我限制协议并希望它会有所帮助的原因。 【参考方案1】:

该站点需要SNI (server name indication),即没有SNI,握手将失败。所有现代浏览器都支持 SNI,但并非所有编程语言中的 SSL 堆栈都支持或默认使用 SNI。有关如何在 Java 8 中使用 SNI,请参阅 http://www.vimino.com/2014/01/jep-114-tls-sni-extension-sunjsse-behavior-changes/。

你可以用openssl s_client检查这种行为:

# without SNI
$ openssl s_client -connect www.flixbus.de:443
CONNECTED(00000003)
140612985652896:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:770:
....
New, (NONE), Cipher is (NONE)

# with SNI
$ openssl s_client -connect www.flixbus.de:443  -servername 'www.flixbus.de'
CONNECTED(00000003)
...
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256

或者你可以使用analyze.pl:

$ perl analyze-ssl.pl  www.flixbus.de
-- www.flixbus.de port 443
 * maximum SSL version  : TLSv1_2 (SSLv23)
 ...
 * SNI supported        : SSL upgrade fails without SNI

【讨论】:

非常感谢您指出这个(新的)SNI 扩展。这确实解决了问题。 @steffen-ullrich 嗨,如果我运行你的脚本,我会收到IO::Socket::SSL version 1.984 required--this is only version 1.31 at analyze-ssl.pl line 10.。对非 perler 的任何提示? @injecteer:您使用的 1.31 版本已经超过 5 年了,所以我想您需要以某种方式获得更新的版本。如果您在 Windows 上,最新的 Strawberry Perl 应该包含您需要的内容。如果您使用的是 Linux,则最新的发行版包含您需要的内容。 您如何确定此站点需要 SNI? @heez:使用我在答案中提到的工具。但你也可以使用SSLLabs。【参考方案2】:

这个错误可能是服务器不支持你使用的Tls1.2

【讨论】:

我限制为 TLS1.2,因为服务器使用 TLS1.2 回复。至少这是我从日志中了解到的。如果我不做任何协议限制,它也不起作用。

以上是关于Java SSL SSLHandshakeException handshake_failure的主要内容,如果未能解决你的问题,请参考以下文章

java实现ssl单/双向认证通信[推荐]

Java基础

配置Java SSL 访问网站证书

java 程序配置 SSL

关于java的SSL https协议

如何忽略 SSL 证书 Java