关于java:Android 6及以上Android SSLSocket握手失败 | 珊瑚贝

Android SSLSocket handshake failure in Android 6 and above


我已经编写了一个基于 Java SSLServerSocket 的服务器,它接受连接并通过自定义二进制协议与 android 应用程序通信:

1
2
3
4
5
ServerSocket serverSocket = SSLServerSocketFactory.getDefault().createServerSocket(1234);
while (true) {
    Socket socket = serverSocket.accept();
    …
}

我使用以下参数运行服务器:

1
2
Djavax.net.ssl.keyStore=keystore.jks
Djavax.net.ssl.keyStorePassword=<PASSWORD>

证书是使用以下构建公钥和私钥集的教程生成的:http://judebert.com/progress/archives/425-Using-SSL-in-Java,-Part-2.html:

1
2
3
keytool genkeypair keystore keystore.jks alias keyname
keytool export alias keyname file keyname.crt keystore keystore.jks
keytool importcert file keyname.crt keystore truststore.jks

另外,我通过使用 bouncycastle 构建信任库来使其与 android 兼容:

1
keytool importkeystore srckeystore truststore.jks srcstoretype JKS srcstorepass <PASSWORD> destkeystore truststore.bks deststoretype BKS deststorepass <PASSWORD> provider org.bouncycastle.jce.provider.BouncyCastleProvider providerpath bcprovextjdk15on1.58.jar

在此处下载 bouncycastle 提供程序:https://www.bouncycastle.org/latest_releases.html

并将生成的 truststore.bks 移动到原始资源文件夹中。

在 Android 上,我使用以下代码构建一个 SSLSocketFactory,它允许我导入生成的 bouncycastle 证书,该证书可以针对服务器对我进行身份验证:

1
2
3
4
5
6
7
8
9
10
11
12
KeyStore trustStore = KeyStore.getInstance(“BKS”);
InputStream trustStoreStream = context.getResources().openRawResource(R.raw.truststore);
trustStore.load(trustStoreStream,“<PASSWORD>”.toCharArray());

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);

SSLContext sslContext = SSLContext.getInstance(“TLS”);
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

Socket socket = sslContext.getSocketFactory().createSocket(“ip”, 1234);
use socket

这适用于低于 6 的 Android 版本。我的问题是在版本 6 及更高版本上尝试使用套接字时出现异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 Shutting down connection Socket[address=/ip,port=1234,localPort=321321] due to exception Handshake failed
 javax.net.ssl.SSLHandshakeException: Handshake failed
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429)
    at com.example.Client.connect(Client.java:97)
    at com.example.Client.start(Client.java:60)
    at com.example.BackendServiceFactory$2.call(BackendServiceFactory.java:136)
    at com.example.BackendServiceFactory$2.call(BackendServiceFactory.java:130)
    …
 Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0xe69ec900: Failure in SSL library, usually a protocol error
 error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:641 0xe2d10880:0x00000001)
 error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/s3_clnt.c:800 0xe6ea5af3:0x00000000)
    at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
    at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
    … 24 more

我不确定这里发生了什么。在处理客户端证书的过程中似乎有一个失误,这可能是密码套件不匹配吗?

我已经用 Java 服务器、Java 客户端和 Android 客户端组合了一个最小示例来帮助诊断此问题:

https://github.com/johncarl81/androidCA

  • 你在使用开放 SSL 吗?
  • 您是否尝试过在证书上设置主题替代名称,如 stackoverflow.com/questions/8744607/…
  • @chandrakantsharma:不
  • @sapensadler:我可以试试,但我不确定这会有什么不同。
  • 我建议它的唯一原因是因为 chrome 58 不再使用通用名称,而是使用 SAN 来识别证书。我猜因为他们都是谷歌,所以安卓可能也会效仿。 support.google.com/chrome/a/answer/7391219?hl=en 我可能完全错了,但可能值得一试。它还可以解释为什么它适用于以前的版本,但如果行为发生变化,则不能在 6 之后。


我认为这将是一个简单的修复。似乎我需要在第一个 keytool 命令中指定密钥算法:

1
keytool genkeypair keystore keystore.jks alias keyname keyalg RSA

这会生成一个 2048 位 RSA 密钥,它与 android < 6 和 >= 6 的版本兼容。


来源:https://www.codenong.com/46375111/

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?