无Google Play服务的Android 4.x设备TLS 1.2 API连接问题
解决Android 4.x设备移除TLS 1.1后无法连接API的问题
一、无需Google Play服务启用TLS 1.2的替代方案
1. 自定义SSL Socket Factory强制启用TLS 1.2
Android 4.x的底层OpenSSL本身支持TLS 1.2,但系统默认未启用。可通过自定义SSLSocketFactory,在创建Socket后手动开启TLS 1.2协议:
public class Tls12SocketFactory extends SSLSocketFactory { private static final String[] TLS_V12_ONLY = {"TLSv1.2"}; private final SSLSocketFactory delegate; public Tls12SocketFactory(SSLSocketFactory delegate) { this.delegate = delegate; } @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 enableTls12(delegate.createSocket(s, host, port, autoClose)); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return enableTls12(delegate.createSocket(host, port)); } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { return enableTls12(delegate.createSocket(host, port, localHost, localPort)); } @Override public Socket createSocket(InetAddress host, int port) throws IOException { return enableTls12(delegate.createSocket(host, port)); } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { return enableTls12(delegate.createSocket(address, port, localAddress, localPort)); } private Socket enableTls12(Socket socket) { if (socket instanceof SSLSocket) { ((SSLSocket) socket).setEnabledProtocols(TLS_V12_ONLY); } return socket; } }
在OkHttpClient配置中使用该SocketFactory:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null, getTrustManager(), new SecureRandom()); SSLSocketFactory tls12SocketFactory = new Tls12SocketFactory(sslContext.getSocketFactory()); okHttpClientBuilder.sslSocketFactory(tls12SocketFactory, getTrustManager());
2. 调整兼容Android 4.x的Cipher Suites
Android 4.x(API 19及以下)不支持GCM类加密套件,需替换为服务器支持且系统兼容的CBC套件(可通过SSL Labs检测服务器支持的套件):
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .tlsVersions(TlsVersion.TLS_1_2) // 服务器已移除TLS 1.1,仅保留TLS 1.2 .cipherSuites( CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA ) .build(); okHttpClientBuilder.connectionSpecs(Arrays.asList(spec));
3. 集成Spongy Castle作为安全提供者
Spongy Castle是Bouncy Castle的Android适配版本,可替换系统默认安全提供者,增强对现代TLS协议的支持:
- 添加依赖:
implementation 'com.madgag.spongycastle:core:1.58.0.0' implementation 'com.madgag.spongycastle:prov:1.58.0.0'
- 在Application初始化时替换提供者:
static { Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); }
二、优雅处理错误并提示用户
在Retrofit的onFailure回调中,捕获SSL相关异常并展示友好提示:
updateAppRemoteCall.enqueue(new Callback<List<UpdateAppRemote>>() { @Override public void onResponse(Call<List<UpdateAppRemote>> call, retrofit2.Response<List<UpdateAppRemote>> response) { // 处理正常响应 } @Override public void onFailure(Call<List<UpdateAppRemote>> call, Throwable t) { if (t instanceof SSLHandshakeException || t instanceof SSLProtocolException) { runOnUiThread(() -> { Toast.makeText(MainActivity.this, "当前设备系统版本过低,无法安全连接服务器,请联系管理员升级设备或更换支持的设备", Toast.LENGTH_LONG).show(); }); } else { t.printStackTrace(); } } });
内容的提问来源于stack exchange,提问作者Rodrigo Salomao
相关产品推荐
相关产品推荐

