Android 使用 Https问题解决(SSLHandshakeException)

title date categories tags

Android 5.0以下TLS1.x SSLHandshakeException

2016-11-30 12:17:02 -0800


Android


Android

TLSv1.x

最近把App的所有请求都换成Https,在测试的时候,部分手机发现请求失败,失败的异常信息如下:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x783e8e70: Failure in SSL library, usually a protocol error
error:14077102:SSL routines:SSL23_GET_SERVER_HELLO:unsupported protocol (external/openssl/ssl/s23_clnt.c:714 0x71a20cf8:0x00000000)

该异常为握手失败,但是为什么有的手机可以成功有的手机又失败了呢,首先查看我们服务端接口TLS支持的版本为1.x,后来发现失败的手机都是5.x以下的版本,推测应该是和这个有关,然后查阅官方文档,SSLSocket中有提到TLS版本和Android SDK版本的对应表,如下:

Protocol Supported (API Levels) Enabled by default (API Levels)
SSLv3 1+ 1+
TLSv1 1+ 1+
TLSv1.1 16+ 20+
TLSv1.2 16+ 20+

通过这个表看到,TLSv1.x(1.1,1.2)Android默认从API16开始支持,而从API20开始默认可用,这就可以解释之前为什么5.x以下手机在进行请求时失败了。

知道问题的原因,我们就要解决,当然服务端可以支持TLSv1版本,这样我们就都可以请求成功,但是这并不是最好的解决方法,我们当然要让我们的App支持新的TLS协议才对。

通过官方文档发现Cipher suites有的也是API20+支持或者默认可用,所以我们如果想支持TLSv1.x版本,可能需要给低版本添加Cipher suites,所以我们需要自定义SSLSocketFactory,自定义的SSLSocketFactory如下:

public class SSL extends SSLSocketFactory {
    private SSLSocketFactory defaultFactory;
    // Android 5.0+ (API level21) provides reasonable default settings
    // but it still allows SSLv3
    // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
    static String protocols[] = null, cipherSuites[] = null;

    static {
        try {
            SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
            if (socket != null) {
                /* set reasonable protocol versions */
                // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
                // - remove all SSL versions (especially SSLv3) because they‘re insecure now
                List<String> protocols = new LinkedList<>();
                for (String protocol : socket.getSupportedProtocols())
                    if (!protocol.toUpperCase().contains("SSL"))
                        protocols.add(protocol);
                SSL.protocols = protocols.toArray(new String[protocols.size()]);
                /* set up reasonable cipher suites */
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    // choose known secure cipher suites
                    List<String> allowedCiphers = Arrays.asList(
                            // TLS 1.2
                            "TLS_RSA_WITH_AES_256_GCM_SHA384",
                            "TLS_RSA_WITH_AES_128_GCM_SHA256",
                            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                            "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                            "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",
                            // maximum interoperability
                            "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
                            "TLS_RSA_WITH_AES_128_CBC_SHA",
                            // additionally
                            "TLS_RSA_WITH_AES_256_CBC_SHA",
                            "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
                            "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                            "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
                            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
                    List<String> availableCiphers = Arrays.asList(socket.getSupportedCipherSuites());
                    // take all allowed ciphers that are available and put them into preferredCiphers
                    HashSet<String> preferredCiphers = new HashSet<>(allowedCiphers);
                    preferredCiphers.retainAll(availableCiphers);
                    /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling
                     * ciphers which are enabled by default, but have become unsecure), but I guess for
                     * the security level of DAVdroid and maximum compatibility, disabling of insecure
                     * ciphers should be a server-side task */
                    // add preferred ciphers to enabled ciphers
                    HashSet<String> enabledCiphers = preferredCiphers;
                    enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites())));
                    SSL.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public SSL(X509TrustManager tm) {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, (tm != null) ? new X509TrustManager[]{tm} : null, null);
            defaultFactory = sslContext.getSocketFactory();
        } catch (GeneralSecurityException e) {
            throw new AssertionError(); // The system has no TLS. Just give up.
        }
    }

    private void upgradeTLS(SSLSocket ssl) {
        // Android 5.0+ (API level21) provides reasonable default settings
        // but it still allows SSLv3
        // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
        if (protocols != null) {
            ssl.setEnabledProtocols(protocols);
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && cipherSuites != null) {
            ssl.setEnabledCipherSuites(cipherSuites);
        }
    }

    @Override public String[] getDefaultCipherSuites() {
        return cipherSuites;
    }

    @Override public String[] getSupportedCipherSuites() {
        return cipherSuites;
    }

    @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        Socket ssl = defaultFactory.createSocket(s, host, port, autoClose);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket) ssl);
        return ssl;
    }

    @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        Socket ssl = defaultFactory.createSocket(host, port);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket) ssl);
        return ssl;
    }

    @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        Socket ssl = defaultFactory.createSocket(host, port, localHost, localPort);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket) ssl);
        return ssl;
    }

    @Override public Socket createSocket(InetAddress host, int port) throws IOException {
        Socket ssl = defaultFactory.createSocket(host, port);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket) ssl);
        return ssl;
    }

    @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        Socket ssl = defaultFactory.createSocket(address, port, localAddress, localPort);
        if (ssl instanceof SSLSocket)
            upgradeTLS((SSLSocket) ssl);
        return ssl;
    }
}

