来自OpenSSLSocketImpl的Android自定义套接字(不是SSLSocket)

时间:2015-05-11 21:56:34

标签: 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之类的okhttp网站,否则此工作正常。在这种情况下,该网站抱怨(我与Wireshark确认)我的应用程序没有发送SNI TLS标头。

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

进一步深入,我在{{3}} Platform.java类中找到了这段代码(okhttp类在HttpsURLConnection中使用)。

@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和会话票证,但仅当套接字扩展了OpenSSLSocketImpl时。

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

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

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

有没有办法可以让我的CustomSocket类从OpenSSLSocketImpl中扩展出来,而我却错过了?

我意识到我可以使用反射来调用&#34;委托&#34;在我的CustomSocket类中,但我担心在所有情况下的可靠性以及在何时/何时进行这些调用。此外,如果他们继续使用类似的方法在新的Android版本中为OpenSSLSocketImpl类添加新功能,我也会错过这些功能。

感谢您一直阅读!

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:-1)

好吧,也许我对你没那么有用,但是你可以尝试设置一个不同的客户端,比如jersey的客户端或apache http客户端,特别是因为Android自己的http客户端太旧而应该被替换。