android - 当服务器禁用 TLS 1.0 和 1.1 时,即使是最新版本的 Android 应用程序也会崩溃
问题描述
我最近在服务器端禁用了对 TLS 1.0 和 1.1 的支持,所有 Android 应用程序都停止工作。我知道 Android 4 及更低版本不支持 TSL 1.2 或更高版本,但即使在 Android 10 上运行的应用程序也停止工作。手机上的堆栈跟踪显示 TLS 问题。安装在 iphone 上的应用程序看不到任何问题。当我们启用对 TLS 1.0 和 1.1 的支持时,Android 应用程序在所有 Android 手机上都可以正常工作,但随后服务器的安全审核就会下降。Android 操作系统中是否可以启用对 TLS 1.2 的支持?
解决方案
我最近遇到了同样的问题,并且在互联网紧缩政策中阅读了有关它的信息,即使在最近的 Android 版本上,您当前的多个地方也可能会出现呼叫失败的情况。但是,您可以执行两个主要步骤来确保您的应用程序已为 TLS 1.2 做好准备:
确保使用 Google Play 服务并启用安全服务 (
com.google.android.gms.security.ProviderInstaller
)。在创建任何 HttpClient 资源之前,您必须在代码中执行此操作(否则它们将使用较旧的安全规范)在onCreate
您的主要活动中具有这样的内容应该可以很好地工作:try { ProviderInstaller.installIfNeeded(this); Log.i(LOG_TAG, "Google Play Services Installed"); } catch (GooglePlayServicesRepairableException e) { GoogleApiAvailability.getInstance() .showErrorNotification(this, e.getConnectionStatusCode()); Log.i(LOG_TAG, "Error", e); } catch (GooglePlayServicesNotAvailableException e) { Log.i(LOG_TAG, "Error", e); }
在创建任何 http 客户端之前运行此程序很重要,否则它们将不支持 TLS 1.2
确保在您的 HttpClient 中启用了 TLS 1.2。某些客户端具有未启用 TLS 1.2 的默认配置。例如,对于 OkHttp,您应该创建以下 SocketFactory:
import android.os.Build;
import android.util.Log;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;
/**
* Enables TLS v1.2 when creating SSLSockets.
* <p/>
* For some reason, android supports TLS v1.2 from API 16, but enables it by
* default only from API 20.
* @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
* @see SSLSocketFactory
*/
public class Tls12SocketFactory extends SSLSocketFactory {
private static final String[] TLS_V12_ONLY = {"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_V12_ONLY);
}
return s;
}
private static final CipherSuite[] APPROVED_CIPHER_SUITES = new CipherSuite[] {
// TLSv1.3
CipherSuite.TLS_AES_128_GCM_SHA256,
CipherSuite.TLS_AES_256_GCM_SHA384,
CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_AES_128_CCM_SHA256,
CipherSuite.TLS_AES_256_CCM_8_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
// Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll
// continue to include them until better suites are commonly available. For example, none
// of the better cipher suites listed above shipped with Android 4.4 or Java 7.
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
};
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT < 22) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.cipherSuites(APPROVED_CIPHER_SUITES)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.supportsTlsExtensions(true)
.build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
client.connectionSpecs(specs);
Log.i("OkHttpTLSCompat", "TLS 1.2 enabled");
} catch (Exception exc) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
}
}
return client;
}
public static OkHttpClient returnClient() {
OkHttpClient.Builder client = new OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cache(null)
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS);
return enableTls12OnPreLollipop(client).build();
}
}
然后Tls12SocketFactory.returnClient()
在你需要的时候做
重要的是您需要同时启用两者!如果没有 Google Play 安全服务,无论您使用什么 http 库,您都不会在较旧的 Android 设备上获得 TLS 1.2 支持。确保在 HttpClient 中启用(并可能强制执行)TLS 也很重要。
另外让我重申,在创建任何 http 客户端之前加载 Google Play 更新非常重要。我最近花了一些时间来调试它,因为在我的代码中,一些库在我放入 Google Play 代码之前已经初始化,因此它们没有获得 TLS 1.2 支持
推荐阅读
- javascript - 选择标签上的 if 语句 javascript
- javascript - 获取选定的值下拉列表 React useSate
- oracle - 我想使用我的公共 IP 地址通过 DBlink 连接到我的 Oracle DB
- python - 我从 3 天开始就一直在尝试这段代码。该代码用于维基百科搜索
- mongodb - 当想要保存 d3 力图以供下次渲染时,我应该使用 neo4j 吗?
- ios - 如何在 iOS 中直接访问 GNSS 传感器
- java - 无法从 Android Studio 上的 url 流式传输音乐
- java - 什么是`LocalTestServerContext`,我该如何使用它?
- c# - 当我想在 Visual Studio 2019 中使用 guna.ui2.licensing 时。它显示了这种错误。我想做什么来解决这个问题?
- javascript - 为什么 React 似乎无法识别 Heroku 的 PORT 环境变量?