然后我们只需要给我们的请求设置这个SSLSocketFactory就可以了,我们以okhttp为例,如下:

//定义一个信任所有证书的TrustManager
final X509TrustManager trustAllCert = new X509TrustManager() {
    @Override
    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
    }

    @Override
    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
    }

    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return new java.security.cert.X509Certificate[]{};
    }
};
//设置OkHttpClient
OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(new SSL(trustAllCert), trustAllCert).build();

设置之后,用低版本手机测试Https,现在可以测试成功了。

时间: 2024-07-28 18:25:52

Android 使用 Https问题解决(SSLHandshakeException)的相关文章

最近排查android webview https的发热耗电和加载速度慢问题解决

最近排查android webview https的发热耗电和加载速度慢问题问题:H5页面发热耗电排查:通过android studio profiler 查看CPU消耗曲线,发现静置情况下webview轮播图波浪式消耗CPU,且峰值高达45%.因为WebView加载的H5页面中的动画导致的是整个WebView的重绘.解决:换用X5内核,没能解决CPU峰值高问题.发现轮播图是JQUERY写的,换用纯js实现轮播图,问题解决,CPU峰值降为8%. 问题:https H5加载速度慢解决:1.首先排查

Android技术5:Android SDK更新问题解决

由于某些原因无法访问Google的所有网站,因此造成android SDK版本工具无法更新. 第一,我们先修改下hosts文件.该文件的位置在系统盘(一般为C盘),具体路径为:C:\Windows\System32\ drivers\etc\hosts.添加以下内容即可 #更新的内容从以下地址下载203.208.46.146 dl.google.com203.208.46.146 dl-ssl.google.com 第二,在Android SDK Manager的Tool->Option选项中把

关于Android的https通讯安全

原文链接:http://pingguohe.net/2016/02/26/Android-App-secure-ssl.html 起因 前段时间,同事拿着一个代码安全扫描出来的 bug 过来咨询,我一看原来是个 https 通信时数字证书校验的漏洞,一想就明白了大概:其实这种问题早两年就有大规模的暴露,各大厂商App 也纷纷中招,想不到过了这么久天猫客户端里还留有这种坑:然后仔细研究了漏洞所在的代码片段,原来所属的是新浪微博分享 sdk 内部的,因为这个 sdk 是源码引用的,一直没有更新,年久

fiddler Android下https抓包全攻略

fiddler Android下https抓包全攻略 fiddler的http.https的抓包功能非常强大,可非常便捷得对包进行断点跟踪和回放,但是普通的配置对于像招商银行.支付宝.陌陌这样的APP是抓不到包的,需要一些特殊的配置,本文把fiddler Android下https抓包的详细配置都罗列出来,供大家参考. 一.普通https抓包设置 先对Fiddler进行设置: 勾选“CaptureHTTPS CONNECTs”,接着勾选“Decrypt HTTPS traffic”.同时,由于我

[Gradle] Gradle 构建 android 应用常见问题解决指南

转载地址:http://www.cnblogs.com/youxilua/p/3348162.html 1: 使用最新的gradle android插件 以前我们写的时候会这么写 dependencies { classpath 'com.android.tools.build:gradle:0.5.0' } 不过,由于android gradle 插件的开发还是很活跃的,而且目前而言,可能还存在一些我们不知道的坑,但是,别人踩过,后边,官方修复,为了不踩坑,我建议android gradle

Android关于 https SSL handshake aborted 问题查找

Android 关于 https SSL handshake aborted 问题查找 记录这个问题的定位过程和问题定位的方法 1.问题描述: 项目改为使用 https 协议,访问时发现返回错误代码: 域名:https://sandbox.api.xxx.com 错误:SSL handshake aborted: ssl=0x5ef8e720: I/O error during system call, Connection reset by peer 错误描述:https 在使用 ssl 时候

android 搭建https Server

在android上采用http协议的服务器,需求有点奇葩,非要用https更是醉了.这里只要求单向https认证,不要双向认证. 本文采用的开源框架Nanohttpd( https://github.com/NanoHttpd/nanohttpd ),在release页面下载jar包本地进行导入. Nano的使用比较简单,集成NanoHTTPD这个类,对serve函数进行重载即可. 这里强调使用是https,这个可能比较的麻烦.下面详细进行说明 public class CenterServic

android studio 不能在线更新android SDK Manager问题解决办法

Failed to fetch URL https://dl-ssl.google.com/android/repository/addons_list-2.xml, reason: Connection to https://dl-ssl.google.com refused 以上一直会会出现以上问题,解决办法如下: 进入sdk 更新的界面 使用代理服务器进行下载更新: 然后在选择第一个图片的package的reload  重新下载,ok,大功告成!

Android开发-Android Studio使用问题解决

回头一看,很久没来更新了,归其原因,还是懒癌发作,倒是生活作息规律了,几乎每天都在11点前休息.今天趁着培训,使用android studio,发现几个坑: 1.android studio每次都提示有新版本,点击后跳转到google被墙的网页,无法下载.解决办法无非就是两种,一个是下最新版的来覆盖安装(http://www.android-studio.org/),一个是按网上的方法修正现在版本(1.5)的一些配置(http://www.linuxidc.com/Linux/2015-02/1