【发布时间】:2019-01-14 09:35:47
【问题描述】:
我使用 Nexus 5X(API 级别 27)作为 React Native 应用程序的模拟器。我正在尝试使用 SSLSockets 将它连接到我的 java 服务器,但在连接后立即出现错误:握手失败
SSL handshake aborted: ssl=0x9613a1c0: Failure in SSL library, usually a protocol error
error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/tls_record.cc:579 0x9679afc0:0x00000001)
error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/handshake_client.cc:893 0xa43b3dc3:0x00000000)
invoke JavaMethodWrapper.java:383
invoke JavaModuleWrapper.java:160
run NativeRunnable.java
handleCallback Handler.java:790
dispatchMessage Handler.java:99
dispatchMessage MessageQueueThreadHandler.java:29
loop Looper.java:164
run MessageQueueThreadImpl.java:192
run Thread.java:764*
由于错误消息,有一段时间我认为它使用的是 SSLv3,但在检查 Wireshark 中的连接后,我注意到它实际上是 TLSv1.2。然后我尝试为服务器和android客户端强制执行特定的密码套件,但没有运气。编写自定义 SocketFactory 类也没有帮助。
我发现了另一个可能与我的问题有关的问题:SSLHandshakeException: Handshake failed on Android N/7.0。我仍然相当确信我的问题与受支持的密码套件和/或椭圆曲线密码学有关。
Android 客户端代码
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket connection = (SSLSocket) factory.createSocket("127.0.0.1", 4192);
connection.setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
connection.setUseClientMode(true);
DataOutputStream output = new DataOutputStream(connection.getOutputStream());
DataInputStream input = new DataInputStream(connection.getInputStream());
String[] cipher_suites = connection.getSupportedCipherSuites();
connection.setEnabledCipherSuites(cipher_suites);
connection.startHandshake();
服务器代码
SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket server = (SSLServerSocket) factory.createServerSocket(4192, 1);
SSLSocket connection = (SSLSocket) server.accept();
DataInputStream input = new DataInputStream(connection.getInputStream());
DataOutputStream output = new DataOutputStream(connection.getOutputStream());
connection.setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
String[] cipher_suites = connection.getSupportedCipherSuites();
connection.setEnabledCipherSuites(cipher_suites);
connection.startHandshake();
服务器本身工作正常,可以使用完全相同的代码从另一台计算机连接。它在 openjdk 10.0.1 上运行。与安卓版本相同。有趣的是,它在发行说明中写道:
安全库/javax.net.ssl 3DES 密码套件已禁用 为了提高 SSL/TLS 连接的强度,已通过 jdk.tls.disabledAlgorithms 安全属性在 JDK 中的 SSL/TLS 连接中禁用 3DES 密码套件。
模拟器中的android版本是8.1,我也在物理设备上用7.0测试过。错误信息都是一样的。有没有其他人遇到过这个问题?
捕获的密码套件
这些是密码套件,以及确认的 TLS 版本,我收到了客户端 Hello
Version: TLS 1.2 (0x0303)
Cipher Suites (14 suites)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
随后是握手失败警报:
TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Handshake Failure)
Content Type: Alert (21)
Version: TLS 1.2 (0x0303)
Length: 2
Alert Message
Level: Fatal (2)
Description: Handshake Failure (40)
【问题讨论】:
-
由于您已经设置了 Wireshark,您应该详细说明您在那里看到的内容。我认为这是理解问题的关键。尤其要查看 CLIENT_HELLO 消息(TLS 版本,支持的密码)以及服务器对此消息的反应。
-
@Robert 我比较了服务器提供的 65 个密码套件和我的模拟器提供的 14 个密码套件。似乎有一些常见的,例如: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 。忘了提 127.0.0.1 实际上不是我使用的地址。这是一个环回地址,我认为最好不要在此处发布。
-
如果服务器和客户端同意使用
ECDHE密码,您可能会遇到您在问题中引用的错误。对于测试,我将在客户端仅启用一个或两个基于RSA_WITH_AES的密码(如果服务器支持它们)。我还将使用testssl.sh 之类的脚本测试服务器端。