Android5.0以下https兼容ssl3的问题

16 篇文章 0 订阅

一、前言

一般来说大家请求一些经过CA认证的https网址时候是不需要做额外操作的,使用正常的https请求方式就好了,有些封装好的第三方库,比如OkHttp则更为方便,只是修改一下网址就可以了。但是https里面有很多不同版本的协议和套接字,比如TSL1.0, TSL2.0,TSL3.0,SSL1,SSL2,SSL3等,还有其余很多细小的东西。一般来说两边协议都一致才能访问成功,假设两边支持的协议不一致,就无法访问成功。一般来说都是没什么问题的,但是由于技术发展过快,在以前的Android5.0以下对ssl3兼容有问题。现在如果新生成的CA证书,或者使用nginx生成的证书在忽略这一点时候会出现无法访问的问题,虽然https://www.baidu.com在系统浏览器里面无法访问。

二、解决方式

解决方式的话就是在进行https校验的话强制使用TLS1.0TLS2.0较低的也是较全的协议进行校验。
使用该解决方式的前提是:
在使用比较新的框架或者版本时候会默认使用ssl3进行通信,但是因为客户端系统比较旧不支持该协议,所以需要强制指定协议版本为TLS1.0TLS2.0。但是假若,服务器只支持ssl3,或者双方没有共同的协议,该指定方式是无效的,所以文末会有查验服务器和手机支持的协议的方式以及拓展协议的方式

三、代码

这里主要使用的是HttpUrlConnection去编写的网络请求,在低版本的时候底层还是使用HttpClient,高版本改成了OkHttp。配置入口可能不一样,但是配置代码是一样的。另外网上流传的很多方式都不太好使,这些代码主要还是参考的OkHttp的部分源码改的。这个代码主要是我的朋友提供,这里仅作为收录。

TLSSocketFactory.java


import android.support.annotation.Nullable;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class TLSSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory delegate;
    private TrustManager[] trustManagers;

    public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
        generateTrustManagers();
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, trustManagers, null);
        delegate = context.getSocketFactory();
    }

    private void generateTrustManagers() throws KeyStoreException, NoSuchAlgorithmException {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init((KeyStore) null);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:"
                    + Arrays.toString(trustManagers));
        }

        this.trustManagers = trustManagers;
    }


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

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

    @Override
    public Socket createSocket() throws IOException {
        return enableTLSOnSocket(delegate.createSocket());
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return enableTLSOnSocket(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return enableTLSOnSocket(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
    }

    private Socket enableTLSOnSocket(Socket socket) {
        if(socket != null && (socket instanceof SSLSocket)) {
            ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
        }
        return socket;
    }

    @Nullable
    public X509TrustManager getTrustManager() {
        return  (X509TrustManager) trustManagers[0];
    }

}

NullHostNameVerifier.java

import android.util.Log;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

public class NullHostNameVerifier implements HostnameVerifier {

    @Override   
    public boolean verify(String hostname, SSLSession session) {
        Log.i("RestUtilImpl", "Approving certificate for " + hostname);
        return true;
    }

}

SSLUtils.java

import android.os.Build;


import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SSLUtils {

    private static void trustAllHttpsCertificates() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[1];
        TrustManager tm = new miTM();
        trustAllCerts[0] = tm;
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    static class miTM implements TrustManager, X509TrustManager {
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public boolean isServerTrusted(X509Certificate[] certs) {
            return true;
        }

        public boolean isClientTrusted(X509Certificate[] certs) {
            return true;
        }

        @Override
        public void checkServerTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }
    }

    /**
     * 忽略HTTPS请求的SSL证书,必须在openConnection之前调用
     *
     * @throws Exception
     */
    public static void ignoreSsl(HttpsURLConnection conn) throws Exception {


        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {// 5.0以下做https兼容处理
            HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory());
            ((HttpsURLConnection) conn).setSSLSocketFactory(new TLSSocketFactory());
        }

//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//            HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
// 			  ((HttpsURLConnection) conn).setHostnameVerifier(new NullHostNameVerifier());
//            trustAllHttpsCertificates();
//        } else {
//            HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory());
//            ((HttpsURLConnection) conn).setSSLSocketFactory(new TLSSocketFactory());
//
//        }


    }


    //这里是创建一个SSLSocketFactory,提供给上面的 .sslSocketFactory()
    public static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
        }

        return ssfFactory;
    }

}

