You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何强制Android 4.x(API<20)应用使用TLSv1.2修复HTTPS通信问题?

解决Android 4.x(API<20)下Retrofit+OkHttp适配TLS 1.2的问题

我之前也碰到过完全一样的情况——后端升级到SHA-2证书和TLS 1.2后,Android 4.1到4.4的设备直接没法走HTTPS了,折腾了好一阵才搞定核心问题:API 16-19的Android系统默认没有启用TLS 1.2支持,哪怕硬件能扛,系统层也没开开关。下面给你一套可直接用的解决方案,包括自定义SocketFactory配置、OkHttp适配,还有可选的公钥固定建议:

1. 实现兼容TLS 1.2的SocketFactory

先写一个TLSSocketFactory,核心是强制SSLSocket启用TLS 1.2,同时兼容旧版本的TLS协议作为 fallback:

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class TLSSocketFactory extends SSLSocketFactory {

    private final SSLSocketFactory delegate;
    private final X509TrustManager trustManager;

    public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
        // 优先使用系统默认的信任管理器(比信任所有证书安全)
        this.trustManager = getDefaultTrustManager();
        
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{trustManager}, null);
        this.delegate = sslContext.getSocketFactory();
    }

    // 获取系统默认的信任管理器
    private X509TrustManager getDefaultTrustManager() throws NoSuchAlgorithmException, KeyStoreException {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init((KeyStore) null);
        return (X509TrustManager) trustManagerFactory.getTrustManagers()[0];
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return enableTlsProtocols(delegate.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return enableTlsProtocols(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return enableTlsProtocols(delegate.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return enableTlsProtocols(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return enableTlsProtocols(delegate.createSocket(address, port, localAddress, localPort));
    }

    // 强制启用TLS 1.2,同时保留1.1和1.0作为兼容 fallback
    private Socket enableTlsProtocols(Socket socket) {
        if (socket instanceof SSLSocket) {
            ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"});
        }
        return socket;
    }

    public X509TrustManager getTrustManager() {
        return trustManager;
    }
}

2. 配置OkHttpClient使用自定义SocketFactory

接下来把这个SocketFactory集成到OkHttp里,还要给API<20的设备指定MODERN_TLS连接规范,确保优先用TLS 1.2:

import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

public class OkHttpClientProvider {

    public static OkHttpClient getTls12EnabledClient() {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS);

        try {
            TLSSocketFactory tlsSocketFactory = new TLSSocketFactory();
            // OkHttp 3.x 需要同时传入SocketFactory和TrustManager
            clientBuilder.sslSocketFactory(tlsSocketFactory, tlsSocketFactory.getTrustManager());
            // 只允许现代TLS协议,同时兼容旧设备
            clientBuilder.connectionSpecs(Collections.singletonList(ConnectionSpec.MODERN_TLS));
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
            e.printStackTrace();
            // 初始化失败时,降级到默认配置
        }

        return clientBuilder.build();
    }
}

3. 关联Retrofit和配置好的OkHttpClient

最后把这个定制的OkHttpClient传给Retrofit就行,和你平时的用法差不多:

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitProvider {

    private static final String BASE_URL = "https://your-backend-domain.com/";

    public static YourApiService createApiService() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(OkHttpClientProvider.getTls12EnabledClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        return retrofit.create(YourApiService.class);
    }
}

可选:添加公钥固定(增强生产环境安全性)

虽然你暂时没加公钥固定,但强烈建议生产环境加上,防止中间人攻击。OkHttp自带证书固定功能,只需要把后端证书的公钥哈希配置进去:

// 在OkHttpClient.Builder里添加这段
clientBuilder.certificatePinner(new CertificatePinner.Builder()
        .add("your-backend-domain.com", "sha256/这里替换成你的证书公钥哈希")
        .build());

你可以用下面的openssl命令提取公钥哈希:

openssl s_client -connect your-backend-domain.com:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

测试注意事项

  • 一定要在真实的Android 4.x设备上测试,模拟器可能和真实设备的TLS支持有差异
  • 如果还是失败,抓包看一下SSL握手的具体错误(比如证书不被信任、协议协商失败),针对性调整

内容的提问来源于stack exchange,提问作者Leem.fin

火山引擎 最新活动