当前位置:   article > 正文

Android使用https_裴智飞

裴智飞

注:所有转载文章只作为学习记录,无其他想法。

前言
HI,欢迎来到裴智飞的《每周一博》。今天是九月第三周,我给大家介绍一下安卓如何使用https。

HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。

为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。

HTTPS和HTTP的区别主要如下:

https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
安卓里使用https的步骤如下,创建一个HttpsURLConnection并设置属性,然后去效验本地证书,接着发起请求,接受结果。

1.创建一个HttpsURLConnection并设置属性

URL url = new URL("https://baidu.com");
HttpsURLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setConnectTimeout(Common.TIME_OUT);
urlConnection.setReadTimeout(Common.FILE_TIME_OUT);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
urlConnection.setRequestProperty("Accept-Encoding", "gzip");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.效验本地证书有两种方式,一种是不安全的,什么证书都相信,另一种则是只相信指定的证书,对其进行效验。

效验要通过SSLSocketFactory创建的 SSLSocket来进行,默认的 SSLSocketFactory校验服务器的证书时,会信任设备内置的100多个根证书。

private synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
  try {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, null, null);
    return defaultSslSocketFactory = sslContext.getSocketFactory();
  } catch (GeneralSecurityException e) {
    throw new AssertionError();
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

校验服务器的证书,其实就是通过TrustManager来操作的,更一般的说是X509TrustManager;

private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
    try {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{
                new X509TrustManager() {
                    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    }

                    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    }

                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
        }, null);
        return sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
        throw new AssertionError();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这种效验过程什么证书都会信任,没有安全性可言,接下来我们看正确的配置。

public static SSLContext getSSLContext() {
    // 从assets中加载证书,取到证书的输入流
    InputStream is = getApplicationContext().getAssets().open("srca.cer");
    // 证书工厂
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    Certificate ca = cf.generateCertificate(is);

    // 加载证书到密钥库中
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null);
    keyStore.setCertificateEntry("cert", ca);

    // 加载密钥库到信任管理器
    String algorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
    trustManagerFactory.init(keyStore);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

    // 用 TrustManager 初始化一个 SSLContext
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagers, null);
    urlCon.setSSLSocketFactory(sslContext.getSocketFactory());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

有个问题:Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉,如果当前应用的请求是http请求,而非 https,这样就会导系统禁止当前应用进行该请求,如果WebView 的url用http协议,同样会出现加载失败,https则不受影响。

所以需要做一个简单的配置,在manifest的application标签下增加一行配置,同时增加一个xml文件。

 android:networkSecurityConfig="@xml/network_security_config"
  • 1
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" overridePins="true"/>
            <certificates src="user" overridePins="true" />
        </trust-anchors>
    </base-config>
</network-security-config>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然后传递参数并获取结果

 Iterator<Entry<String, Object>> it = params.entrySet().iterator();
    while (it.hasNext()) {
        Entry<String, Object> element = (Entry<String, Object>) it.next();
        sb2.append(element.getKey());
        sb2.append("=");
        sb2.append(element.getValue());
        sb2.append("&");
    }
    if (sb2.length() > 0) {
        sb2.deleteCharAt(sb2.length() - 1);
    }
    os = urlCon.getOutputStream();
    os.write(sb2.toString().getBytes());
    os.flush();

    int statusCode = urlCon.getResponseCode();
    if (statusCode == HttpsURLConnection.HTTP_OK) {
        is = urlCon.getInputStream();
        String contentEncoding = urlCon.getContentEncoding();
        if ((contentEncoding != null) && contentEncoding.contains("gzip")) {
            is = new GZIPInputStream(new BufferedInputStream(is));
        }
        // 创建字节输出流对象
        baos = new ByteArrayOutputStream();
        // 定义读取的长度
        int len = 0;
        // 定义缓冲区
        byte[] buffer = new byte[1024];
        // 按照缓冲区的大小,循环读取
        while ((len = is.read(buffer)) != -1) {
            // 根据读取的长度写入到os对象中
            baos.write(buffer, 0, len);
        }
        // 返回字符串
        String result = new String(baos.toByteArray(), "utf-8");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

结尾:
这样一个使用本地证书效验的https请求就算完成了,但是如果不对内容做加密,而只是依赖https通道是不安全的,因为请求https的证书如果还用https,那么就是一个无尽循环,而用http有如何保证安全呢?另外https比较消耗流量,所以可以采用http配合高强度加密的方式来进行网络传输,关于安卓里面使用加解密的知识我会在下一篇文章里编写,我们下周再见。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/210602
推荐阅读
相关标签
  

闽ICP备14008679号