Android OkHttp与HttpsURLConnection如何修改支持的TLS版本

科技   2024-11-01 11:37   中国  

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. 1. 检查当前系统支持的 TLS 版本。

  2. 2. 如果不支持 TLS 1.2,手动启用。

  3. 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[0as 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[0as 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[0as 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. 1. SSL:支持 SSL 2.0 和 SSL 3.0 协议。由于存在已知的安全漏洞(例如 SSL 3.0 中的 POODLE 攻击),这两种协议已被认为是不安全的,现代应用不再推荐使用。

  2. 2. TLS:这是一个通用的协议版本设置,通常指代 TLS 1.0 及以上版本。默认情况下,它会尝试选择当前系统支持的最高版本。例如,如果系统支持 TLS 1.3,那么它会选择使用 TLS 1.3;若不支持,则会降级到 TLS 1.2 或更低版本。

  3. 3. TLSv1TLSv1.1TLSv1.2TLSv1.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(nullnullnull)
    }
}

在上例中,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/


虎哥Lovedroid
Android技术达人 近10年一线开发经验 关注并分享Android、Kotlin新技术,新框架 多年Android底层框架修改经验,对Framework、Server、Binder等架构有深入理解
 最新文章