写于:2026 年中 一句话结论:不行。 普通人没有合法、可行的通道走”运营商级 WiFi Calling”。技术上被三重锁死,第一道且最硬的一道就是 eUICC 证书信任链——私有证书行不通,这是硅片级的死结。能做的最远边界是应用层 VoIP over WiFi,那不是 WiFi Calling。
0. 写这篇文章的动机
我最近在做一个 SGP.22 SM-DP+ 的开源实现,仓库叫 sm-dp,目标是把”用户从二维码触发 eSIM Profile 下载”这条链路完整跑通。几个月时间,我把 ES9+ 第一步 initiateAuthentication 写完、容器化、跑通 HTTPS、跑通 nginx 反代、修了一遍又一遍的协议头(大小写不敏感 + 多版本协议号支持),最后 MiniLPA 真的把请求发到我服务器上,sm-dp 返回 HTTP 200 + 标准失败 envelope,错误码 subjectCode 8.8.4 / reasonCode 3.7,信息是:
“The SM-DP+ has no CERT.DPauth.ECDSA signed by a CI Public Key supported by the eUICC”
这一行错误信息不是 bug,不是协议实现不对,不是网络问题。它就是答案:eUICC 里没有能信任我 SM-DP+ 证书的 CI 公钥。
撞墙的位置:eUICC 证书信任链。
我把这条墙讲清楚,再回答”那能不能自建 WiFi Calling”这个问题。
1. 先把两件事分清楚:运营商 WiFi Calling ≠ 应用层 VoIP over WiFi
读者里肯定有人把这两个当回事,所以我必须先拆开:
| 运营商 WiFi Calling(VoWiFi) | 应用层 VoIP over WiFi | |
|---|---|---|
| 信令 | IMS SIP(P-CSCF / S-CSCF / I-CSCF / TAS) | App 自有协议(APNs / Signal Protocol / 自建 WS) |
| 鉴权凭据 | ISIM @ UICC(SIM 卡里那个 SIP Digest-AKA 凭据) | App 账户 / 设备证书 |
| 寻址 | E.164 MSISDN(手机号) | App ID / UUID / 自编号 |
| 接入网 | 不可信 WiFi + IPsec/IKEv2 → ePDG → EPC/5GC → IMS | 任何 IP 承载,裸 socket |
| 通话对象 | 跨运营商经 IPX / IBCF 进 PSTN | 私域;想进 PSTN 需 SIP Trunk |
| 紧急呼叫 | E.112 / E.911 路径(受 PLMN 管制) | 通常无合规紧急呼叫能力 |
| 计费 / 合法监听 | 运营商 CDR + LI 合规 | App 厂商服务器侧,不在运营商 CDR |
| 在手机拨号盘里 | 是,作为运营商主叫 | 不是,仅 App 自己的来电界面 |
运营商WiFi Calling / VoWiFi 是一个 3GPP 规范的术语,本质是”把 IMS 语音搬到不可信 WiFi 上”。它不是”用 WiFi 打电话的 App”。两者在协议层完全平行,互不相通。
读者能做的最远边界——也是我自己正在做的——是右列那一条。那不是 WiFi Calling。这一点必须先说死,不然下面没法讲。
2. 第一道锁(也是最硬的一道):eUICC 证书信任链无法自建,私有证书就是不行
这一节是整篇文章最重要的一节,我会写得很慢。
2.1 我手里这块 eUICC 里到底有什么
我用的是一块 Eastcompeace(东信和平)生产、GSMA SAS 编号 ED-ZI-UP-0826 的消费级商用 eUICC。svn 是 2.2.2,euiccFirmwareVer 4.2.0。我用 MiniLPA 读出 EUICCInfo2,展开 euiccCiPKIdListForVerification:
euiccCiPKIdListForVerification: [
"81370f5125d0b1d408d4c3b232e6d25e795bebfb", // GSM Association - RSP2 Root CI1
]
euiccCiPKIdListForSigning: [
"81370f5125d0b1d408d4c3b232e6d25e795bebfb", // GSM Association - RSP2 Root CI1
]
就一项。不是”列表里主要是 GSMA,但也可以有别人”——是只有一项。
这一项是 GSM Association - RSP2 Root CI1 的公钥指纹。整张芯片一辈子就认这一个 CI。它的公钥在 eUICC 出厂时被一次性烧入 eUICC 内部的 ECASD(Embedded Certificate Authority Security Domain)—— ECASD 是 eUICC 的 JavaCard 安全元件里专门管理证书链的应用。这个烧入是 OTP(一次性可编程)或同等强度的写保护,软件层面改不了。
2.2 我试过”绕过”,结果就是不行
我 OpenSSL 生成了一对 ECDSA P-256 私钥,自签了一张 CI 证书,role OID 用 id-rsp-ci(1.3.6.1.4.1.46898.3)。再用这张自签 CI 给 SM-DP+ 签了一张 CERT.DPauth.ECDSA,role OID 用 id-rsp-dp-authentication。我把 SM-DP+ 跑起来,让 MiniLPA 触发下载。
协议层面一切正常。POST /gsma/rsp2/es9plus/initiateAuthentication 200 OK,所有握手都过,serverCertificate 也下发了。但真正的 Profile 下载在 ES10b 阶段被 eUICC 拒接——eUICC 拿到 serverCertificate 后会沿着 DPauth → CI → GSMA Root CI1 做链式验签,发现”这张 DPauth 是由一张不在我信任集的 CI 签的”,链路到我的自签 CI 时断了,回退到根时对不上,于是拒接。
报错就是上面那个 8.8.4 / 3.7:”无 CI 信任的 ECDSA 证书”。
2.3 一个常见误区:”装个根证书到手机能不能绕过”
有人问过我:”那我把 GSMA Root CI1 的证书装到手机的系统信任根里,不就解决了吗?”
不行。这是一个典型的认知误区,原因有两个:
第一,iOS / Android 的”系统信任根”只管 TLS / HTTPS 客户端校验,跟 eUICC 完全无关。iOS 上你装一个企业 CA 根证书,它影响的是 Safari / Mail / 第三方 App 怎么验 HTTPS 服务器。Android 7+ 默认连用户 CA 都不信,要 root 才能让 App 默认信用户 CA。但 eUICC 是独立的 JavaCard 安全元件,跟主系统隔离,主系统的”信任根”管不到它。eUICC 验签用的是它自己烧死在 ECASD 里的那张 GSMA Root CI1 公钥,只此一份。
第二,eUICC 的 ECASD 没有外部 APDU 写接口。ECASD 是 eUICC OS 厂商(Eastcompeace / Idemia / Thales 等)在工厂模式下用厂商签名固件装入的,运行态下不允许任何 APDU 修改它的信任列表。你能换的只有 Profile(普通应用),碰不到 ECASD。能”修改” ECASD 信任列表的唯一合法途径是 eUICC 固件更新——但固件更新包也得由 EUM(eUICC 厂商)私钥签发,EUM 的根又是 GSMA Root CI。走到哪里都绕回 GSMA。
2.4 那”开发 / 仿真 eUICC”呢?—— 能绕,但用途有限
必须诚实补一刀:“绕”对部分 eUICC 是可以的。少数开发 / 仿真 eUICC 的 ECASD 是”软”的,工厂模式允许加载额外 CI 信任锚:
- osmocom 系列(
swp-euicc等) - 某些 Idemia / Valid 测试卡(需通过厂商渠道申请,明确”测试模式”)
- 纯软件仿真器(如某些 JavaCard 仿真器)
在这些卡上,你可以把自己生成的自签 CI 公钥塞进信任集,eUICC 就会信。但这些卡不是消费级 eUICC,不能用于任何商用场景(运营商商用网不识别它们)。它们的用途是:
- 协议层验证(”我的 SM-DP+ 协议实现对不对”)✓
- 端到端加密路径验证(”签名验签链路通不通”)✓
- 真实商用 Profile 下载 ✗
- 让消费级手机原生电话 App 把你的 SM-DP+ 当运营商 ✗
我手头这块 Eastcompeace 不在列。本文后面所有讨论都以”商用 eUICC”为前提。开发卡的”绕过”是另一篇文章的话题,不能混淆。
2.5 GSMA PKI 的真相
把上面这些事实落到 GSMA PKI 体系里看:
GSMA Root CI(公开信任锚,固化在每张合规 eUICC 的 ECASD)
│
│ 由 GSMA Root CI 私钥签发
▼
Sub-CI(也叫 CI)
│ - EUM 厂商(Eastcompeace / Idemia / Thales / Valid / G+D 等)持有
│ - 这些 CI 的公钥被它们出厂的 eUICC 信任
▼
End-entity 证书:SM-DP+ / SM-DS / EUM
这张图的三个特征决定了”个人不可能”:
Root CI 私钥不卖:GSMA Root CI 私钥物理隔离在 GSMA 金库里,没有任何合法出售渠道。任何说”出售 GSMA Root CI 私钥”的网站都是骗子。
Sub-CI 只发给通过 SAS-SM / SAS-SDS 现场审计的企业:SAS 是 GSMA 的安全认证体系,SM(Subscription Management)针对 SM-DP+ 服务,SDS 针对 SM-DS。审计员是 GSMA 授权的第三方(BSI / atsec / TÜV 等),要查你的:
- HSM(硬件安全模块,FIPS 140-2 Level 3 或同等),必须,不能把 SM-DP+ 私钥存成 PEM 文件塞进容器
- 物理安保(双人控制、门禁、监控)
- 密钥管理与备份方案
- 站点隔离、网络隔离
- 人员背景调查
- 年度复审
审计周期和钱:从启动到拿证 6 到 12 个月,钱 六位数 USD 起步(HSM 几万到十几万 + 审计费 + 持续合规运维人力)。
任何个人 / 小团队,没有这条路。
2.6 结论(这一节最重要的一句话)
eUICC 证书信任链是个人根本无法触碰的硅片级约束,私有证书行不通,这不是软件问题。
我在 sm-dp 里跑通 ES9+ 协议层之后撞上的墙,就是这堵墙。这堵墙在我手里这块 Eastcompeace 芯片上不可逾越,对任何商用 eUICC 都不可逾越,对所有 GSMA 体系内的”自建 SM-DP+“尝试都不可逾越。这是 eSIM 安全模型的核心:如果信任锚能由软件改写,整个体系就崩了。
3. 第二道锁:运营商 IMS 核心网不是你想接就能接
即便你解决了证书链(你做不到,但假设做到了),下一个墙是运营商 IMS 核心网。
运营商 WiFi Calling 的完整链路是:
UE(手机)
│
│ IPsec/IKEv2 隧道(SWu 接口,TS 33.402)
▼
ePDG(运营商侧加密网关,Cisco / Ericsson / Nokia / Mavenir 商用产品)
│
│ GTP / PMIPv6(3GPP TS 23.402 S2b / TS 23.501 非授信接入)
▼
EPC / 5GC(核心网:MME / SGW / PGW / AMF / SMF / UPF)
│
│ SIP(IMS 接入)
▼
IMS 核心:P-CSCF → I-CSCF → S-CSCF → TAS → SBC
│ │
│ │ 媒体:RTP / SRTP(受 QoS 控制)
▼ ▼
HSS / UDR(用户数据库,存 ISIM 凭据)
│
│ 跨运营商经 IBCF 进 IPX → 对端运营商 IMS → PSTN
相关 3GPP 规范锚点:
- TS 23.228(IMS 总体)
- TS 23.402(非授信非 3GPP 接入,ePDG / SWu / S2b)
- TS 23.501 / 23.502(5G 系统架构,非授信接入归入 5GC)
- TS 24.502(非授信接入的会话管理)
- TS 33.402(IKEv2 / IPsec 安全)
IMS 凭据在 ISIM @ UICC——也就是 SIM 卡里的 SIP Digest-AKA 凭据。这意味着”号码”绑在 SIM 卡上,不绑在 App 上。你给朋友发个”我的 WiFi Calling 号码是 13800138000”,那个号码必须在运营商 HSS 里登记,必须有 ISIM 凭据,必须有对应的 SM-DP+ Profile 已下发到某张 UICC。这条链路没有任何一个环节是开放的。
- ePDG 是运营商专有设备
- HSS/HLR 是运营商核心数据库
- IBCF 是运营商间边界
- IPX 是 GSMA 运营的 IP 交换网络(Equinix / Etisalat / Tata 是主要运营商),不接入散户
这是企业基础设施级别的封闭,普通人连”租一个 IMS”都做不到。
4. 第三道锁:iOS / Android WiFi Calling 入口是运营商 entitlement,不是用户开关
假设(不可能的假设)你前两关都过了,你的 SM-DP+ 把 Profile 下发到了某张 eUICC 上,你还需要 iOS / Android 在系统设置里亮出”WiFi 通话”开关。
4.1 iOS
iOS 的 WiFi Calling 开关不是用户能强制打开的。它出现的条件是:
- 系统读到一张经过 Apple 审核的 carrier bundle / IPCC(carrier.plist + 配套 APN、IMS entitlement、号码映射)
- Modem 固件支持 IMS VoWiFi
- eUICC 上有对应运营商下发的 Profile
参考 Apple 官方文档:Use Wi-Fi calling on your iPhone。Apple 维护一个运营商审核流程,运营商必须提交自己的 carrier bundle 经 Apple 审核。散户的 carrier bundle 不会被审核。
4.2 Android
Android 类似但更复杂。Android 11+ 的 IMS API(ImsService / ImsManager)是 AOSP 提供的接口框架,但实现必须由运营商提供:
- 运营商的 IMS 服务二进制(OEM / modem 厂商预装或系统镜像里)
carrier_config.xml/ carrier_settings 配置文件(描述 PLMN、APN、IMS entitlement)- Modem HAL(QMI / RIL / IMS 调制解调器层)支持 IMS
参考 Android 官方文档:Wi-Fi calling。
裸 AOSP / 第三方 ROM 没有这些运营商二进制,永远显示不出 WiFi Calling 开关。LineageOS / GrapheneOS / Pixel Experience 上你都不会看到这个开关。
4.3 平台权力
这一道锁的本质是平台权力——苹果和 Google 决定”哪个运营商能被点亮”。这不是开放接口,不是 SDK 调一调就能开。运营商必须:
- 走 Apple 的 Carrier Bundle 审核流程
- 与 Google 签 carrier entitlement 协议
- 接受苹果 / 谷歌的合规审计
普通开发者、创业公司想绕过去?没门。
5. 应用层 VoIP 进不去 IMS
行,我们退一万步——不追求运营商 WiFi Calling,就做个 App 让你和朋友能语音通话。是不是就行了?
可以。但要清醒地知道,这条路是和 IMS 完全平行的另一条路:
- FaceTime:信令走 APNs,媒体 P2P(ICE/STUN/TURN),完全不进运营商 IMS / ePDG / SIP / ISIM
- WhatsApp / Signal / Telegram:应用层 E2EE(Signal Protocol 等),UDP/TCP/TLS 上的 proprietary 信令,不注册到 IMS,不用 ePDG,不读 ISIM
- 我的
rtpd:自建 WebSocket 信令 + UDP RTP 媒体,phone-android/phone-ios通过 UniFFI 调用rtpd-sdk,跑的是 48 kHz Opus / G.711 PCMU over UDP RTP
这些应用在 App 层面 用 CallKit / VOIP Push 让锁屏体验接近原生电话——iOS 上 App 可以在锁屏弹出来电 UI,点击接听,CallKit 让通话显示在系统的”通话中”界面里。但媒体始终在 App 自己控制的实时连接里,不进运营商核心网,不被运营商 CDR 计费,不走 ePDG,不使用 ISIM 凭据。
GSMA 在 2018 年的一份白皮书里把这类服务统称为 OTT(Over-The-Top)VoIP,明确区别于运营商 VoWiFi:GSMA blog - OTT Calling Protocol Analysis。
互通 PSTN 仍然需要 SIP Trunk + 落地号,回到运营商身份,绕不开。
6. GSMA SGP.22 “999” 是不是个人通道?
不是。这是个常见误解。
GSMA 在 SGP.22 体系里确实给非传统运营商 / 私有网络 / MVNO 保留了一个简化通道,对应 Apple iOS 上的”999 标识”。它的本质是:
- 简化版 SAS-SM 合规(不是免除,仍然要 GSMA 注册 + 当地监管备案)
- iOS PLMN 白名单(”999”是一个 MCC/MNC 范围,由 Apple 审核)
- 走标准 SGP.22 协议(仍然是 GSMA Root CI 签发的 CI 链)
它的目标客户是:
- 企业 / 园区 / 港口 / 矿区 / 校园的私有 5G/LTE 网络运营商
- 有 ICP/ISP 资质的 MVNO
- 政府 / 应急 / 国防通信
不是个人。仍然要法人主体,仍然要 GSMA 注册,仍然要合规审计。它降的是合规门槛,不是消除 GSMA PKI——这点要讲清楚。
7. “那我做私有 IMS 网不就完了?”——是的,企业可行,个人不是合适主体
有些读者看到上面可能会想:”既然问题在 IMS 核心网,那我自建一个 IMS 私有网给园区用不就行了?”
这条路技术上完全可行,且已经有很多商用产品。具体拓扑:
私有基站(small cell / 5G gNB)
│
▼
私有 5GC:AMF / SMF / UPF
│
▼
私有 IMS:CSCF / TAS / SBC / HSS
│
▼
私有 ISIM / USIM 凭据 → 行业终端(防爆手机 / 三防 PDA / 工业模组)
商用方案提供商:Athonet、Celona、CoreNetwork Dynamics、Mavenir、华为、中兴、京信、佰才邦等。
能做什么:
- ✅ 园区内部打通:工人 A 给工人 B 走 IMS VoNR / VoLTE
- ✅ 行业终端之间互联互通
- ✅ 私有网络的 QoS / 紧急呼叫 / 计费 / 调度
不能做什么:
- ✗ 出园区打电话给北京/纽约/伦敦的朋友:仍要公网运营商 SIP Trunk + 落地号
- ✗ 让消费级 iPhone / 普通 Android 原生电话 App 显示为你的私有运营商:需要 Apple / Google 审核 carrier bundle,要 GSMA PKI 颁发合法 SM-DP+ 证书链
- ✗ 在淘宝上卖给个人用户:合规不允许
个人适不适合:
- 50–500 用户的企业场景:可走 Athonet / Celona / Mavenir 这类 SaaS 化方案,半年到一年部署完成,百万级人民币预算
- 个人 / 小作坊:不合适。HSM + 现场审计 + GSMA 注册 + 当地工信部备案(频谱 + 电信业务资质),百万级设备和合规起步
- 不要混淆:私有 IMS 网不是”WiFi Calling 替代品”,它是企业自建专网方案
结论:私有 IMS 是企业选项,不是个人 “WiFi Calling” 的替代品。
8. 我手头这套栈在整张图里是什么位置
让我把我自己跑的东西摆出来,给读者一个具象的”普通人能做的最远边界”:
┌──────────────────────────────────────────────────────────────┐
│ phone-android (Kotlin / Compose, 48 kHz Opus over UDP RTP) │
│ phone-ios (SwiftUI, G.711 PCMU over UDP RTP) │
│ │ │
│ │ UniFFI bindings │
│ ▼ │
│ rtpd-sdk (Rust) │
│ │ │
│ │ WebSocket signaling + UDP RTP │
│ ▼ │
│ rtpd (Rust) ──── PostgreSQL(短号注册、状态) │
└──────────────────────────────────────────────────────────────┘
完全在 IP 层之上,不进运营商 IMS / ePDG / ISIM
类比:FaceTime / WhatsApp / Signal / 自建 SIP PBX
┌──────────────────────────────────────────────────────────────┐
│ MiniLPA (Kotlin 桌面 GUI) │
│ │ │
│ │ GSMA RSP 流程 │
│ ▼ │
│ sm-dp (Rust / Rocket, SGP.22 SM-DP+) │
│ - ES9+ /gsma/rsp2/es9plus/initiateAuthentication ✓ │
│ - ES9+ /gsma/rsp2/es9plus/authenticateClient (待实现) │
│ - ES9+ /gsma/rsp2/es9plus/getBoundProfilePackage (待实现) │
│ - 卡在:ProfilePackagingProvider = UnavailableProvider │
│ - 原因:没有 GSMA 签发的 CERT.DPauth.ECDSA │
└──────────────────────────────────────────────────────────────┘
左边那套(rtpd + phone-*)是”应用层 VoIP over any-IP”,我可以做,可以给你用,可以做完整加密协议,可以用 iOS CallKit 让体验接近原生电话。但它永远进不了运营商拨号盘。
右边那套(sm-dp + MiniLPA)是”eSIM 个人化通道”,我可以做完整协议层,但永远拿不到 GSMA 签发的 SM-DP+ 证书,所以商用 eUICC 永远不信我。
这两个栈的交集:没有。它们解决不同的问题,互不替代。
9. 结论
TL;DR(再说一遍):普通人自建”运营商级 WiFi Calling”——不行。三重锁死:
- eUICC 证书信任链(首道锁,最硬) —— GSMA Root CI1 公钥烧死在 ECASD,私有证书行不通,软件改不了,这是硅片级的死结。
- 运营商 IMS 核心网 —— 凭据在 ISIM @ UICC,ePDG / HSS / IBCF / IPX 全部是企业级专有基础设施。
- iOS / Android WiFi Calling 入口 —— carrier entitlement + carrier bundle + modem 固件,平台权力决定谁能被点亮。
普通人能做的最远边界:应用层 VoIP over WiFi。它不是 WiFi Calling,不能进运营商拨号盘,不能跨运营商互通 PSTN 出园区,但仍能在 IP 范围内提供良好体验——我手头的 rtpd + phone-android / phone-ios 就是这个边界的一个真实案例。
“那能不能做私有 IMS 网?”——企业可以,个人不行,仍要 GSMA 注册 + 合规审计 + 法人主体。
“那 GSMA SGP.22 ‘999’ 是不是个人通道?”——不是。简化通道仍是企业通道,仍然走 GSMA PKI。
“那把 GSMA Root CI 装到手机系统信任根里能不能绕过?”——不能。iOS / Android 系统信任根管不到 eUICC,ECASD 是独立的 JavaCard 安全元件。
如果你想做”普通人能用的语音通话网络”,我的真实建议:
- 不要碰 eSIM / SM-DP+ 这一条路,除非你是企业、愿意投百万级 + 半年到一年走 SAS-SM
- 走应用层 VoIP——IP 网络上能跑通就好,iOS CallKit 让体验接近原生电话,体验上限就在这里
- 要合法合规:不要碰真实 PSTN 互通,不要碰紧急呼叫责任,不要碰 PLMN 标识
如果你想做”私有 5G/LTE + 私有 IMS”——这是个百万级起步的企业项目,不是个人项目,请按企业路径走。
10. 参考资料
- GSMA - eSIM & SAS
- GSMA blog - OTT Calling Protocol Analysis
- Apple Support - Use Wi-Fi calling on your iPhone
- Android Source - Wi-Fi calling
- VoLTE Info - Wi-Fi Calling Architecture
- Mavenir - WiFi Calling (VoWiFi) vs OTT VoIP
- Ericsson - WiFi Calling Deployment
- Cisco - ePDG (Evolved Packet Data Gateway)
- Facebook Engineering - WhatsApp Calling Architecture
- Signal Blog - Calling 2.0
- 3GPP TS 23.228 - IMS
- 3GPP TS 23.402 - Non-3GPP Access
- 3GPP TS 24.502 - Non-3GPP Access Session Management
- 3GPP TS 33.402 - IKEv2 / IPsec Security
11. 写在最后
我搭 sm-dp 的初衷是”我想搞清楚 eSIM 到底是怎么工作的”。结果搞清楚之后,发现这条路对个人没有商用落点。
但搞清楚的过程本身有价值。我跑完了协议层,撞上了 eUICC 信任链这堵墙,写下这篇文章,至少能让下一个想”自建 eSIM 运营商”的朋友少走三个月弯路。
对普通开发者来说,应用层 VoIP over IP 仍然是 IP 网络上能做的事情里体验最好的一条路。iOS CallKit 让它看起来像电话,Signal Protocol 让它端到端加密,WebRTC 让浏览器免装 App。它不是运营商级 WiFi Calling,但它是真正可用的语音通信。
这是我能给出的最诚实的答案。





,展开后,用wget下载deb安装包,找个ubuntu机器,用



粤ICP备2022112217号