我可以使用 OkHttp 将本地 IP 地址绑定到我的 SSLSocketFactory 吗?
Can I bind a local IP address to my SSLSocketFactory with OkHttp?
我正在努力让我的 OkHttpClient 在 Android 上使用自定义证书发出 HTTPS 请求,同时绑定到特定网络接口的本地地址。我目前的尝试使用以下 OkHttpClient
:
val client = OkHttpClient.Builder()
.readTimeout(2, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS)
.sslSocketFactory(
MySocketFactory(
getSslContext().socketFactory,
localAddress
),
MyTrustManager()
)
.build()
MySocketFactory
class实现为:
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
}
这主要是基于 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 没有提及任何关于能够绑定套接字的内容。当我使用上面的代码 运行 我的应用程序时, createSocket
日志条目从未生成,表明工厂被完全忽略。因此,永远无法到达 HTTP 端点。
如果我在没有包装器 MySocketFactory
class 的情况下尝试相同的设置 - 而不是直接将 getSslContext().socketFactory
传递到 Builder.sslSocketFactory
- 那么我可以很好地联系端点,假设当时我的机器上只有一个本地地址。
所以我的问题是:这可能与自定义 SSLSocketFactory
相关吗?
是的!但不是自定义 SSLSocketFactory,而是自定义常规 SocketFactory。当 OkHttp 创建任何套接字时,它总是首先使用常规的 SocketFactory,然后可能用 SSL 包装它。这对于 TLS 隧道是必需的,但无处不在。
使用builder.socketFactory(getSocketFactory())
。此示例代码应 select 基于 IP 或名称。
class InterfaceSocketFactory(private val localAddress: InetAddress) : SocketFactory() {
private val systemFactory = getDefault()
override fun createSocket(): Socket {
val s = systemFactory.createSocket()
s.bind(InetSocketAddress(localAddress, 0))
return s
}
override fun createSocket(host: String, port: Int): Socket {
return systemFactory.createSocket(host, port, localAddress, 0)
}
override fun createSocket(address: InetAddress, port: Int): Socket {
return systemFactory.createSocket(address, port, localAddress, 0)
}
override fun createSocket(
host: String,
port: Int,
localAddr: InetAddress,
localPort: Int
): Socket {
return systemFactory.createSocket(host, port, localAddr, localPort)
}
override fun createSocket(
address: InetAddress,
port: Int,
localAddr: InetAddress,
localPort: Int
): Socket {
return systemFactory.createSocket(address, port, localAddr, localPort)
}
companion object {
fun byName(ipOrInterface: String): SocketFactory? {
val localAddress = try {
// example 192.168.0.51
InetAddress.getByName(ipOrInterface)
} catch (uhe: UnknownHostException) {
// example en0
val networkInterface = NetworkInterface.getByName(ipOrInterface) ?: return null
networkInterface.inetAddresses.nextElement()
}
return InterfaceSocketFactory(localAddress)
}
}
}
我正在努力让我的 OkHttpClient 在 Android 上使用自定义证书发出 HTTPS 请求,同时绑定到特定网络接口的本地地址。我目前的尝试使用以下 OkHttpClient
:
val client = OkHttpClient.Builder()
.readTimeout(2, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS)
.sslSocketFactory(
MySocketFactory(
getSslContext().socketFactory,
localAddress
),
MyTrustManager()
)
.build()
MySocketFactory
class实现为:
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
}
这主要是基于 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 没有提及任何关于能够绑定套接字的内容。当我使用上面的代码 运行 我的应用程序时, createSocket
日志条目从未生成,表明工厂被完全忽略。因此,永远无法到达 HTTP 端点。
如果我在没有包装器 MySocketFactory
class 的情况下尝试相同的设置 - 而不是直接将 getSslContext().socketFactory
传递到 Builder.sslSocketFactory
- 那么我可以很好地联系端点,假设当时我的机器上只有一个本地地址。
所以我的问题是:这可能与自定义 SSLSocketFactory
相关吗?
是的!但不是自定义 SSLSocketFactory,而是自定义常规 SocketFactory。当 OkHttp 创建任何套接字时,它总是首先使用常规的 SocketFactory,然后可能用 SSL 包装它。这对于 TLS 隧道是必需的,但无处不在。
使用builder.socketFactory(getSocketFactory())
。此示例代码应 select 基于 IP 或名称。
class InterfaceSocketFactory(private val localAddress: InetAddress) : SocketFactory() {
private val systemFactory = getDefault()
override fun createSocket(): Socket {
val s = systemFactory.createSocket()
s.bind(InetSocketAddress(localAddress, 0))
return s
}
override fun createSocket(host: String, port: Int): Socket {
return systemFactory.createSocket(host, port, localAddress, 0)
}
override fun createSocket(address: InetAddress, port: Int): Socket {
return systemFactory.createSocket(address, port, localAddress, 0)
}
override fun createSocket(
host: String,
port: Int,
localAddr: InetAddress,
localPort: Int
): Socket {
return systemFactory.createSocket(host, port, localAddr, localPort)
}
override fun createSocket(
address: InetAddress,
port: Int,
localAddr: InetAddress,
localPort: Int
): Socket {
return systemFactory.createSocket(address, port, localAddr, localPort)
}
companion object {
fun byName(ipOrInterface: String): SocketFactory? {
val localAddress = try {
// example 192.168.0.51
InetAddress.getByName(ipOrInterface)
} catch (uhe: UnknownHostException) {
// example en0
val networkInterface = NetworkInterface.getByName(ipOrInterface) ?: return null
networkInterface.inetAddresses.nextElement()
}
return InterfaceSocketFactory(localAddress)
}
}
}