HTTPS
为什么出现 HTTPS
防止 敏感数据 (银行卡号、账号密码等) 被劫持。HTTPS 会对 HTTP 的请求和响应进行加密,因此即使劫持,攻击者看到的也都是一些随机的字符串。
SSL/TLS
双向非对称加密
Client 和 Server 把自己的公钥公开出去,各自保留自己的私钥。
客户端 (PubA、PriA) 给服务器 (PubB、PriB) 发送消息的过程如下:
// 私钥签名,再用服务器的公钥加密
let sign = PriA(msg)
let encryption = PubB(sign)
Client --encryption--> Server
服务器接受客户端消息的过程:
// 私钥解密,再用客户端的公钥验证签名
PubA(unEncryption(PriB, encryption), '验证签名')
- 签名和验签:私钥签名,公钥验签,目的防篡改。
- 加密和解密:公钥加密,私钥解密,目的防止第三方拦截偷听。
单向非对称加密
互联网上,只能
Client
验证Server
,因此只有服务器提供一对公钥和私钥即可。
当然,如果是银行网银,那么也有验证每个客户端的合法性,给用户发的 U 盘,就包含了客户端的公钥和私钥对,用的是双向非对称加密。
SSL/TLS 的原理就是,先单向非对称加密,然后再对称加密:
RSA 应用场景
(1)加密
Bob 想要给 Alice 发送 “Hello Alice!” 这个信息:
- 公钥加密
- 私钥解密
(2)数字签名
- 私钥签名
- 公钥验证
如何保证公钥传输不被中间人劫持?
引入中间机构 CA,服务器把公钥发送给客户端的时候,不是直接发送公钥,而是发送公钥对应的证书。
证书是如何来的?
客户端如何验证服务器的证书?
用 CA 给的公钥 (公钥在网络上,谁都可以知道),解密证书的签名,解密成功,Hash 对的上,就证明公钥没有问题。
如何证明 CA 也是真的 ?
给 CA 颁发证书。谁颁发,CA 的上一级。
HTTPS 握手
握手图二:
握手图三:
Client Hello
在 TCP 建立连接之后,浏览器会首先发一个“Client Hello”消息,也就是跟服务器“打招呼”。里面有客户端的版本号、支持的密码套件,还有一个随机数(Client Random),用于后续生成会话密钥。
Handshake Protocol: Client Hello
Version: TLS 1.2 (0x0303)
Random: 1cbf803321fd2623408dfe…
Cipher Suites (17 suites)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
这个的意思就是:“我这边有这些这些信息,你看看哪些是能用的,关键的随机数可得留着。”
Server Hello
作为“礼尚往来”,服务器收到“Client Hello”后,会返回一个“Server Hello”消息。把版本号对一下,也给出一个随机数(Server Random),然后从客户端的列表里选一个作为本次通信使用的密码套件,在这里它选择了“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”。
Handshake Protocol: Server Hello
Version: TLS 1.2 (0x0303)
Random: 0e6320f21bae50842e96…
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
这个的意思就是:“版本号对上了,可以加密,你的密码套件挺多,我选一个最合适的吧,用椭圆曲线加 RSA、AES、SHA384。我也给你一个随机数,你也得留着。”
Server Certificate
然后,服务器为了证明自己的身份,就把证书也发给了客户端(Server Certificate)。
Server Key Exchange
接下来是一个关键的操作,因为服务器选择了 ECDHE 算法,所以它会在证书后发送“Server Key Exchange”消息,里面是椭圆曲线的公钥(Server Params),用来实现密钥交换算法,再加上自己的私钥签名认证。
Handshake Protocol: Server Key Exchange
EC Diffie-Hellman Server Params
Curve Type: named_curve (0x03)
Named Curve: x25519 (0x001d)
Pubkey: 3b39deaf00217894e...
Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
Signature: 37141adac38ea4...
这相当于说:“刚才我选的密码套件有点复杂,所以再给你个算法的参数,和刚才的随机数一样有用,别丢了。为了防止别人冒充,我又盖了个章。”
Server Hello Done
之后是“Server Hello Done”消息,服务器说:“我的信息就是这些,打招呼完毕。”
这样第一个消息往返就结束了(两个 TCP 包),结果是客户端和服务器通过明文共享了三个信息:Client Random、Server Random 和 Server Params。
客户端这时也拿到了服务器的证书,那这个证书是不是真实有效的呢?
这就要开始走证书链逐级验证,确认证书的真实性,再用证书公钥验证签名,就确认了服务器的身份:“刚才跟我打招呼的不是骗子,可以接着往下走。”
Client Key Exchange
然后,客户端按照密码套件的要求,也生成一个椭圆曲线的公钥(Client Params),用“Client Key Exchange”消息发给服务器。
Handshake Protocol: Client Key Exchange
EC Diffie-Hellman Client Params
Pubkey: 8c674d0e08dc27b5eaa…
现在客户端和服务器手里都拿到了密钥交换算法的两个参数(Client Params、Server Params),就用 ECDHE 算法一阵算,算出了一个新的东西,叫“Pre-Master”,其实也是一个随机数。
至于具体的计算原理和过程,因为太复杂就不细说了,但算法可以保证即使黑客截获了之前的参数,也是绝对算不出这个随机数的。
现在客户端和服务器手里有了三个随机数:Client Random、Server Random 和 Pre-Master。用这三个作为原始材料,就可以生成用于加密会话的主密钥,叫“Master Secret”。而黑客因为拿不到“Pre-Master”,所以也就得不到主密钥。
为什么非得这么麻烦,非要三个随机数呢?
这就必须说 TLS 的设计者考虑得非常周到了,他们不信任客户端或服务器伪随机数的可靠性,为了保证真正的“完全随机”“不可预测”,把三个不可靠的随机数混合起来,那么“随机”的程度就非常高了,足够让黑客难以猜测。
你一定很想知道“Master Secret”究竟是怎么算出来的吧,贴一下 RFC 里的公式:
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)
这里的“PRF”就是伪随机数函数,它基于密码套件里的最后一个参数,比如这次的 SHA384,通过摘要算法来再一次强化“Master Secret”的随机性。
主密钥有 48 字节,但它也不是最终用于通信的会话密钥,还会再用 PRF 扩展出更多的密钥,比如客户端发送用的会话密钥(client_write_key)、服务器发送用的会话密钥(server_write_key)等等,避免只用一个密钥带来的安全隐患。
Change Cipher Spec
有了主密钥和派生的会话密钥,握手就快结束了。客户端发一个“Change Cipher Spec”,然后再发一个“Finished”消息,把之前所有发送的数据做个摘要,再加密一下,让服务器做个验证。
意思就是告诉服务器:“后面都改用对称算法加密通信了啊,用的就是打招呼时说的 AES,加密对不对还得你测一下。”
服务器也是同样的操作,发“Change Cipher Spec”和“Finished”消息,双方都验证加密解密 OK,握手正式结束,后面就收发被加密的 HTTP 请求和响应了。
CA 证书验证过程
抓包可以抓到吗
TLS 握手的前几个消息都是明文的,能够在 Wireshark 里直接看。但只要出现了“Change Cipher Spec”,后面的数据就都是密文了,看到的也就会是乱码,不知道究竟是什么东西。