Java 通过另一个SSLSocket的SSLSocket

Java 通过另一个SSLSocket的SSLSocket,java,android,ssl,cryptography,sslsocketfactory,Java,Android,Ssl,Cryptography,Sslsocketfactory,我正在尝试在Android应用程序中的另一个SSLSocket上创建一个SSLSocket。较低的连接是到的SSL安全连接(HTTP proxy over SSL),较高的连接是HTTP over SSL(HTTPS) 为此,我使用了SSLSocketFactory的createSocket()函数,该函数允许传递一个现有套接字,通过该套接字运行SSL连接,如下所示: private Socket doSSLHandshake(Socket socket, String host, int po

我正在尝试在Android应用程序中的另一个
SSLSocket
上创建一个
SSLSocket
。较低的连接是到的SSL安全连接(HTTP proxy over SSL),较高的连接是HTTP over SSL(HTTPS)

为此,我使用了SSLSocketFactory的
createSocket()
函数,该函数允许传递一个现有套接字,通过该套接字运行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
我正在安卓7.1.1上测试。该应用程序的目标是SDK级别23

  • 我可能做错了什么
  • 如何进一步调试该问题
  • 有人有一个SSLSocket超过另一个SSLSocket的工作示例吗 最新Android版本上的SSLSocket
非常感谢您的帮助


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


更新2:从概念上讲,以下是连接所经历的步骤:

  • 从Android应用程序连接到安全代理服务器(套接字)
  • 与代理服务器进行SSL/TLS握手(通过套接字的SSLSocket)
  • 通过SSLSocket,将CONNECT消息发送到代理服务器
  • 代理连接到目标(https)服务器,从现在起只复制字节
  • 与目标(https)服务器进行SSL/TLS握手(SSLSocket over SSLSocket over Socket)
  • 将GET消息发送到目标服务器并读取响应
  • 在SSLSocket上进行握手时,问题出现在步骤5中 通过SSLSocket(通过套接字)的


    更新3:我现在打开了一个GitHub回购协议,其中包含一个示例项目和tcpdumps:



    注意:我已经找到并阅读了,但不幸的是,它没有包含太多有用的帮助。

    我不认为你做错了什么。在第二次握手时,协议协商中似乎存在错误。一个好的候选者可能会在NPN TLS握手扩展中失败

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


    您可以浏览列出的协议并单独尝试。看看是否可以锁定失败的部分,以及是否需要支持特定的协议或扩展

    所以我试图找出安卓系统出现了什么问题,但到目前为止,我没有发现你的代码有任何问题。此外,由于该代码适用于JRE,因此它支持这一假设

    从您提供的tcpdump中,有大量信息可以总结Android如何使用与JRE相同的API集

    让我们看看JRE tcpdump:

    • 请参阅初始握手消息(客户端Hello、服务器Hello、更改密码规范)。这显示了JRE客户端和代理服务器之间的握手。这是成功的
    • 现在我们看不到JRE客户端和www.google.com(终端服务器)之间的第二次握手,因为这是加密的,因为我们正在通过SSL进行SSL。代理服务器正在将它们逐位复制到终端服务器。所以这是正确的行为
    现在让我们看看android tcpdump:

    • 请参阅初始握手消息(客户端Hello、服务器Hello、更改密码规范)。这显示了android客户端和代理服务器之间的握手。这是成功的
    • 现在理想情况下,我们不应该看到第二次握手,因为它应该被加密。但在这里,我们可以看到android客户端正在发送一个“client hello”,并将其发送到“www.google.com”,即使数据包被发送到代理服务器。
    • 上面的注定会失败,因为数据包应该通过SSL套接字而不是最初的普通套接字写入。我查看了您的代码,发现您正在通过SSLSocket而不是普通套接字进行第二次握手
    来自proxy/stunnel wireshark的分析:

    JRE案例:

    对于JRE,客户端与stunnel/代理服务器进行初始SSL握手。如下所示:

    握手成功,连接完成

    然后,客户端尝试连接到远程服务器(www.google.com)并开始握手。因此,客户机发送的客户机hello在数据包34中被视为加密消息,当stunnel对其进行解密时,它会出现在“客户机hello”中,由stunnel转发到代理服务器

    现在让我们看看android客户端案例。

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

    然后,当android客户端开始与remote(www.google.com)握手时,理想情况下,它应该使用SSL套接字进行握手。如果是这种情况,我们应该看到从android到stunnel的加密流量(类似于JRE案例中的数据包#34),stunnel应该解密并向代理发送“客户端hello”。然而,正如您在下面看到的,android客户端正在通过普通套接字发送一个“client hello”

    如果您比较来自JRE的数据包24和数据包34,我们可以发现这一差异

    结论:

    这是android SSL(
    factory.createsocket()
    with SSL socket)实现的一个bug,我觉得使用同一组API可能没有一个神奇的解决方法。事实上,我在android bug列表中发现了这个问题。请参阅以下链接:

    这个问题仍然没有解决,您可能可以与android开发团队跟进以解决同样的问题

    可能的解决方案:

    如果我们认为同一套API无法工作,那么您只有一个选择:

  • 在服务器上编写自己的SSL包装器
    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
    
    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);
        }
    }