WiFi 数据加密的形式

本文在于阐述我对 WiFi 网络中各站点之间加密方式的理解。

提问

此前,我有过这样一个疑惑:

wifi 的数据传输形式为广播,那么考虑一下,当 A 站发送数据给 B 站时

  1. 如果 A 站和 B 站之间信号不直接可达,那么数据将会先由 A 站流往 AP(接入点/基站/路由器),再由 AP 流往 B 站,即靠 AP 做转发
  2. 如果 A 站和 B 站之间信号直接可达,数据广播出去后,AP 和 B 站都会收到这个数据,那么 B 站是直接接受还是抛弃?又由于 AP 并不知道 B 站是否已经接收,所以会坚持转发一次,如果 B 站直接接受了,岂不是会导致数据无条件重发?

在理解了 wifi 的数据加密形式后,疑惑也被顺便解开了。

PS:说 wifi 其实不严谨,802.11 会更规范一点,不过为了方便理解,直接说 wifi 也无妨,因为不少读者可能不熟悉 802.11 和 wifi 的关系。

共识

行文开始前先做一些概念约束以及一些我的个人观点。

模式限定

探讨范围限定在 AP 模式,也就是最流行的 wifi 接入模式,不涉及 Ad-hoc 模式,简要的描述就是:

  • 有一个无线设备工作在 AP 模式,承担基站的角色(路由器)
  • 其他所有的设备全部和基站建立无线链路(手机、平板、笔记本……接入 wifi)
  • 所有站点之间的数据通信均通过基站进行转发,类似于 C/S 模式
  • 只要基站挂掉,所有站点之间将无法互相通信

通信数据安全

一般而言,通信数据的加密手段不同于数据存储。对于数据存储,通常采用对称加密,且只用一把密钥;而通信数据的做法大致是这样的:

  • 通信双方共享一把长期的主密钥 K
  • 通信双方基于 K 做一次安全握手,派生出一把当前会话使用的密钥 k
  • 接下来的通信均使用 k 加密
  • 每隔一定时间,双方重新派生出一把新的会话密钥 k2,替换 k,继续后续通信

这样做的好处有两点:

  • 密钥分离:会话密钥 k 即便泄露也不会牵连主密钥 K,反过来主密钥被攻破之前的历史会话也不会因为一把 k 的泄露而全军覆没
  • 密钥新鲜度:同一把密钥加密的样本越多,越有利于密码分析。即便算法本身没被破,定期轮换密钥也能提高整体的安全性

广播报文与广播链路

在广播式链路上发送广播报文具有得天独厚的优势:

  • 古老的集线器也是广播式链路,某站点要发送一个广播报文,集线器不需要任何处理,只需要把广播报文当成普通报文广播即可
  • 交换机将集线器的广播式链路改成了点对点链路,某站点要发送一个广播报文,交换机只能将该报文从每一个端口发送一次
  • wifi 使用无线电通讯,自然也是广播链路,因此,某站点要发送一个广播报文,AP 只需要发送一次数据即可,无需将广播报文挨个站点发送一次

流程

大致流程如下(忽略广播):

  1. AP 和站点之间共享相同的初始密钥 PMK(Pairwise Master Key),关于 PMK 的来源后文会给出
  2. 每当有站点请求接入 AP,AP 和站点之间就会通过 PMK 生成出一把正式传输数据用到的密钥 PTK(Pairwise Transient Key)
  3. AP 和站点之间的通讯全部使用 PTK 加密

如果稍加留意,你会发现,每个站点和 AP 之间都维护着一个 PTK,但是站点和站点之间是不共享 PTK 的,站点 A 和 AP 之间的通讯使用 PTKa,站点 B 和 AP 之间的通讯使用 PTKb。

因此,站点 A 是没有能力和站点 B 直接通讯的,必须经由 AP 做数据转发,也就是说,即使站点 B 接收到了站点 A 发出的帧中白纸黑字写着”接收方为站点 B”,站点 B 也不得不抛弃,因为帧中的载荷数据是使用 PTKa 加密的,而站点 B 并不知道 PTKa,只能等待 AP 解析此帧,并换用 PTKb 加密后重新发送一个新帧,站点 B 才可以看见来自站点 A 的数据。

换言之,PTK 的方式保证了站点和站点之间在链路上是 “隔离” 的,任何站点都无法嗅探到不是发送给它的数据。

接下来谈谈数据的广播,如果站点 A 需要发送 IP 广播,单借助 PTK,也是可以做到的:站点 A 将广播包发送给 AP,AP 再将广播包重新编码逐个发给所有接入的站点,但这种做法太愚蠢,现实的流程如下:

  1. AP 启动后生成一个随机初始密钥 GMK(Group Master Key)
  2. AP 使用 GMK 通过某种算法,衍生出一个用于加密广播/组播数据的密钥 GTK
  3. 每个接入 AP 的站点在得到了 PTK 后,AP 将 GTK 使用 PTK 加密后发送给站点

换言之,所有站点都持有相同的 GTK,那么站点发送 IP 广播的流程究竟是怎么样的呢?最容易想到的有两种。

第一种,站点直接通过 GTK 进行广播:

  1. 站点 A 使用 GTK 加密广播包并发送
  2. 站点 B 接收到从站点 A 发出的广播包
  3. 站点 C 离 A 较远,没能收到从站点 A 发出的广播包
  4. AP 收到了从站点 A 发出的广播包,然后做一次接力转发
  5. 所有站点接收到从 AP 转发的广播包

第二种,由 AP 通过 GTK 进行广播:

  1. 站点 A 使用 PTKa 加密广播包并发送
  2. AP 接收到从站点 A 发出的广播包
  3. AP 解码接收到的广播包,并使用 GTK 加密该广播包,再接力转发出去
  4. 所有站点接收到该 AP 转发的广播包

不难看出,第二种方式是最优的,因为模式简单且和单播方式统一,而且第一种方式会造成站点重复接收广播包。事实上也的确采用的第二种方式。所以,GTK 对于站点来说,只用于解密,对于 AP 来说,只用于加密。

总结:wifi 网络内的所有站点间的所有通信都必须交给 AP 转发,站点和站点之间不存在任何直接通信行为。

关于 PMK

PMK 是怎么来的呢?

简单的说,接入 wifi 时需要你填的那个密码被称之为 PSK(Pre-Shared Key),将 PSK 使用一套算法进行一次转换,就是所谓的 PMK 了。

当然,PMK 不仅仅可以通过 PSK 得到,如果 wifi 使用 802.1X 的企业级证书认证方式(民用 wifi 很少支持这种认证方式),那么,PMK 就是标准的非对称密钥生成的对称密钥。

关于 GMK

此前我认为:GMK 可能只是为了和 PMK 概念对称,实际使用中,感觉完全没有存在的必要,AP 每次都生成一个随机串作为 GTK 就可以了。

如今我认为:GMK 的存在是为了方便 GTK 的周期性轮换——比如有站点退出网络时,应当更换 GTK 让退出的站点无法继续解密后续的广播数据,由 GMK 经过派生算出新 GTK,比每次都重新生成随机串要更轻量、可控。GMK 是某个 AP 自己内部的事,并不在多个 AP 之间共享。