okhttpsslsocketfactory

Can I bind a local IP address to my SSLSocketFactory with OkHttp?


I'm working to get my OkHttpClient on Android to make its HTTPS requests using a custom certificate whilst bound to a specific network interface's local address. My current attempt at this uses the following OkHttpClient:

val client = OkHttpClient.Builder()
        .readTimeout(2, TimeUnit.SECONDS)
        .connectTimeout(2, TimeUnit.SECONDS)
        .sslSocketFactory(
            MySocketFactory(
                getSslContext().socketFactory,
                localAddress
            ),
            MyTrustManager()
        )
        .build()

The MySocketFactory class is implemented as:

class MySocketFactory(
    private val delegate: SSLSocketFactory, 
    private val localAddress: InetAddress
) : SSLSocketFactory() {
    override fun createSocket(): Socket {
        Timber.i("createSocket has been called!")
        val socket = delegate.createSocket()
        socket.bind(InetSocketAddress(localAddress, 0))
        return socket
    }

[other overrides of the various abstract createSocket methods, each of which throws an UnsupportedOperationException]

    override fun getDefaultCipherSuites() = delegate.defaultCipherSuites

    override fun getSupportedCipherSuites() = delegate.supportedCipherSuites
}

This is mostly based on the documentation from the OkHttp GitHub site which describes how passing a value to Builder.socketFactory() allows us to bind each socket to a specific address by overriding the parameter-less createSocket method. However, the docs for sslSocketFactory do not mention anything about being able to bind sockets. When I run my app with the above code, the createSocket log entry is never generated, indicating that the factory is completely ignored. As a result, the HTTP endpoint is never reached.

If I try this same setup without the wrapper MySocketFactory class - instead just passing getSslContext().socketFactory directly into Builder.sslSocketFactory - then I can contact the endpoint perfectly well, assuming I only have the single local address on my machine at the time.

So my question: is this possible to do with a custom SSLSocketFactory?


Solution

  • Yes! But rather than customizing the SSLSocketFactory, customize the regular SocketFactory. When OkHttp creates any socket it always uses the regular SocketFactory first, and then potentially wraps that with SSL. This is necessary for TLS tunnels but used everywhere.