我们的后端从SHA-1和TLS1.0更新到SHA-2证书和TLS 1.2。它破坏了我们的Android应用程序在API级别低于20(Android 4.1-4.4)的平台上的HTTPS通信。
(我们的Android项目正在使用Retrofit 2.4.0和okhttp 3.10.0)
我尝试通过使用TLS 4.x在Android 1.x上强制我们的应用程序来解决上述问题,我的代码受到本教程的启发(但我排除了教程代码的公钥固定):
我首先创建了一个TLSSocketFactory
:
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { systemDefaultTrustManager() }, null);
internalSSLSocketFactory = sslContext.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
public X509TrustManager systemDefaultTrustManager() {
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.
}
}
}
然后,将上述TLSSocketFactory
应用于OkHttpClient
:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_1, TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
builder.connectionSpecs(Collections.singletonList(spec));
builder.readTimeout(10, TimeUnit.SECONDS);
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.writeTimeout(10, TimeUnit.SECONDS);
// apply TLSSocketFactory
try {
TLSSocketFactory socketFactory = new TLSSocketFactory();
builder.sslSocketFactory(socketFactory, socketFactory.systemDefaultTrustManager());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
OkHttpClient client = builder.build();
然后,使用上述OkHttpClient
创建Retrofit
实例:
Retrofit retrofit = new Retrofit.Builder().baseUrl(myUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client) // my OkHttpClient
.build();
但是当我现在在Android 4.x上与我们的后端通信上运行我的应用程序时,我仍然收到错误:
OkHttp: <-- HTTP FAILED: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
为什么?我错过了什么?
(在TLSSocketFactory
的构造函数中,我也尝试了sslContext.init(null, null, null);
但它没有帮助,同样的错误。
(新证书很好,它适用于Android 5.0+,但新证书和旧证书来自不同的颁发者)
我不明白为什么有必要将此方法称为systemDefaultTrustManager
要使用系统默认值,请尝试替换
sslContext.init(null, new TrustManager[] { systemDefaultTrustManager() }, null);
由
sslContext.init(null, null, null);
尝试在okHttp库的Github上发布的命题 - 这是众所周知的问题