您的位置:首页 > 理论基础 > 计算机网络

Android6.0使用Https出现SSLProtocolException

2016-06-25 15:18 447 查看
转载自:http://blog.csdn.net/duanbokan/article/details/50911148

为做Android6.0版本的兼容性修改,升级后,在测试我们的应用时,突然出现握手失败错误:



但是在Android6.0以下版本中,并没有出现该问题。


一、Android6.0的一些修改

因为该问题仅仅出现在Android6.0版本中,因此,考虑是由版本升级引起的。查看Google给出的Android6.0修改文档,发现以下两点:



即:
从Android6.0之后将不再支持HttpClient的使用,建议使用HttpURLConnection代替。
Android6.0之后,在Https请求中,SSL层将不再使用OpenSSL协议,改用自己的BoringSSL协议


二、分析


2.1 取消HttpClient

在我们的项目中使用的是HttpClient执行Https请求,但是官方升级只是在API文档中删除了HttpClient相关的文件,但是并不影响其使用,用户可以通过以下两种方法继续使用:

使用Android6.0进行编译,则需要添加 org.apache.http.legacy.jar,文件目录:SDK\platforms\Android-23\optional;或者是在AndroidStudio中的build.gradle文件中加入:
android {useLibrary 'org.apache.http.legacy'
}

使用Android6.0以下版本进行编译。

因此,排除该可能。


2.2 使用BoringSSL替换OpenSSL

因为考虑到是该问题引起的,因此回过头重新对握手失败原因进行查看,发现在之前Log结尾部分忽略了一句话,也正是因为忽略这句话,导致之前思维一直停在可能是HttpClient被取消导致的,浪费了很多时间,握手失败后,在最后边Log中,出现这样的信息:



关键字:BAD_DH_P_LENGTH
经过一番寻找,发现意思应该是Diffie-Hellman的p参数长度错误。主要参考文章:谢谢大神——连接
通过对帖子的阅读和资料的查找,总结其主要原因在于:
Https建立连接之前,会进行多次握手,即单向认证和双向认证。在该过程中,客户端会将自己支持的所有加密方式发送给服务端,供服务端选择,服务端选择好加密程度较高的加密方式后,会以明文或者是客户端私钥加密密文的方式发送给客户端。
具体认证过程可参考我的上一篇文章:Https单向认证和双向认证
在握手过程中,必定会涉及到公钥加密,私钥解密的过程,而该过程中,当服务端选择使用诸如
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
算法进行加密时,需要使用到Diffie-Hellman算法进行加密解密,通过阅读Diffie-Hellman算法的介绍,发现在加密解密计算过程中,会使用到两个参数,一个是q,一个是a,而在JDK8之前,服务器端提供的q参数只是用了768bit的长度,而不足1024bit则存在相应的安全漏洞,会被替换后的BroingSSL拒绝,因此出现了Handshake
failed错误。
终于找到问题所在了,总结这个纠结的过程,我只想说:一定要认真看Log!!!


三、解决方案

通过阅读上边提到的大神写的帖子,发现两种可以解决的办法:

升级JDK到8(条件限制,未经过测试)

配置Tomcat服务器,限制加密方式:
修改Tomcat服务器conf/server.xml文件中和Https有关的Connector节点,添加ciphers用于指定密钥:
<Connector
SSLEnabled="true"
clientAuth="false"
connectionTimeout="20000"
keystoreFile="/usr/xinwei/tienlen/apache-tomcat-https/server.keystore"
keystorePass="xinwei"
maxThreads="150"
port="443"
protocol="org.apach
4000
e.coyote.http11.Http11Protocol"
redirectPort="8443"
scheme="https"
secure="true"
ciphers="TLS_RSA_WITH_AES_128_CBC_SHA256,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA256,
TLS_RSA_WITH_AES_256_CBC_SHA,
SSL_RSA_WITH_3DES_EDE_CBC_SHA"
sslProtocol="TLS"
truststoreFile="/usr/xinwei/tienlen/apache-tomcat-https/server.keystore"
truststorePass="密码"
/>


添加完该配置后,重启,测试,Android6.0版本没有再发现Handshake failed错误。


四、总结

这篇文章主要是记录了我解决该问题的过程和方法,没什么特别多的原创。主要是在原文作者的基础上,更深入的解释了一下出现该错误的原因。
同时也因为我的疏忽,浪费了大量时间,引以为戒。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息