OkHttp在4.4及以下不支持TLS协议的解决方法

错误信息如下:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x79f145b0: Failure in SSL library, usually a protocol error
error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version

原因:Android 4.4及以下的系统默认不支持TLS协议,所以遇到使用TLS协议的网站就无法访问了。

解决方法如下:创建一个SSLSocketFactoryCompat.java文件,内容如下:

 

  1 import java.io.IOException;
  2 import java.net.InetAddress;
  3 import java.net.Socket;
  4 import java.net.UnknownHostException;
  5 import java.security.GeneralSecurityException;
  6 import java.util.Arrays;
  7 import java.util.HashSet;
  8 import java.util.LinkedList;
  9 import java.util.List;
 10 
 11 import javax.net.ssl.SSLContext;
 12 import javax.net.ssl.SSLSocket;
 13 import javax.net.ssl.SSLSocketFactory;
 14 import javax.net.ssl.X509TrustManager;
 15 
 16 public class SSLSocketFactoryCompat extends SSLSocketFactory {
 17     private SSLSocketFactory defaultFactory;
 18     // Android 5.0+ (API level21) provides reasonable default settings
 19     // but it still allows SSLv3
 20     // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
 21     static String protocols[] = null, cipherSuites[] = null;
 22     static {
 23         try {
 24             SSLSocket socket = (SSLSocket)SSLSocketFactory.getDefault().createSocket();
 25             if (socket != null) {
 26                 /* set reasonable protocol versions */
 27                 // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
 28                 // - remove all SSL versions (especially SSLv3) because they're insecure now
 29                 List<String> protocols = new LinkedList<>();
 30                 for (String protocol : socket.getSupportedProtocols())
 31                     if (!protocol.toUpperCase().contains("SSL"))
 32                         protocols.add(protocol);
 33                 SSLSocketFactoryCompat.protocols = protocols.toArray(new String[protocols.size()]);
 34                 /* set up reasonable cipher suites */
 35                 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 36                     // choose known secure cipher suites
 37                     List<String> allowedCiphers = Arrays.asList(
 38                             // TLS 1.2
 39                             "TLS_RSA_WITH_AES_256_GCM_SHA384",
 40                             "TLS_RSA_WITH_AES_128_GCM_SHA256",
 41                             "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
 42                             "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
 43                             "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
 44                             "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
 45                             "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",
 46                             // maximum interoperability
 47                             "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
 48                             "TLS_RSA_WITH_AES_128_CBC_SHA",
 49                             // additionally
 50                             "TLS_RSA_WITH_AES_256_CBC_SHA",
 51                             "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
 52                             "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
 53                             "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
 54                             "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
 55                     List<String> availableCiphers = Arrays.asList(socket.getSupportedCipherSuites());
 56                     // take all allowed ciphers that are available and put them into preferredCiphers
 57                     HashSet<String> preferredCiphers = new HashSet<>(allowedCiphers);
 58                     preferredCiphers.retainAll(availableCiphers);
 59                     /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling
 60                      * ciphers which are enabled by default, but have become unsecure), but I guess for
 61                      * the security level of DAVdroid and maximum compatibility, disabling of insecure
 62                      * ciphers should be a server-side task */
 63                     // add preferred ciphers to enabled ciphers
 64                     HashSet<String> enabledCiphers = preferredCiphers;
 65                     enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites())));
 66                     SSLSocketFactoryCompat.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]);
 67                 }
 68             }
 69         } catch (IOException e) {
 70             throw new RuntimeException(e);
 71         }
 72     }
 73     public SSLSocketFactoryCompat(X509TrustManager tm) {
 74         try {
 75             SSLContext sslContext = SSLContext.getInstance("TLS");
 76             sslContext.init(null, (tm != null) ? new X509TrustManager[] { tm } : null, null);
 77             defaultFactory = sslContext.getSocketFactory();
 78         } catch (GeneralSecurityException e) {
 79             throw new AssertionError(); // The system has no TLS. Just give up.
 80         }
 81     }
 82     private void upgradeTLS(SSLSocket ssl) {
 83         // Android 5.0+ (API level21) provides reasonable default settings
 84         // but it still allows SSLv3
 85         // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
 86         if (protocols != null) {
 87             ssl.setEnabledProtocols(protocols);
 88         }
 89         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && cipherSuites != null) {
 90             ssl.setEnabledCipherSuites(cipherSuites);
 91         }
 92     }
 93     @Override
 94     public String[] getDefaultCipherSuites() {
 95         return cipherSuites;
 96     }
 97     @Override
 98     public String[] getSupportedCipherSuites() {
 99         return cipherSuites;
100     }
101     @Override
102     public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
103         Socket ssl = defaultFactory.createSocket(s, host, port, autoClose);
104         if (ssl instanceof SSLSocket)
105             upgradeTLS((SSLSocket)ssl);
106         return ssl;
107     }
108     @Override
109     public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
110         Socket ssl = defaultFactory.createSocket(host, port);
111         if (ssl instanceof SSLSocket)
112             upgradeTLS((SSLSocket)ssl);
113         return ssl;
114     }
115     @Override
116     public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
117         Socket ssl = defaultFactory.createSocket(host, port, localHost, localPort);
118         if (ssl instanceof SSLSocket)
119             upgradeTLS((SSLSocket)ssl);
120         return ssl;
121     }
122     @Override
123     public Socket createSocket(InetAddress host, int port) throws IOException {
124         Socket ssl = defaultFactory.createSocket(host, port);
125         if (ssl instanceof SSLSocket)
126             upgradeTLS((SSLSocket)ssl);
127         return ssl;
128     }
129     @Override
130     public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
131         Socket ssl = defaultFactory.createSocket(address, port, localAddress, localPort);
132         if (ssl instanceof SSLSocket)
133             upgradeTLS((SSLSocket)ssl);
134         return ssl;
135     }
136 }

在创建OkHttpClient的时候,添加这个SSLSocketFactory(我用单例模式来每次获取同一个全局的OkHttpClient,每个人的实现不一定一样,从我的代码里参考问题的解决方法就好):

 1 public synchronized static OkHttpClient getClient(){
 2         if (okHttpClient == null) {
 3             OkHttpClient.Builder builder = new OkHttpClient.Builder();
 4             try {
 5                 // 自定义一个信任所有证书的TrustManager,添加SSLSocketFactory的时候要用到
 6                 final X509TrustManager trustAllCert =
 7                         new X509TrustManager() {
 8                             @Override
 9                             public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
10                             }
11 
12                             @Override
13                             public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
14                             }
15 
16                             @Override
17                             public java.security.cert.X509Certificate[] getAcceptedIssuers() {
18                                 return new java.security.cert.X509Certificate[]{};
19                             }
20                         };
28                 final SSLSocketFactory sslSocketFactory = new SSLSocketFactoryCompat(trustAllCert);
29                 builder.sslSocketFactory(sslSocketFactory, trustAllCert);
30             } catch (Exception e) {
31                 throw new RuntimeException(e);
32             }
33             okHttpClient = builder.build();
34         }
35         return okHttpClient;
36     }

这样就可以解决了~

posted @ 2016-09-18 23:58  daquexian  阅读(13803)  评论(0编辑  收藏  举报