OkHttp在Android 4.4及以下不支持TLS协议的解决方法
问题描述
Glide加载https图片:https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates#0
OkHttp在Android 4.4及以下请求https报错:
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x610df808: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x410f976a:0x00000000)
问题原因
由于集成的 SSL 库版本不同,不同 Android 版本的默认 SSL/TLS 版本配置如下表:
Protocol | Supported (API Levels) | Enabled by default (API Levels) |
---|---|---|
SSLv3 | 1–25 | 1–22 |
TLSv1 | 1+ | 1+ |
TLSv1.1 | 16+ | 20+ |
TLSv1.2 | 16+ | 20+ |
在 Android 5.0 之前,最高支持的 SSL/TLS 版本为 TLSv1,而这个版本实际上是一个安全性并不是太好的版本,当前已经有许多网站配置为不再支持这种老版本的 SSL/TLS。
在 Android 4.X 的设备上,使用相同版本的 OkHttp 访问最低支持 TLSv1.1 及更低 SSL/TLS 版本的 HTTPS 服务器时,HTTPS 连接会在握手阶段失败。
问题解决
为请求的网络客户端设置一个特殊的SSLSocketFactory
public class Tls12SocketFactory extends SSLSocketFactory {
private static final String[] TLS_SUPPORT_VERSION = {"TLSv1.1", "TLSv1.2"};
final SSLSocketFactory delegate;
public Tls12SocketFactory(SSLSocketFactory base) {
this.delegate = base;
}
@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 patch(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if (s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_SUPPORT_VERSION);
}
return s;
}
}
设置OkHttpClient
public class SSLHelper {
private static X509TrustManager getDefaultTrustManager() {
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
private static TrustManager[] getTrustManagers(InputStream... certificates) {
if (certificates == null || certificates.length <= 0) return null;
try {
//构造CertificateFactory对象
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
//得到Certificate并放入到keyStore中
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
}
}
//利用keyStore初始化TrustManagerFactory
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client, InputStream... certificates) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Tls12SocketFactory socketFactory = null;
TrustManager[] trustManagers = null;
try {
trustManagers = getTrustManagers(certificates);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, null);
socketFactory = new Tls12SocketFactory(sslContext.getSocketFactory());
client.sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]);
} catch (Exception e) {
Log.e("yezhou", "Error while setting TLS", e);
if (trustManagers != null) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, trustManagers, null);
socketFactory = new Tls12SocketFactory(sc.getSocketFactory());
client.sslSocketFactory(socketFactory, (X509TrustManager) trustManagers[0]);
} catch (Exception e12) {
Log.e("yezhou", "Error while setting TLS 1.2", e12);
}
}
}
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);
client.connectionSpecs(specs);
}
return client;
}
}
初始化OkHttpClient
private OkHttpClient initOkHttpClient() {
OkHttpClient builder = OkHttpClient.Builder()
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cache(null)
.connectTimeout(10L, TimeUnit.SECONDS) //设置读取超时时间
.writeTimeout(10L, TimeUnit.SECONDS) //设置写的超时时间
.readTimeout(10L, TimeUnit.SECONDS); //设置连接超时时间
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
InputStream is = null;
try {
is = context.getAssets().open("appblog.cer");
} catch (IOException e) {
e.printStackTrace();
}
SSLHelper.enableTls12OnPreLollipop(clientBuilder, is);
}
return builder.build()
}
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/18/solution-for-okhttp-not-supporting-tls-protocol-on-android-4-4-and-below/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论