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

版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

本文要解决的两类问题:

  1. 在Android4.1-Android5.0的系统上启用TLSv1.1和TLSv1.2
  2. java.lang.IllegalArgumentException: protocol TLSv1.1 is not supported

这两个问题比较具有代表性,在群里面讨论的时候见过的次数也是最多的,因此有些人遇到的问题也许协议名称不一样,但是本质都是类似的。

问题原因和解决思路分析

Android5.0行为变更可以看到Android5.0开始默认启用了TLSv1.1和TLSv1.2,但是从Android4.1开始TLSv1.1和TLSv1.2其实就被支持了,只是默认没有启用而已。

我们最常用到的几种协议是SSLv3、TLSv1、TLSv1.1和TLSv1.2,解决上面两个问题要搞清楚的是这几个协议在Android系统中被支持的情况和被启用的情况,然后我们结合minSdkVersion和targetSdkVersion来选择协议即可,不同版本的Android系统对上述协议的支持情况:

客户端(SSLSocket)的支持情况:

协议被支持(Api级别)被启用(Api级别)
SSLv31–251–22
TLSv11+1+
TLSv1.116+20+
TLSv1.216+20+

服务端(SSLServerSocket)的支持情况:

协议被支持(Api级别)被启用(Api级别)
SSLv31–251–22
TLSv11+1+
TLSv1.116+16+
TLSv1.216+16+

数据来源:https://developer.android.com/reference/javax/net/ssl/SSLSocket

注意:这里说的客户端和服务端不是指Android端和JavaEE端/PHP端(还有Python、.NET等等),是指的在Android开发中的客户端Socket(SSLSocket)和服务端Socket(SSLServerSocket)。

到这里其实已经知道本文开始处的问题的原因了,TLSv1.1和TLSv1.2从Android4.1(Api级别16)开始才被支持,从Android4.4 Wear(Api级别20)才被启用(手机是Android5.0,Api级别21),因此在不同版本的Android系统中会出现需要被启用和启用时报不被支持的问题。

我们可以写一个TLS协议通用的兼容类,在所有的Android中强制启用已经被支持的所有协议,总结一下就是Android8.0及以上系统可以强制启用TLSv1、TLSv1.1和TLSv1.2协议,Android4.1-Android7.1.1系统可以强制启用SSLv3、TLSv1、TLSv1.1和TLSv1.2协议,其它版本系统可以强制启用SSLv3和TLSv1协议。

综上所述,如果开发者使用SSLv3协议,那么minSdkVersion不限制,targetSdkVersion不高于25,并且需要尽快更新为TLSv1.1协议或者TLS1.2协议;如果开发者使用TLSv1.1协议或者TLSv1.2协议,那么minSdkVersion应该不低于16,targetSdkVersion不限制;如果开发者使用TLSv1协议,那么目前不受限制。

本文内容是目前本人了解到的知识,如果有其它更好解决方案或者本文没有讲到的要点请读者在评论中告知!

代码实现

我们需要让SSLSocket启用对应的协议,代码对应的方法是:

SSLSocket#setEnabledProtocols(String[]);

因此我们需要先在不同版本的Android系统中生成不同的协议数组:

private static final String PROTOCOL_ARRAY[];

static {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        PROTOCOL_ARRAY = new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"};
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        PROTOCOL_ARRAY = new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"};
    } else {
        PROTOCOL_ARRAY = new String[]{"SSLv3", "TLSv1"};
    }
}

SSLSocket是由SSLSocketFactory负责生成的,我们再写一个SSLSocketFactory的包装类,主要代码如下:

public class TLSSocketFactory extends SSLSocketFactory {

    private static final String PROTOCOL_ARRAY[];

    static {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            PROTOCOL_ARRAY = new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"};
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            PROTOCOL_ARRAY = new String[]{"SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"};
        } else {
            PROTOCOL_ARRAY = new String[]{"SSLv3", "TLSv1"};
        }
    }

    private SSLSocketFactory delegate;

    /**
     * 默认构造方法,直接生成启用所有协议的SSLSocket。
     */
    public TLSSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{DEFAULT_TRUST_MANAGERS}, new SecureRandom());
            delegate = sslContext.getSocketFactory();
        } catch (GeneralSecurityException e) {
            throw new AssertionError(); // The system has no TLS. Just give up.
        }
    }

    /**
     * 包装SSLSocketFactory的构造方法,让外部生成的SSLScoket启用所有协议。
     */
    public TLSSocketFactory(SSLSocketFactory factory) {
        this.delegate = factory;
    }

    /**
     * 如果是SSLSocket,启用所有协议。
     */
    private static void setSupportProtocolAndCipherSuites(Socket socket) {
        if (socket instanceof SSLSocket) {
            ((SSLSocket) socket).setEnabledProtocols(PROTOCOL_ARRAY);
        }
    }

    // TODO 下面省去每一个createSocket()方法调用setSupportProtocolAndCipherSuites()方法的代码。
}

完整源码我了一段gist放到Github了:https://gist.github.com/yanzhenjie/f7217f3fc0f641d9ef50ca0145a7185c

特别强调:成员变量delegate强烈建议不要更改,原因是客户端使用OkHttp(本人是使用okhttp-urlconnection时遇到的问题,没有测试直接使用OkHttp是否存在问题)时,OkHttp会反射SSLSocketFactory的delegate变量来做一些操作,否则会出现Https请求失败的问题。

写下本文时OkHttp的版本是3.10.0,相关源码地址:
https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/internal/platform/Platform.java


版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
AndroidTLS(Transport Layer Security)是一种安全协议,用于在网络通信提供加密和认证。它的作用是保护数据的机密性、完整性和身份验证。 在Android开发,可以使用TLS协议来加密网络连接,以确保数据在传输过程不被窃听或篡改。Android提供了一些API和类来支持TLS协议的使用,例如javax.net.sslSSLSocketSSLContext类。 要使用TLS协议进行安全通信,您需要创建一个TLS连接,并使用SSLContext类来配置TLS参数。然后,您可以使用SSLSocket类来建立与服务器的安全连接,并通过该连接发送和接收数据。 以下是一个简单的示例代码,展示了如何在Android使用TLS协议进行安全通信: ```java try { // 创建SSLContext对象,并指定TLS协议 SSLContext sslContext = SSLContext.getInstance("TLS"); // 初始化SSLContext对象 sslContext.init(null, null, null); // 创建SSLSocketFactory对象 SSLSocketFactory socketFactory = sslContext.getSocketFactory(); // 创建URL对象 URL url = new URL("https://example.com"); // 打开HTTPS连接 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); // 设置SSLSocketFactory connection.setSSLSocketFactory(socketFactory); // 发送请求并获取响应 InputStream inputStream = connection.getInputStream(); // 处理响应数据 // ... } catch (Exception e) { e.printStackTrace(); } ``` 这只是一个简单的示例,实际使用可能需要更复杂的配置和处理。您可以根据您的具体需求和服务器配置进行调整和扩展。 请注意,TLS协议的版本和加密算法可以根据您的需求进行配置,以提供更高的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值