(Note) 浅析Https

Thanks

HTTPS理论基础及其在Android中的最佳实践
聊聊HTTPS和SSL/TLS协议

理解HTTPS

HTTPS概述

HTTPS是建立在HTTP的基础上,Http是一个网络协议,用于传输内容,我们知道HTTP的通信过程大致如下:

8.png

Http基于4层网络模型:

    ┌────------────┐┌─┬─┬─-┬─┬─-┬─┬─-┬─┬─-┬─┬─-┐
  │        ││D│F│W│F│H│G│T│I│S│U│ │
  │        ││N│I│H│T│T│O│E│R│M│S│其│
  │第四层,应用层 ││S│N│O│P│T│P│L│C│T│E│ │
  │        ││ │G│I│ │P│H│N│ │P│N│ │
  │        ││ │E│S│ │ │E│E│ │ │E│它│
  │        ││ │R│ │ │ │R│T│ │ │T│ │
  └───────------─┘└─┴─┴─-┴─┴─-┴─┴─-┴─┴─-┴─┴-─┘
  ┌───────-----─┐┌─────────-------┬──--------─────────┐
  │第三层,传输层 ││   TCP   │    UDP    │
  └───────-----─┘└────────-------─┴──────────--------─┘
  ┌───────-----─┐┌───----──┬───---─┬────────-------──┐
  │        ││     │ICMP│          │
  │第二层,网络层 ││     └──---──┘          │
  │        ││       IP            │
  └────────-----┘└────────────────────-------------─-┘
  ┌────────-----┐┌─────────-------┬──────--------─────┐
  │第一层,网络接口││ARP/RARP │    其它     │
  └────────------┘└─────────------┴─────--------──────┘
       TCP/IP四层参考模型

从层级上我们可以发现,应用层产生的数据,直接经由传输层直接传输,存在一个很大的问题,安全性。原本的Http就是传输明文的,数据一经拦截就很容易被别人盗取信息。那怎么解决呢?HTTPS的方法是,对传输的数据进行加密,在应用层和传输层的中间加一层,S层: SSL/TLS,Secure Sockets Layer 安全套接层 / Transport Layer Security 传输层安全协议,这一层使用的主要是 SSL/TLS 技术,这两个协议其实就是安全加密算法的不同:

    ┌────------────┐┌─┬─┬─-┬─┬─-┬─┬─-┬─┬─-┬─┬─-┐
  │        ││D│F│W│F│H│G│T│I│S│U│ │
  │        ││N│I│H│T│T│O│E│R│M│S│其│
  │第四层,应用层 ││S│N│O│P│T│P│L│C│T│E│ │
  │        ││ │G│I│ │P│H│N│ │P│N│ │
  │        ││ │E│S│ │ │E│E│ │ │E│它│
  │        ││ │R│ │ │ │R│T│ │ │T│ │
  └───────------─┘└─┴─┴─-┴─┴─-┴─┴─-┴─┴─-┴─┴-─┘

              ┌───────-----─┐
            │SSL/TLS     │       
            └───────-----─┘

  ┌───────-----─┐┌─────────-------┬──--------─────────┐
  │第三层,传输层 ││   TCP   │    UDP    │
  └───────-----─┘└────────-------─┴──────────--------─┘
  ┌───────-----─┐┌───----──┬───---─┬────────-------──┐
  │        ││     │ICMP│          │
  │第二层,网络层 ││     └──---──┘          │
  │        ││       IP            │
  └────────-----┘└────────────────────-------------─-┘
  ┌────────-----┐┌─────────-------┬──────--------─────┐
  │第一层,网络接口││ARP/RARP │    其它     │
  └────────------┘└─────────------┴─────--------──────┘
       TCP/IP四层参考模型
对称加密算法

既然加密,那怎么一个规则呢?加密,在我们的理解是这样的:

9.png

用一个密码/密钥,用某种算法对明文进行加密,得到密文,解密也容易:

10.png

像上面这样,用同一个密码/密钥进行加密和解密的,就是对称加密算法,因为其密码/密钥是一样的,所以名曰为对称。但如果HTTPS中使用这种算法的话,就需要通信双方知道密钥,这样就必须在通信的时候,先把密钥发给对方,但这样,黑客若截获这密码,等于没用。

非对称加密算法

“非对称加密技术”,意思就是说:“加密”和“解密”使用不同的密钥。其基本原理,就是大数的因式分解。这里就不展开了,密码学的东西,很神奇。因为有两个不同的密钥,我们命名为公钥和私钥,公钥是可以对外公开的,私钥是自己保管的,用公钥或私钥中的任何一个进行加密,用另一个进行解密。 加密和解密就变成这样:

