Android 6.0 HTTPS SSL 无法访问,提示Handshake failed(握手失败),解决方案

前言

之前开发的一个项目使用的是http请求,但是安全公司给出了一个安全报告,建议使用https协议来访问网络资源,使用私签证书来实现了https。Android 6.0以下的版本均可以使用,Android 6.0及以上的机型请求成功,并且在logcat中有Handshake failed的Exception。

分析

Android 6.0以下可以访问,6.0及以上不可以,初步判断是因为Android版本更新造成的,
经过一番google之,根据本地报错含有BAD_DH_P_LENGTH关键字。
确定问题出现在Java ssl 加密漏洞上。

在握手过程中,必定会涉及到公钥加密,私钥解密的过程,而该过程中,当服务端选择使用诸如TLS_DHE_RSA_WITH_AES_128_CBC_SHA等算法进行加密时,需要使用到Diffie-Hellman算法进行加密解密,通过阅读Diffie-Hellman算法的介绍,发现在加密解密计算过程中,会使用到两个参数,一个是q,一个是a,而在JDK8之前,服务器端提供的q参数只是用了768bit的长度,而不足1024bit则存在相应的安全漏洞,会被替换后的BroingSSL拒绝,因此出现了Handshake failed错误。

文/一只猿_(简书作者)
原文链接:http://www.jianshu.com/p/40c17846e626
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

解决方案

方案一、修改tomcat

配置Tomcat服务器,限制加密方式:
修改Tomcat服务器conf/server.xml文件中和Https有关的Connector节点,添加ciphers用于指定密钥:
SSLEnabled=”true”
clientAuth=”false”
connectionTimeout=”20000”
keystoreFile=”/usr/xinwei/tienlen/apache-tomcat-https/server.keystore”
keystorePass=”xinwei”
maxThreads=”150”
port=”443”
protocol=”org.apache.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错误。

文/一只猿_(简书作者)
原文链接:http://www.jianshu.com/p/40c17846e626
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

很遗憾,我们服务器使用的是JDK 1.6、tomcat 7 ,配置ciphers,启动服务后会报错,报错原因是不识别加密方式、所以我们没有采用这个方式,应该是因为TSL协议的版本问题

方案二、升级JDK至1.8版本

我们采用这个方式,成功修复了SSL Handshake failed的问题,只要代码中没有使用Java8 不再兼容的特性,一般没有问题。

方案三、使用OKhttp时客户端修改SSL配置客户端加密套件(未验证,收录)

OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .connectTimeout(15, TimeUnit.SECONDS)
                    .readTimeout(15, TimeUnit.SECONDS)
                    .writeTimeout(15, TimeUnit.SECONDS)
                    .addNetworkInterceptor(new StethoInterceptor())
                    .followSslRedirects(true)
                    .connectionSpecs(Collections.singletonList(getConnectionSpec()))
                    .build();

    private static ConnectionSpec getConnectionSpec() {
        ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS).tlsVersions(TlsVersion.TLS_1_0).cipherSuites(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA).build();
        return spec;
    }
  • https三次握手:
    • 客户端发送 TLS版本号 加密套件(这里) 随机数 hello
    • 服务端根据客户端发送的加密套件、TLS版本号 选择对应的加密协议及套件 把加密协议、加密套件、随机数、服务端证书、hello发送给客户端
    • 客户端收到信息后 生成随机数(通过公钥进行加密) 编码结速通知 握手结束通知
    • 服务端 通过私钥解密 编码改变通知 握手结束通知
    • 对称加密数据传输

你可能感兴趣的:(Android)