非对称加密
- 公钥是公开的。私钥是私有的。公钥加密只能用私钥解密,私钥加密只能用公钥解密。
- 其他人使用我的公钥加密,自己用私钥解密,这叫“加密”。
- 自己用私钥加密,其他人使用我的公钥解密,这叫“签名”。
- 在 3 的基础上,如果这对非对称密钥是由权威的“证书中心”颁发的,就叫“数字证书”。
数字证书
- 证书机构对该证书的数字签名(Signature Algorithm)
- 有效时间(Validity)
- 证书持有人 的信息(包含网址域名)
- 公钥(Public Key)
- 其他
为什么说 HTTPS 是安全可靠的?
https 通信机制
TLS - RSA
都有Random_C,Random_S,Pre_Random
其中 Pre_Random 使用公钥加密,外界无法得知; Note over C,S: Client和Server各自使用Random_C,Random_S,
Pre_Random生成相同的SessionKey
TLS - ECDHE
-
DH(Diffie-Hellman) 密钥交换算法
随机数 a, b 都只存在本地
sequenceDiagram participant A as Alice participant B as Bob A->>A: 生成 a, g, p 然后计算 A = g^a mod p A->>B: g, p, A B->>B: 生成随机数 b,
计算 B = g^b mod p B->>A: B A->>A: K = B^a mod p B->>B: K = A^b mod p -
使用 ECDHE 的 TLS 流程
向前安全(前向保密性):私钥被泄漏了,历史数据是否安全
sequenceDiagram participant C as Client participant S as Server C->>S: Random_C,TLS版本,加密套件 S->>C: Random_S, 加密方式,公钥 S->>C: DH_S_Params, 加签 C->>C: 验证公钥及域名;生成 Pre_Random C->>S: DH_C_Params Note over C,S: 至此Client和Server
都有Random_C,
Random_S,DH_S_Params,DH_C_Params Note over C,S: Client和Server各自使用
上面的数据生成相同的SessionKey
TLSv1.3
-
流程
sequenceDiagram participant C as Client participant S as Server C->>S: Random_C,Params_C,TLS版本,加密套件 S->>C: Random_S,Params_S,加密方式,公钥 C->>C: 验证公钥及域名,然后生成 Pre_Random Note over C,S: 至此Client和Server
都有Random_C,Random_S,
DH_S_Params,DH_C_Params Note over C,S: Client和Server各自使用
上面的数据生成相同的SessionKey -
如何实现 0-RTT
使用 PSK - pre_shared_key
-
TLS 握手结束后,服务器可以发送一个NST(new_session_ticket)的报文给客户端,该报文中记录PSK的值、名字和有效期等信息,双方下一次建立连接可以使用该PSK值作为初始密钥材料。
-
因为PSK是从以前建立的安全信道中获得的,只要证明了双方都持有相同的PSK,不再需要证书认证,就可以证明双方的身份,因此,PSK也是一种身份认证机制。
-
缺陷:0-RTT的实现有一定的安全缺陷,自身没有抗重放攻击的机制
-
中间人攻击:窃听数据
因为 随机数3 是使用服务器的公钥加密的,所以中间人得不到 随机数3 的值,因而不知道密钥,中间人窃听失败。
中间人攻击:代理( 欺上瞒下 )
中间人代理的方式,如果有中间人代理了你的服务器,但它其实并没有服务器的数字证书,所以它的证书在客户端这边是校验不通过的:要不就是合法性校验不过(非权威机构颁布),要不就是证书中的域名校验不过。(它不可能从权威机构那里拿到你的域名的数字证书)
总结下:HTTPS 能保证服务器到客户端之间数据的安全性,可靠性。你不用担心你的数据被窃听或者被中间人改过了。
HTTPS 双向认证
-
以上的 HTTPS 只是单向的验证。仔细想想,只有客户端验证了数据的合法性,而服务端并没有验证客户端数据的合法性,造成了当“客户端沦陷”的时候,服务端完全不知情的状况。
-
Https 双向认证的基本原理和上文所讲的单向认证差不多,区别在于:客户端也有数字证书,在建立 https 连接的时候,客户端会把自己的证书以及公钥发给服务器。这样就能保证客户端发给服务端的消息不会被中间人篡改。
HTTPS 真的就安全了么?
- 从普通用户角度来说,HTTPS 的确已经够了。
- 但对于企业来说,除了服务普通用户之外,还得提防黑客的攻击,HTTPS 能保障你的安全么?显然不会:
- 黑客客户端可以安装信任自定义证书,那么你和客户端之间就不是直接连接,而是被“代理”了,而在代理服务器上,你的所有数据都是透明的。Fiddler, Charles, whistle 都是这么实现的。
- 某些浏览器支持将 TLS 会话中使用的对称密钥保存在外部文件中,这相当于密钥泄露了。所以黑客完全可以通过这个密钥来解密窃听来的数据。Wireshark 就是这么搞的。可以看看:三种解密 HTTPS 流量的方法介绍
- 对于双向认证来说,客户端的“私钥”就安全么?怎么保证不会落入黑客之手?
解决方案
-
证书锁定:在客户端判断证书是否是预设的证书,若不是的话就不信任。
-
在OkHttp 中可以设置:
1 2 3 4 5 6 7 8
val hostName = "www.baidu.com" val pin1 = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" val pin2 = "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" val certificatePinner = CertificatePinner.Builder().add(hostName, pin1, pin2).build(); val okHttpClient = OkHttpClient.Builder() .certificatePinner(certificatePinner) .build()
这样,就在客户端锁定了服务端的证书 sha256 值必须得是
sha256/AAAA.....
才行,其他的证书都不行。以此防止客户端被黑客调试。 -
设置证书锁定后,客户端首先还是会和常规 https 一样在系统根证书中去确认本连接的证书的合法性,校验通过后,再去比对 CertificatePinner 中对应域名的 pin 值是否匹配。匹配后才会放行。
-
弊端:证书锁定限制了服务器更新证书的能力,就像客户端用 IP 地址类似。
-
-
白盒加密:在客户端使用白盒加密对称加密密钥,然后和服务端之间采用对称加密的 http 传输。(如何防止黑客从内存中获取解密后的密钥呢?)
现在安全了么?
道高一尺魔高一丈,黑客如果还要“搞你”,还是有其他方法的,以上方法并没有高枕无忧。
- Frida hook 方案。(需要手机 Root,但对于黑客来说谁没有一只 Root 的手机呢?)
- 各种逆向,重打包方案。