okhttp在android开发中使用已经很普遍。在android5.0以下版本使用okhttp时,如果需要使用https,需要指定支持tls1.1和tls1.2版本。
我们可以用google查到很多"解决方法",其实根本没那么复杂,实现本身比较简单,关键在于**读源码 **。
OkHttpClient内推荐使用public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
,而public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory)
这个方法已经被打上了@deprecated
/**
* Sets the socket factory used to secure HTTPS connections. If unset, the system default will
* be used.
*
* @deprecated {@code SSLSocketFactory} does not expose its {@link X509TrustManager}, which is
* a field that OkHttp needs to build a clean certificate chain. This method instead must
* use reflection to extract the trust manager. Applications should prefer to call {@link
* #sslSocketFactory(SSLSocketFactory, X509TrustManager)}, which avoids such reflection.
*/
原因是如果不提供X509TrustManager, okhttp会使用反射的方法创建一个空的证书链。
其实X509TrustManager
和SSLSocketFactory
已经提供了现成的方法,只不过是private的,copy出来就可以了
private 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.
}
}
private SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
return sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
我们唯一需要做的就是创建一个SSLSocketFactory的代理类, 通过代理设置SSLSocket的TLS版本吗,然后使用代理类替换默认的SSLSocketFactory。
完整代码如下:
private 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.
}
}
private SSLSocketFactory sslSocketFactory(X509TrustManager trustManager) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
return new TLSSocketFactory(sslContext.getSocketFactory());
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
public OkHttpClient.Builder compactTls(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
try {
X509TrustManager trustManager = systemDefaultTrustManager();
SSLSocketFactory sslSocketFactory = sslSocketFactory(trustManager);
client.sslSocketFactory(sslSocketFactory, trustManager);
} catch (Exception exc) {
logger.error("Error while setting TLS compact", exc);
}
}
return client;
}
public class TLSSocketFactory extends SSLSocketFactory {
private static final String[] TLS_V1_V2 = {"TLSv1.1", "TLSv1.2"};
final SSLSocketFactory delegate;
public TLSSocketFactory(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 {
return patch(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
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 != null && s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_V1_V2);
}
return s;
}
}