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

在做超理论坛app的过程中,遇到许多用户反馈在他们的手机上客户端不能访问网络,我问了他们的手机型号和Android系统版本,全部是5.0以下的,之后我自己运行API19(4.4)的Android模拟器,也遇到了同样的错误。

错误信息如下:

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     }

这样就可以解决了~

不知道如果用别人在OkHttp基础上封装好的工具类,比如okhttp-utils还会不会有这个问题,不过我在使用OkHttp之前,是用AsyncHttpClient做网络库的,因为它已经太老而过时所以换到了OkHttp,在使用AsyncHttpClient的时候也遇到了这个问题

时间: 2024-10-25 11:57:29

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

ASP.NET MVC 此安装不支持该项目类型解决方法

http://www.cnblogs.com/younggun/archive/2011/03/03/1969498.html ASP.NET MVC  此安装不支持该项目类型解决方法 打开 .csproject 文件  在  <ProjectTypeGuids>中的三个GUID的前两个修改为: {F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21}; 后面还有一个 GUID 是你项目的GUID

ubuntu不支持rpm安装软件解决方法

以前经常使用的是redhat,习惯使用rpm方法安装软件.最近发现Ubuntu系统居然不支持rpm方法安装软件,提示信息如下: [email protected]$ rpm -i package.rpm The program 'rpm' is currently not installed.  You can install it by typing: sudo apt-get install rpm 然而通过apt-get安装rpm包以后依然不能够安装,最后才知道原来Ubuntu不支持rpm

IE6/IE7浏览器不支持display: inline-block;的解决方法

display: inline-block;在IE6与IE7中存在bug. 1.inline元素的display属性设置为inline-block时,所有的浏览器都支持: 2.block元素的display属性设置为inline-block时,IE6/IE7浏览器是不支持的: IE中对内联元素使用display:inline-block,IE是不识别的,但使用display:inline-block在IE下会触发layout,从而使内联元素拥有了display:inline-block属性的表征

使安卓手机支持ipv6的终极解决方法

使安卓手机支持ipv6的终极方法 2015年3月29日 | 分类: 网络技术 测试日期:2015年3月29日 测试环境:安卓4.2 需要软件: 1,用于管理:smanager   (script manager) 2,用于编辑:Smeditor  (script manager and Editor) 3,用于浏览和寻址:ES   文件管理器 4:QQ文件传送器 5,linux文件编辑器:EDITPLUS 安装地址: C:\Documents\Tencent Files\你的QQ号码\FileR

Python内置的urllib模块不支持https协议的解决办法

Django站点使用django_cas接入SSO(单点登录系统),配置完成后登录,抛出“urlopen error unknown url type: https”异常.寻根朔源发现是python内置的urllib模块不支持https协议. >>> import urllib>>> urllib.urlopen('http://www.baidu.com')<addinfourl at 269231456 whose fp = <socket._fileo

Sass、Less编译器koala及koala不支持中文字体的解决方法

一款很好用的Sass编译器,还可以编译Less.coffeescript等 去官网下载适合自己电脑的版本 http://koala-app.com/index-zh.html 打开后拖动或者打开项目目录,如果文件种类较多,可以在下方筛选需要的项目类型 点击左上角的设置,可以修改为中文语言,再重启一下就设置成功了 右键单击项目设置输出目录及输出css文件名,点右边操作的执行编译就可以看到实时生成的CSS文件了 koala编译的Sass等是不支持中文字体的,解决方法: 1.在scss文件第一行加上这

远程计算机需要网络级别身份验证,而您的计算机不支持该验证的解决方法

故障:"远程计算机需要网络级别身份验证,而您的计算机不支持该验证,请联系您的系统管理员或者技术人员来获得帮助" 故障症状:当您使用Windows XP"远程桌面连接"工具去连接Windows Vistas或Windows Server 2008的远程桌面.终端服务时,出现上述故障. 故障产生环境:远程桌面连接工具6.0以下版本,或者Windows XP Profressional SP1.SP2.SP3 解决方法:1.请升级"远程桌面连接"工具最

服务不支持 chkconfig 的解决方法

"服务不支持 chkconfig": 请注意检查脚本的前面,是否有完整的两行:#chkconfig: 2345 80 90    #description:auto_run 在脚本前面这两行是不能少的,否则不能chkconfig命令会报错误.

IIS7.5 webapi 不支持 Delete、Put 解决方法

在IIS管理界面选择API的项目,选择 “Features View”. 2.  选择 “Handler Mappings” 菜单. 3. 打开“WebDAV” 选项. 4. 点击 “Request Restrictions” 选项. 5. 选择 “Vebs” 菜单项,选 “All verbs”,保存即可. 这样设置IIS 就支持 Delete 和 Put,你可以不全部放开,放开部分的方法也是可以的.