SSLSocket通过另一个SSLSocket

我试图在Android应用程序中的另一个SSLSocket顶部创build一个SSLSocket 。 较低的连接是安全Web代理 (基于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套接字时,此代码工作正常,但是当我使用上面的代码创build的SSLSocket作为底层套接字之前,握手失败,出现以下exception:

 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 

我正在testingAndroid 7.1.1。 该应用程序的目标是23级的SDK。

  • 我可能做错了什么?
  • 我怎么能进一步debugging这个问题?
  • 有没有人有一个SSLSocket在最近的Android版本上的另一个SSLSocket的工作示例?

任何帮助是极大的赞赏!


更新 :相同的代码在Mac上的JRE 1.8中工作,但不在Android上。


更新2 :从概念上讲,这些是连接经过的步骤:

  1. 从Android应用程序,连接到安全代理服务器(套接字)
  2. 与代理服务器(Socket上的SSLSocket)进行SSL / TLS握手
  3. 通过SSLSocket,发送CONNECT消息到代理服务器
  4. 代理连接到目标(https)服务器,从现在开始只复制字节
  5. 与目的地(https)服务器(SSLSocket通过Socket上的SSLSocket)进行SSL / TLS握手
  6. 将GET消息发送到目标服务器并读取响应

问题出现在第5步,当执行SSLSocket上的SSLSocket握手(通过套接字)。


更新3 :我现在已经打开一个示例项目和tcpdumps的GitHub回购: https : //github.com/FD-/SSLviaSSL


注意 :我发现并阅读了一个非常相似的标题 ,但不幸的是它没有包含太多有用的帮助。

我不认为你做错了什么。 看起来你在第二次握手期间在协议协商中有一个错误。 一个好的候选人将会在NPN TLS握手扩展中失败。

在这个调用中看看你的协议: sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());

您可以遍历所列出的协议并单独尝试。 看看你是否可以locking什么是失败的,是否需要支持特定的协议或扩展。

所以我试图找出什么错误的情况下的Android,但到目前为止,我没有发现你的代码有任何问题。 此外,由于代码适用于JRE,它断言了这个假设。

从您提供的tcpdump中,可以得出大量的信息来断定Android如何使用与JRE相同的一组API。

让我们来看看JRE tcpdump:

在这里输入图像说明

  • 请参阅初始握手消息(Client Hello,Server Hello,更改密码规范)。 这显示了JRE客户端和代理服务器之间的握手。 这是成功的。
  • 现在我们没有看到JRE客户端和www.google.com(terminal服务器)之间的第二次握手,因为我们正在通过SSL进行SSLencryption。 代理服务器将它们一点一点地复制到最终服务器。 所以这是一个正确的行为。

现在我们来看一下android tcpdump:

在这里输入图像说明

  • 请参阅初始握手消息(Client Hello,Server Hello,更改密码规范)。 这显示了Android客户端和代理服务器之间的握手。 这是成功的。
  • 现在理想情况下,我们不应该看到第二次握手,因为它应该被encryption。 但是在这里,我们可以看到android客户端正在发送一个“客户端问候语”,即使该数据包被发送到代理服务器,它也会将其发送到“www.google.com”。 在这里输入图像说明
  • 由于数据包应该是通过SSL套接字而不是通过最初的简单套接字写入的,因此上面的绑定失败 。 我检查了你的代码,我发现你正在通过SSLSocket进行第二次握手,而不是通过普通套接字。

来自代理/ stunnel wireshark的分析:

JRE案例:

在JRE的情况下,客户端与通讯/代理服务器进行初始SSL握手。 同样可以看到下面: 在这里输入图像说明

握手成功,连接完成

然后客户端尝试连接到远程服务器(www.google.com)并开始握手。 因此,客户端发送的客户端hello在包#34中被视为encryption的消息,当stunnel解密的时候,在通过stunnel转发给代理服务器的“Client hello” 在这里输入图像说明

现在我们来看看Android客户端的情况。 在这里输入图像说明

如上所示,从客户端到stunnel / proxy的初始SSL握手成功。

然后,当Android客户端启动与远程(www.google.com)握手,理想情况下,它应该使用相同的SSL套接字。 如果是这样的话,我们应该看到从android到stunnel的encryptionstream量(类似于来自JRE的数据包#34),stunnel应该解密并发送“client hello”到proxy。 然而,正如你可以看到下面,Android客户端通过普通套接字发送“客户端你好”。

在这里输入图像说明

如果将数据包#24与JRE的数据包#34进行比较,我们可以发现这种差异。

结论:

这是一个与Android SSL( factory.createsocket()与SSL套接字)实现的错误,我觉得可能没有一个神奇的解决方法,使用相同的一套API。 事实上,我发现这个问题在Android的bug列表。 请参阅下面的链接: https : //code.google.com/p/android/issues/detail?id=204159

这个问题仍然没有解决,你可以跟进与Android开发团队修复相同。

可能的解决scheme:

如果我们得出这样的结论:同一组API不能工作,那么只剩下一个选项:

  1. 通过SSL套接字编写自己的SSL封装。 您可以手动进行握手或使用第三方实施。 这可能需要一段时间,但看起来像唯一的方法。

由于HTTPS确保没有中间人中断两者之间的通信。 这就是为什么你不能这样做。 所以第二次握手失败了。

这里的链接可能有帮助。

代理服务器上的HTTPS连接

我不知道这是否有帮助,但:
我已经build立了回购testing环境,而且我的错误稍有不同:

 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); } }