注释的代码是因为高版本无需处理,所以只处理低版本,如果高版本需要处理,则视情况决定

使用方式如下
在执行网络请求前调用
SSLUtils.ignoreSsl(connection);

本地不支持的加密套件

如果出现本地不支持的加密套件,服务器又无法修改,那么需要本地升级openssl.so。新的库去GitHub下载,该方案未测试
https://github.com/leenjewel/openssl_for_ios_and_android

待验证的搜集的链接:

  1. SSLSocket,Android各个版本协议支持情况:
    https://developer.android.google.cn/reference/javax/net/ssl/SSLSocket#cipher-suites

  2. 不同版本的TLS在Android中的支持情况

  3. 检查服务器协议支持情况(使用nmap命令):
    https://blog.csdn.net/wurensen/article/details/122343213
    https://gist.github.com/gotev/f1a8a221e2d1d09bcb93e823b8e5a05a

  4. OkHttp对TLS1.2的兼容:support enabling TLSv1.2 on Android 4.1-4.4

  5. 服务器https在线测试情况1(这个网站更好点,会列出加密算法,比如x25519):
    https://www.ssllabs.com/ssltest/

  6. 服务器https在线测试情况2:
    https://myssl.com

  7. Android 4.2.2,如何为HttpsURLConnection启用TLS 1.2?

  8. android版本对tls支持,Android 4.2 TLS1.2支持(包含了怎么添加不支持的加密套件):
    https://www.codenong.com/js469fd525c191/

  9. conscrypt(该库可以在Android SDK版本9 之上支持所有最新的tls协议及缺少的加密套件): https://github.com/google/conscrypt/

  10. ssl-provider(一个轻量级加密库,如果协议缺少就会下载conscrypt进行支持):https://github.com/szkolny-eu/ssl-provider

  11. wolfssljni(也是一个加密程序,但是应该是跟服务端配套使用,需要测试):https://github.com/wolfSSL/wolfssljni

  12. https://blog.csdn.net/weixin_30249953/article/details/115781291
    测试工具:
    https://github.com/mozilla/cipherscan

可以验证椭圆曲线算法属于哪一种加密套件版本。然后选择启用哪一个加密套件

  1. X25519仅在Java13时候开始支持

https://zhuanlan.zhihu.com/p/491112832

  1. NetCipher加密库,支持Android平台的网络请求

关于X25519的一些帖子

  1. SSLHandshakeException: Handshake failed on Android N/7.0 :
    https://stackoverflow.com/questions/39133437/sslhandshakeexception-handshake-failed-on-android-n-7-0
  2. Is X25519 available as curve for KEX in Android? :
    https://stackoverflow.com/questions/47959542/is-x25519-available-as-curve-for-kex-in-android
  3. Android - SSL/TLS and ECC (Elliptic curve cryptography) :
    https://stackoverflow.com/questions/9114950/android-ssl-tls-and-ecc-elliptic-curve-cryptography
  4. SSL/TLS深度解析–测试TLS/SSL加密
    https://www.yisu.com/zixun/53187.html
  5. KeyAgreement:
    https://developer.android.com/reference/javax/crypto/KeyAgreement
  6. 通过 HTTPS 和 SSL 确保安全:
    https://developer.android.google.cn/training/articles/security-ssl
  7. 关键词: Curve25519 X25519
    利用openssl查看网站证书
    https://blog.csdn.net/m0_46500807/article/details/113783957
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值