Android OkHttp与HttpsURLConnection如何修改支持的TLS版本
在 Android 开发中,确保应用程序的网络通信安全是非常重要的一环,而选择合适的 TLS(传输层安全)版本,则是提高网络安全的关键之一。由于 Android 各个版本支持的 TLS 版本不同,开发者需要在实现网络请求时设置兼容性。本文将围绕 OkHttp
和 HttpsURLConnection
这两个常用的 HTTP 客户端,探讨如何自定义和设置支持的 TLS 版本。
为什么需要指定 TLS 版本?
TLS 是一种用于加密互联网通信的协议,确保数据传输的机密性和完整性。由于网络攻击技术的不断更新,TLS 早期版本(如 TLS 1.0 和 TLS 1.1)已经逐渐被淘汰,推荐使用的最低版本是 TLS 1.2,而 TLS 1.3 是最新的版本。然而,Android 系统的不同版本对 TLS 的支持情况有所不同:
• Android 5.0 以下:仅支持 TLS 1.0 和 1.1
• Android 5.0 - 7.0:支持 TLS 1.2,但需要手动启用
• Android 7.0 及以上:默认支持并启用 TLS 1.2 和 1.3
因此,如果应用需要兼容较低版本的 Android 系统或对接一些要求高版本 TLS 的服务,开发者就需要手动配置支持的 TLS 版本。
使用 OkHttp 修改支持的 TLS 版本
OkHttp[1] 是 Square 提供的一个高效 HTTP 客户端库,支持较多配置选项和较强的兼容性,以下是使用 OkHttp
自定义 TLS 版本的方法。
1. 配置 OkHttp 支持 TLS 1.2 及以上版本
在 Android 5.0 以下的系统中,OkHttp 默认不启用 TLS 1.2,所以我们需要手动指定。可以通过创建一个自定义的 SSLSocketFactory
来启用 TLS 1.2。
以下是具体步骤:
1. 检查当前系统支持的 TLS 版本。
2. 如果不支持 TLS 1.2,手动启用。
3. 使用自定义的
SSLSocketFactory
配置OkHttp
客户端。
import okhttp3.OkHttpClient
import java.security.KeyStore
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
fun createOkHttpClientWithTLS12(): OkHttpClient {
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
val trustManager = trustManagers[0] as X509TrustManager
val sslContext = SSLContext.getInstance("TLSv1.2")
sslContext.init(null, arrayOf(trustManager), null)
val sslSocketFactory = sslContext.socketFactory
//val specs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
// .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_3).build()
val client = OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
//.connectionSpecs(listOf(specs,ConnectionSpec.CLEARTEXT))
.hostnameVerifier { _, _ -> true }
.build()
return client
}
在以上代码中:
• 使用
SSLContext
设置 TLS 版本为TLSv1.2
。• 创建并配置
SSLSocketFactory
,将其传入OkHttpClient.Builder()
中,使OkHttp
支持 TLS 1.2。• 设置
hostnameVerifier
为true
仅作示例,在生产环境中应根据安全需求设置。
阅读源码,我了解到Okttp初始化过程中也会创建默认的sslSocketFactory
,所以也可以不设置sslSocketFactory, 通过ConnectionSpec
设置支持的TLS版本列表。
使用 HttpsURLConnection 修改支持的 TLS 版本
HttpsURLConnection
是 Android 平台的内置网络请求类,尽管较老旧,但在某些项目中仍然会使用。以下是修改 HttpsURLConnection
的 TLS 版本支持的方法。
1. 为低版本 Android 手动启用 TLS 1.2
对于 Android 5.0 以下系统,需手动设置 TLS 1.2 的支持。以下是自定义 SSLSocket
启用 TLS 1.2 的示例:
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
import java.security.KeyStore
import javax.net.ssl.X509TrustManager
fun enableTLS12OnHttpsURLConnection() {
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
val trustManager = trustManagers[0] as X509TrustManager
val sslContext = SSLContext.getInstance("TLSv1.2")
sslContext.init(null, arrayOf(trustManager), null)
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory)
}
在此代码中,我们创建一个 SSLContext 设置为 TLS 1.2,并将其应用为 HttpsURLConnection
的默认 SSLSocketFactory
。这样,在 Android 5.0 以下的设备上,可以确保所有通过 HttpsURLConnection
的请求使用 TLS 1.2。
2. 设置支持的协议列表
在 Android 中,直接通过 HttpsURLConnection
设置特定的协议或 TLS 版本比较有限。开发者可以使用自定义的 SSLSocketFactory
强制指定支持的 TLS 版本:
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
import java.security.KeyStore
fun createSSLSocketFactoryWithTLSVersions(): SSLSocketFactory {
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
val trustManager = trustManagers[0] as X509TrustManager
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(trustManager), null)
return sslContext.socketFactory
}
fun configureHttpsURLConnection() {
val sslSocketFactory = createSSLSocketFactoryWithTLSVersions()
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory)
}
此代码会启用最新版本的 TLS(如设备支持的最高版本 TLS 1.2 或 1.3),确保设备自动选择最优的 TLS 版本。
测试代码
最后,提供一个测试请求的代码片段,便于验证是否启用了指定的 TLS 版本:
fun testRequest() {
val client = createOkHttpClientWithTLS12()
val request = Request.Builder()
.url("https://example.com")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
println("Request failed: ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
println("Response received: ${response.body?.string()}")
}
})
}
SSLContext#protocol参数解读
在 SSLContext
中,protocol
参数用于指定 SSL/TLS 的协议版本。SSLContext
是 Java 中提供的一种用于实现 SSL(安全套接字层)和 TLS(传输层安全)协议的环境,配置它可以实现对不同版本 SSL 和 TLS 的支持。
常用的 protocol
参数
1.
SSL
:支持 SSL 2.0 和 SSL 3.0 协议。由于存在已知的安全漏洞(例如 SSL 3.0 中的 POODLE 攻击),这两种协议已被认为是不安全的,现代应用不再推荐使用。2.
TLS
:这是一个通用的协议版本设置,通常指代 TLS 1.0 及以上版本。默认情况下,它会尝试选择当前系统支持的最高版本。例如,如果系统支持 TLS 1.3,那么它会选择使用 TLS 1.3;若不支持,则会降级到 TLS 1.2 或更低版本。3.
TLSv1
,TLSv1.1
,TLSv1.2
,TLSv1.3
:这些分别表示具体的 TLS 协议版本:
•
TLSv1
:即 TLS 1.0 版本。已经过时,存在已知的安全问题。•
TLSv1.1
:即 TLS 1.1 版本。相对 TLS 1.0 更安全,但在现代安全标准中也逐步被淘汰。•
TLSv1.2
:即 TLS 1.2 版本。至今仍然是广泛支持的协议版本,并且具备较高的安全性。•
TLSv1.3
:即 TLS 1.3 版本。最新的协议版本,提供更强的安全性和更高的性能,但支持情况在早期 Android 版本中不完全。
如何选择 protocol
选择 protocol
参数时应尽量遵循以下原则:
• 首选高版本协议:在较新的 Android 或 Java 版本中,建议使用
TLSv1.3
或TLSv1.2
,并避免使用较低的版本。• 兼容性考虑:在需要兼容旧版本 Android 系统时,可以选择
TLS
,系统会自动选择最高可用的协议版本。
示例:设置 SSLContext
的 protocol
import javax.net.ssl.SSLContext
fun createSSLContext(): SSLContext {
// 创建一个 SSLContext 实例,设置协议为 TLSv1.2
return SSLContext.getInstance("TLSv1.2").apply {
init(null, null, null)
}
}
在上例中,SSLContext.getInstance("TLSv1.2")
将实例化一个支持 TLS 1.2 的 SSLContext
。如果需要支持更高的 TLS 版本(例如 TLS 1.3),可以将 "TLSv1.2"
替换为 "TLSv1.3"
,不过需确保 Android 设备的系统和 JVM 版本支持 TLS 1.3。
总结
在 Android 开发中,选择合适的 TLS 版本是确保应用网络通信安全的关键一步。通过 OkHttp
和 HttpsURLConnection
支持的特性,我们可以手动设置和启用需要的 TLS 版本,以提高应用的兼容性和安全性。对于老版本 Android 系统,手动启用 TLS 1.2 尤为重要,而在支持 TLS 1.3 的系统上,可以直接利用 OkHttp 的自动支持。希望本篇指南可以帮助您更好地理解和设置 Android 网络安全。
引用链接
[1]
OkHttp: https://square.github.io/okhttp/