源自 OpenSSLSocketImpl(而非 SSLSocket)的 Android 自定义套接字

标签 android sockets

抱歉,这是一个很长的问题,但它有点复杂。感谢阅读。

我有一个我开发的自定义套接字工厂和套接字类 (Android 5.0),用于执行我需要在该级别执行的一些特定任务。这是我的套接字工厂和套接字(为简洁起见,我省略了许多方法):

public class CustomSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;

public CustomSocketFactory(SSLSocketFactory delegate) {
    this.delegate = delegate;
}

private Socket createCustomSocket(Socket socket) {
    if (socket instanceof SSLSocket) {
        socket = new CustomSocket((SSLSocket) socket);
    }
    return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return createCustomSocket(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException {
    return createCustomSocket(delegate.createSocket(host, port));
}


private class CustomSocket extends SSLSocket {

    protected final SSLSocket delegate;

    private CustomSocket(SSLSocket delegate) {
        this.delegate = delegate;
    }

我正在这样使用这个工厂:

  private void doCustomSocketFactoryWithHttpUrlConnection() {

    try {
        String uri = "https://alice.sni.velox.ch";

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, null, null);
        CustomSocketFactory customSocketFactory = new CustomSocketFactory(context.getSocketFactory());
        HttpsURLConnection.setDefaultSSLSocketFactory(customSocketFactory);

        URL url = new URL(uri);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();

        Log.d(TAG, "HTTP Response Code: " + conn.getResponseCode());

    } catch (Exception e) {
        Log.d(TAG, e.getLocalizedMessage());
    }

}

除了当我访问一个使用 Server Name Indication 的站点时,它按预期工作。喜欢alice.sni.velox.ch .在这种情况下,该网站会提示(我向 Wireshark 确认)我的应用程序未发送 SNI TLS header 。

取出我的自定义套接字工厂并发送 header 。

进一步挖掘,我在 okhttp 中找到了这段代码Platform.java 类(在 HttpsURLConnection 内部使用了 okhttp 类)。

@Override public void enableTlsExtensions(SSLSocket socket, String uriHost) {
     super.enableTlsExtensions(socket, uriHost);
     if (!openSslSocketClass.isInstance(socket)) return;
     try {
       setUseSessionTickets.invoke(socket, true);
       setHostname.invoke(socket, uriHost);
     } catch (InvocationTargetException e) {
// snip
}

openSSLSocketClass 是这样设置的:

Class<?> openSslSocketClass =    Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl");

因此此代码启用 SNI 和 session 票证,但前提是套接字扩展了 OpenSSLSocketImpl。

回到我的自定义套接字,在调试器中我看到传递给构造函数的套接字类是:com.android.org.conscrypt.OpenSSLSocketImplWrapper(它扩展了 OpenSSLSocketImpl)。

所以我错过了 SNI 和 session 票证功能,因为我的套接字扩展了 java.net.ssl.SSLSocket(不是 OpenSSLSocketImpl)。

想到的最佳解决方案是让我的 CustomSocket 扩展 OpenSSLSocketImpl 并添加所需的委托(delegate)方法,但我看不到如何导入 OpenSSLSocketImpl。它似乎不在标准 Android 库中。 Android 文档只讨论了 SSLSocket,而没有提及 OpenSSLSocketImpl。

有没有办法让我的 CustomSocket 类从我缺少的 OpenSSLSocketImpl 扩展?

我意识到我可以使用反射在我的 CustomSocket 类中的“委托(delegate)”上调用这些方法,但我担心它在所有情况下的可靠性以及进行这些调用的确切位置/时间。此外,如果他们继续使用类似的方法在新的 Android 版本中向 OpenSSLSocketImpl 类添加新功能,我也会错过这些功能。

感谢您一路阅读!

最佳答案

这取决于您需要在 CustomSocket 中实现什么功能,但您可以执行以下操作:

  • 使 CustomSocket 扩展套接字(无 SSL)
  • 在您的套接字工厂中,创建一个普通的自定义套接字,然后使用 delegate.createSocket(s, host, port, autoClose)
  • 在其周围包裹一个 SSL 套接字
  • 返回delegate的结果,类型为com.android.org.conscrypt.OpenSSLSocketImplWrapper

关于源自 OpenSSLSocketImpl(而非 SSLSocket)的 Android 自定义套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30178294/

相关文章:

android - 如果设置了 APN,android GPS_PROVIDER 是否使用 A-GPS?

android - 电池圈像 Battery Widget Reborn

java - 如何查找连接到套接字的 Wi-Fi 设备的 MAC 地址

flash - Flash 客户端未收到来自 Node.js 的套接字数据

c++ - 将结构转换为要传递给 UDP 的通用消息格式

java - 我的java立即初始化缺少什么?

java - CAB 上的 Android 持久 gridview 选择器

Android 错误 : java. lang.IllegalStateException:试图重新查询已经关闭的游标

c++ - 为什么我在使用 winsock 时收不到 UDP 数据包?

安卓 : Thread does not start