明文 + 加密算法 + 公钥 => 密文, 
密文 + 解密算法 + 私钥 => 明文 
11.png

或者是这样:

明文 + 加密算法 + 私钥 => 密文, 
密文 + 解密算法 + 公钥 => 明文 

总结来说,两个密钥皆可加密,加密后只有由另一个密钥解密。

HTTPS中加密算法

非对称加密很棒,但是呢,效率不高,速度慢,对称加密虽然安全性不高,但是效率高啊。怎么选择呢?小孩子才做选择,大人全都要。HTTPS结合了两种加密算法的优势,加密解密变成这样:

13.png

这个过程涉及到四个密钥:私钥,公钥,对称加密的Key(这里称为KeyX),公钥加密KeyX后的KeyY。首先客户端会得到服务器发来的公钥,然后,客户端会随机生成一个对称加密用的Key,这里称为KeyX,用KeyX对我们要传输的明文进行对称加密,因为明文可能很多,所以这里用对称加密,就效率高很多,得到对称加密的密文,然后用公钥对KeyX进行非对此加密得到KeyY,然后一并传输密文和KeyY给服务器。想要解释密文,就得先得到KeyX,想要得到KeyX,就得有密钥,完美。所以只能拥有密钥的服务器能得到明文。

具体的HTTPS请求实际可细分为一下步骤:(摘自大神博客)

  1. 客户端向服务器发起HTTPS请求,连接到服务器的443端口。
  2. 服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的,服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人。
  3. 服务器将自己的公钥发送给客户端。
  4. 客户端收到服务器端的公钥之后,会对公钥进行检查,验证其合法性。如果发现发现公钥有问题,那么HTTPS传输就无法继续。严格的说,这里应该是验证服务器发送的数字证书的合法性。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。
  5. 客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。
  6. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
  7. 然后服务器将加密后的密文发送给客户端。
  8. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。

数字证书

我们拿到的公钥怎么确保真的是服务器的公钥呢?黑客有可能中途篡改公钥,将其改成黑客自己的。摘个例子:

假设一个镇里面有两个人A和B,A是个富豪,B想向A借钱,但是A和B不熟,怕B借了钱之后不还。这时候B找到了镇长,镇长给B作担保,告诉A说:“B人品不错,不会欠钱不还的,你就放心借给他吧。” A听了这话后,心里想:“镇长是全镇最德高望重的了,他说B没问题的话那就没事了,我就放心了”。 于是A相信B的为人,把钱借给了B。

类似地,公钥需要一个担保人:证书认证中心(Certificate Authority),简称CA。CA本身有一对公钥和私钥,CA会用CA自己的私钥对要进行认证的公钥进行非对称加密,此处待认证的公钥就相当于是明文,加密完之后,得到的密文再加上证书的过期时间、颁发给、颁发者等信息,就组成了数字证书。

Android-Retrofit 配置证书 访问HTTPS

  • 首先,如果证书是由CA颁发的或者是CA授权的机构颁发的,直接可以使用,因为Android有CA的根证书,所以默认支持

  • 如果是阿里云申请的证书,可能会有一个 pem 证书,需要先转换格式 cer 参考:https://blog.csdn.net/qq_33315813/article/details/73532846,
    关于格式的说明,可以参考 https://blog.csdn.net/qq_30698633/article/details/77895151

  • retrofit的写法参考于:https://blog.csdn.net/qq_20521573/article/details/79233793
    https://blog.csdn.net/lmj623565791/article/details/48129405

先把转换后的cer文件放到 R.raw.

private SSLContext initCertificates(InputStream... certificates) {
    try {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);
        int index = 0;
        for (InputStream certificate : certificates) {
            String certificateAlias = Integer.toString(index++);
            keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
            try {
                if (certificate != null)
                    certificate.close();
            } catch (IOException ignored) { }
        }
        SSLContext sslContext = SSLContext.getInstance("TLS");
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        return sslContext;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
public void init(Context context) {
    InputStream inputStream = context.getResources().openRawResource(R.raw.https);
    sslContext = initCertificates(inputStream);
}

调用:

private HttpsContract getBaseHttpProtocol(SSLContext sslContext, String baseServer) {
    OkHttpClient client=new OkHttpClient.Builder()
            .sslSocketFactory(sslContext.getSocketFactory())
            .hostnameVerifier(new SafeHostnameVerifier())
            .build();
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseServer)
            .addConverterFactory(ScalarsConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(client)
            .build();
    return retrofit.create(HttpsContract.class);
}

你可能感兴趣的:((Note) 浅析Https)