我尝试在Android应用中的另一个SSLSocket
上创建SSLSocket
。较低的连接是与Secure Web Proxy(基于SSL的HTTP代理)的SSL加密连接,上层连接用于HTTP over SSL(HTTPS)。
为此,我使用SSLSocketFactory的createSocket()
函数,该函数允许传递一个现有的Socket来运行SSL连接,如下所示:
private Socket doSSLHandshake(Socket socket, String host, int port) throws IOException {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){ return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new SecureRandom());
SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, true);
sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());
sslSocket.setEnableSessionCreation(true);
sslSocket.startHandshake();
return sslSocket;
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new IOException("Could not do handshake: " + e);
}
}
当底层套接字是普通的tcp套接字时,此代码工作正常,但当我使用之前使用上述代码创建的SSLSocket作为底层套接字时,握手失败并出现以下异常:
javax.net.ssl.SSLHandshakeException: Handshake failed
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:429)
at com.myapp.MyThreadClass.doSSLHandshake(MyThreadClass.java:148)
at com.myapp.MyThreadClass.run(MyThreadClass.java:254)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x7374d56e80: Failure in SSL library, usually a protocol error
error:100000e3:SSL routines:OPENSSL_internal:UNKNOWN_ALERT_TYPE (external/boringssl/src/ssl/s3_pkt.c:618 0x738418ce7e:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357)
... 2 more
我在Android 7.1.1上进行测试。该应用针对的是SDK级别23。
非常感谢任何帮助!
更新:完全相同的代码适用于Mac上的JRE 1.8,但不适用于Android。
更新2 :从概念上讲,这些是连接所经历的步骤:
- 从Android应用程序,建立与安全代理服务器(套接字)的连接
- 与代理服务器(SSLSocket over Socket)进行SSL / TLS握手
- 通过SSLSocket,将CONNECT消息发送到代理服务器
- 代理连接到目标(https)服务器,仅从现在起复制字节
- 与目标(https)服务器进行SSL / TLS握手(SSLSocket over SSocket over SSocket)
- 将GET消息发送到目标服务器并阅读响应
醇>当在SSLSocket上进行握手时,问题出现在步骤5中 它遍历SSLSocket(通过Socket)。
更新3 :我现在已经打开了一个包含示例项目和tcpdumps的GitHub仓库:https://github.com/FD-/SSLviaSSL
注意:我找到并阅读了a question with very similar title,但不幸的是它没有太多有用的帮助。
答案 0 :(得分:7)
我认为你做错了什么。看起来在第二次握手期间协议协商中存在错误。一个好的候选人将在NPN TLS握手扩展中失败。
在此次通话中查看您的协议:sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());
您可以浏览列出的协议并单独尝试。看看您是否可以锁定失败的内容以及是否需要支持该特定协议或扩展。
答案 1 :(得分:7)
所以我试图找出在android的情况下出了什么问题,但到目前为止我没有发现你的代码有什么问题。此外,由于代码适用于JRE,因此它断言了这一假设。
从您提供的tcpdump中,可以获得大量信息,以了解Android如何使用与JRE相同的API集合。
让我们来看看JRE tcpdump:
现在让我们来看看android tcpdump:
来自proxy / stunnel wireshark的分析:
JRE案例:
在JRE的情况下,客户端与stunnel /代理服务器进行初始SSL握手。同样可以在下面看到:
握手成功,连接完成
然后客户端尝试连接到远程服务器(www.google.com)并开始握手。因此客户端发送的客户端问候语被视为数据包#34中的加密消息,当stunnel对其进行解密时,可以在“客户端问候”中看到它,该信息由stunnel转发到代理服务器
如上所示,从客户端到stunnel / proxy的初始SSL握手成功。
然后当android客户端启动与远程(www.google.com)的握手时,理想情况下它应该使用SSL套接字。如果是这种情况,我们应该看到从android到stunnel的加密流量(类似于JRE情况下的#34包),stunnel应该解密并向代理发送“client hello”。但是正如你在下面看到的那样,android客户端正在通过普通套接字发送“客户端问候”。
如果将数据包#24与来自JRE的数据包#34进行比较,我们可以发现这种差异。
<强>结论:强>
这是android SSL(factory.createsocket()
带SSL套接字)实现的错误,我觉得使用相同的API集可能没有相同的魔法解决方法。事实上我在android bug列表中发现了这个问题。见以下链接:
https://code.google.com/p/android/issues/detail?id=204159
这个问题仍未解决,您可以跟进Android开发团队进行修复。
可能的解决方案:
如果我们得出结论认为同一组API无效,那么您只剩下一个选项:
答案 2 :(得分:0)
答案 3 :(得分:0)
我不知道这是否有帮助但是:
我已经构建了你的repo测试环境,我的错误略有不同:
I/SurfaceTextureClient(20733): [0x52851b98] frames:2, duration:1.005000, fps:1.989805
I/System.out(20733): [socket][2] connection /192.168.1.100:10443;LocalPort=35380(0)
I/System.out(20733): [CDS]connect[/192.168.1.100:10443] tm:90
I/System.out(20733): [socket][/192.168.1.123:35380] connected
I/System.out(20733): Doing SSL handshake with 192.168.1.100:10443
I/System.out(20733): Supported protocols are: [SSLv3, TLSv1, TLSv1.1, TLSv1.2]
E/NativeCrypto(20733): ssl=0x53c96268 cert_verify_callback x509_store_ctx=0x542e0a80 arg=0x0
E/NativeCrypto(20733): ssl=0x53c96268 cert_verify_callback calling verifyCertificateChain authMethod=RSA
I/System.out(20733): Doing SSL handshake with 192.168.1.100:443
I/System.out(20733): Supported protocols are: [SSLv3, TLSv1, TLSv1.1, TLSv1.2]
E/NativeCrypto(20733): Unknown error during handshake
I/System.out(20733): Shutdown rx/tx
I/System.out(20733): [CDS]close[35380]
I/System.out(20733): close [socket][/0.0.0.0:35380]
W/System.err(20733): javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException:
SSL handshake aborted: ssl=0x53c9c1d8:
Failure in SSL library, usually a protocol error
W/System.err(20733): error:140770FC:SSL routines:
SSL23_GET_SERVER_HELLO:
unknown protocol (external/openssl/ssl/s23_clnt.c:766 0x4e7cb3ad:0x00000000)
W/System.err(20733): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:413)
W/System.err(20733): at com.bugreport.sslviassl.SecureWebProxyThread.doSSLHandshake(SecureWebProxyThread.java:147)
W/System.err(20733): at com.bugreport.sslviassl.SecureWebProxyThread.run(SecureWebProxyThread.java:216)
W/System.err(20733): Caused by: javax.net.ssl.SSLProtocolException:
SSL handshake aborted: ssl=0x53c9c1d8:
Failure in SSL library, usually a protocol error
W/System.err(20733): error:140770FC:SSL routines:
SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:766 0x4e7cb3ad:0x00000000)
W/System.err(20733): at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
W/System.err(20733): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:372)
W/System.err(20733): ... 2 more
I/SurfaceTextureClient(20733): [0x52851b98] frames:5, duration:1.010000, fps:4.946089
关于线程的一个想法:你在运行doSSLHandshake()方法的线程是什么?
public void policy()
{
int SDK_INT = android.os.Build.VERSION.SDK_INT;
if (SDK_INT > 8)
{
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
}
答案 4 :(得分:0)
事实证明,这确实是我测试过的任何Android设备上默认SSL提供程序中的错误(尽管我最新的设备是运行Android 7.1.1的Nexus 9)。 最终,我发现Conscrypt SSL提供程序的独立版本的基于引擎的SSLSocket实现(当时刚刚发布)在Android上完全按照我期望的方式运行。
有关更多详细信息,请查看与GitHub上的Conscrypt维护者的讨论:https://github.com/google/conscrypt/issues/104