运营商 Wi-Fi 是一种自动连接功能(使用加密的 IMSI),在 Android 9 及更高版本中可用,允许设备自动连接到运营商实施的 Wi-Fi 网络。在高拥堵区域或蜂窝网络覆盖率极低的区域(例如体育场或地下火车站),运营商 Wi-Fi 可用于改善用户的连接体验并分流流量。
具有运营商 Wi-Fi 功能的设备会自动连接到配置的运营商 Wi-Fi 网络(具有公钥证书的网络)。当用户手动断开与运营商 Wi-Fi 网络的连接时,该网络将被列入黑名单 24 小时(无自动连接)。用户可以随时手动连接到已列入黑名单的网络。
实现
设备制造商和运营商必须执行以下操作才能实施运营商 Wi-Fi。
制造商
对于运行 Android 11 及更高版本的设备,请使用 Wi-Fi 建议 API 为每个运营商添加 Wi-Fi 配置文件。
对于运行 Android 10 或更低版本的设备,请通过在运营商配置管理器中为每个运营商配置 carrier_wifi_string_array
参数来添加 Wi-Fi 配置文件。
carrier_wifi_string_array
:一个字符串数组,其中每个字符串条目是以逗号分隔的 Base64 编码的 Wi-Fi SSID 和 EAP 类型,其中 EAP 类型是一个整数(请参阅可扩展身份验证协议 (EAP) 注册表)。例如,以下配置适用于使用 EAP-AKA 的SOME_SSID_NAME 和使用 EAP-SIM 的 Some_Other_SSIDconfig { key: "carrier_wifi_string_array" text_array { item: "U09NRV9TU0lEX05BTUUK,23" item: "U29tZV9PdGhlcl9TU0lECg==,18" } }
在运营商配置管理器中,为每个运营商配置以下参数
imsi_key_availability_int
:标识用于 IMSI 加密的密钥是否可用于 WLAN(位 1 已设置)、EPDG(位 0 已设置)或两者(位 0 和位 1 都已设置)。例如,以下配置表明 IMSI 加密可用于 WLAN,但不可用于 EPDGconfig { key: "imsi_key_availability_int" int_value: 2 }
imsi_key_download_url_string
:用于下载包含运营商公钥的 proto 的 URL,该公钥用于 IMSI 加密。例如,以下配置提供了一个特定的 URLconfig { key: "imsi_key_download_url_string" text_value: "https://www.some_company_name.com:5555/some_directory_name/some_filename.json" }
allow_metered_network_for_cert_download_bool
:一个标志,指示是否允许通过按流量计费的网络(蜂窝网络)下载运营商的公钥。如果未设置此标志,则没有 Wi-Fi 连接的新设备将无法连接到运营商 Wi-Fi 网络,因为它将不允许下载密钥。config { key: "allow_metered_network_for_cert_download_bool" bool_value: true }
运营商
要实施运营商 Wi-Fi,运营商必须启用 IMSI 隐私保护并提供公钥。
IMSI 隐私保护
Android 使用公钥加密技术保护订阅者永久身份 (IMSI) 的机密性。Android 实施了无线宽带联盟 (WBA) 关于 Wi-Fi IMSI 隐私保护的规范。当连接启用 IMSI 隐私保护时,永久订阅者身份不会在空中以明文形式传输。
永久身份加密
加密的永久身份的格式如下
- 永久身份的格式为
<EAP-Method><IMSI>@<NAI realm>
。 - EAP-Method 前缀是一个单字节,用于定义用于身份验证的 EAP 方法
0
:EAP-AKA1
:EAP-SIM6
:EAP-AKA'
- NAI realm 格式为
wlan.mncXXX.mccYYY.3gppnetwork.org
,其中XXX
替换为 SIM 卡的移动网络代码 (MNC),YYY
替换为移动国家代码 (MCC)。 - 永久身份使用运营商提供的 RSA 公钥进行加密。公钥包含在 X.509 证书中。
- 加密方案是 RSAES-OAEP,使用 SHA-256 作为加密哈希函数。此加密方案保证每次使用该方案时都生成唯一的密文,从而避免了另一个可跟踪的持久身份。
- RSA 密钥长度为 2048 位。
- 加密缓冲区为 256 字节。
- 密文使用 Base64 进行编码。
- 输出的加密永久身份长度为 344 字节。
Encrypted Permanent Identity = Base64(RSAES-OAEP-SHA-256(<EAP-Method><IMSI>@<NAI Realm>))
密钥标识符
密钥标识符是一个可选的属性值对,运营商将其附加到证书,以便服务器在身份验证期间找到正确的私钥。密钥标识符的一个示例是 CertificateSerialNumber=123456
。如果提供了密钥标识符,则它将以明文形式作为身份验证过程的一部分发送。
对基于 SIM 的 EAP 身份验证方法的修改
当连接上启用 IMSI 隐私保护时,系统在收到 EAP-Request/Identity
时不会发送永久身份,而是以匿名登录响应
SERVER: EAP-Request/Identity
UE: EAP-Response/Identity AT_IDENTITY=<prefix>|anonymous@<NAI Realm>
<prefix>
是可选的。如果 enable_eap_method_prefix_bool
运营商配置设置为 true
,则身份(在 anonymous
之前)的第一个字符会通知服务器在 EAP 交换开始之前使用的 EAP 方法类型。
0
:EAP-AKA1
:EAP-SIM6
:EAP-AKA'
如果运营商配置设置为 false
,则消息中不包含此前缀。
作为响应,服务器发送 EAP-Request/AKA-Identity
消息,系统以以下格式响应
SERVER: EAP-Request/AKA-Identity AT_ANY_ID_REQ
UE: EAP-Response/AKA-Identity AT_IDENTITY=<prefix>|<Encrypted Permanent Identity>|","|"<attribute>=<value>"
身份的第一个字符通知服务器使用了加密身份,或者配置的 EAP 方法类型
\0
:加密的永久身份0
:EAP-AKA1
:EAP-SIM6
:EAP-AKA'
密钥标识符属性值对是可选的,如果未使用,则不会附加到加密的永久身份的末尾。
此时,服务器从密钥标识符(如果提供)中找到私钥,使用运营商私钥解密加密的身份,并继续正常的 EAP 流程。
身份验证成功后,服务器可以提供快速重新身份验证身份或临时身份(假名),该身份用于后续连接。如果服务器未提供临时身份,则系统会在后续连接中发送加密的身份。
运营商证书检索、过期和吊销
如果系统中未安装证书,则系统使用 imsi_key_download_url_string
运营商配置中提供的 URL,使用 HTTP GET 方法下载证书。仅当 allow_metered_network_for_cert_download_bool
运营商配置设置为 true
时,系统才使用蜂窝数据。否则,系统仅在 Wi-Fi 连接可用时下载证书。
证书过期由系统强制执行。系统会在证书到期日期前 21 天开始尝试续订证书,并使用相同的 URL 下载新证书。
如果服务器无法解密加密的身份,则服务器会发送 EAP-Request/AKA-Notification
消息,其中 AT_NOTIFICATION
代码为 General Failure
(16384),以终止 EAP 交换。
如果证书被吊销或过期,则服务器会发送 EAP-Request/AKA-Notification
消息,其中 AT_NOTIFICATION
代码为 Certificate Replacement Required
(16385),以终止 EAP 交换。作为响应,系统会应用内部启发式方法来确定是否删除证书并尝试从同一 URL 下载新证书。
提供公钥
提供指向服务器的公共 URL,最好使用基于 TLS 的 HTTP,该服务器托管运营商的证书,其中
- 公钥和到期日期可以从证书中提取。
来自服务器的信息采用以下 JSON 格式
Property: key-identifier Type: String Encoding: UTF-8 Description: Specifies an identifier that the carrier would like to attach to the certificate. Optional: Yes Property: certificate Property alternative name: public-key Type: String Encoding: Base64 Description: The content of the carrier's X.509 certificate. Optional: No Property: key-type Type: String Encoding: UTF-8 Description: Specifies the module that will use the key. The value for type must be either WLAN or EPDG. Optional: Yes. If the key-type property isn't included, then its value defaults to WLAN.
以下是公钥的示例。
{ "carrier-keys" : [ { "key-identifier" : "CertificateSerialNumber=5xxe06d4", "public-key" : "-----BEGIN CERTIFICATE-----\r\nTIIDRTCCAi2gAwIBAgIEVR4G1DANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJVUzELMAkGA1UE\r\nCBMCTkExCzAJBgNVBAcTAk5BMQswCQYDVQQKEwJOQTELMAkGA1UECxMCTkExEDAOBgNVBAMTB1Rl\r\nc3RiT6N1/w==\r\n-----END CERTIFICATE-----" } ] }