- Published on
Passkey 可移植性只到货了一半 — CXF 已成为标准,CXP 还停在 2024 年的工作草案
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 引言 — 「passkey 可以导出」这句话实际覆盖的范围
- 两份规范,两种速度
- CXP 工作草案对自己说了什么
- CXF 实际搬运的是什么 — 明文私钥
- 那么机密性由谁负责
- 实际发货的是什么 — 操作系统居中转发的同设备内传输
- 无法搬运的那些 passkey
- 还没解决的问题 — 拒绝导出与锁定
- 那么现在应该怎么做
- 结语
- 参考资料
引言 — 「passkey 可以导出」这句话实际覆盖的范围
2026 年 6 月,Android 加入了 passkey 导入/导出功能。前一年,iOS 26 已经做了同样的事。只看标题,故事似乎已经结束了 — passkey 现在可移植了,锁定的担忧解决了。
但直接打开规范目录,画面就不一样了。FIDO 的凭据交换被拆成了两份文档,而不是一份,而这两份的成熟度相差一个数量级。一份成了标准,另一份则已经是第 21 个月的工作草案。而且,做完的那份还依赖没做完的那份来保证安全。
本文梳理这种不对称在实务中意味着什么。先说结论:截至 2026 年 7 月,实际发货的并不是「跨生态的 passkey 可移植性」,而是「操作系统居中转发的、同一设备内的应用间迁移」。这个差别在营销文案里会消失,但在架构里不会。
两份规范,两种速度
按原样读 FIDO Alliance 的规范目录(fidoalliance.org/specs/cx/),情况是这样的。
cxf-v1.0-wd-20240522.html CXF 工作草案 2024-05-22
cxf-v1.0-wd-20241003.html CXF 工作草案 2024-10-03
cxf-v1.0-rd-20250313.html CXF 审阅草案 2025-03-13
cxf-v1.0-ps-20250814.html CXF Proposed Standard 2025-08-14
cxf-v1.0-errata-20260309.html CXF 勘误 2026-03-09
cxf-v1.0-ps-errata-20260309.html CXF PS + 已并入勘误 2026-03-09
cxp-v1.0-wd-20240522.html CXP 工作草案 2024-05-22
cxp-v1.0-wd-20241003.html CXP 工作草案 2024-10-03
...到这里就结束了
CXF(Credential Exchange Format) 定义的是交换什么 — 数据结构与格式。它从工作草案、经过审阅草案,在 2025 年 8 月 14 日成为 Proposed Standard,又在 2026 年 3 月 9 日追加了勘误。这是一条正常的标准化轨迹。
CXP(Credential Exchange Protocol) 定义的是怎么交换 — 两个提供方之间实际的传输协议。它最后一个版本停在 2024 年 10 月 3 日的工作草案上,既没有审阅草案,也没有 Proposed Standard。截至今天(2026 年 7 月 16 日),它已经原地不动 21 个月了。
顺带一提,2026 年 3 月的勘误从内容上看只是编辑性修正。附录 A 的示例载荷中有一个与规范正文不一致、遗留下来的 hmacSecret 键,勘误把它改成了 hmacCredentials,并补上了缺失的 example-file 内容,仅此而已。勘误文档自己写着「The contents of example-file were missing and cfx example was updated」。规范性行为并没有改变,所以不应该把这理解成「CXF 在 2026 年做了一次大改」。
CXP 工作草案对自己说了什么
把 CXP 工作草案里「Status of This Document」一节原样搬过来是这样的。
This is a Working Draft Specification and is not intended to be a basis for any
implementations as the Specification may change. This document is merely a FIDO
Alliance working group internal and member-confidential document. It has no
official standing of any kind and does not represent consensus of the FIDO Alliance.
No rights are granted to prepare derivative works of this Specification.
这读起来像是可以一眼扫过的模板文字,但考虑到它正是 passkey 可移植性故事里「传输」那一半,每一句都分量不轻。「不打算作为任何实现的依据」「仅仅是 FIDO Alliance 工作组内部、会员保密的文档」「不具有任何官方地位,也不代表 FIDO Alliance 的共识」。
文档的未完成程度在目录里也看得出来。CXP §5「Implementation Requirements」— 本该定义这份规范里哪些算法和功能是实现必需的一节 — 内容只有两句话:「This section defines which algorithms and features of this specification are mandatory to implement. Applications using this specification can impose additional requirements upon implementations that they use.」然后就直接跳到 §6 Security Considerations。必须实现的算法集合是空的。互操作的地基还没打好。
协议本身确实有骨架。用 Diffie-Hellman 密钥交换在两个提供方之间建立安全通道或数据载荷,协商 HPKE 参数(mode 为 base / psk / auth / auth-psk),归档则用 DEFLATE。流程分五步:导入方生成 Export Request → 授权主体决定迁移 → 导出方加密凭据数据 → 发送 Export Response → 导入方解密捆绑包。设计是合理的。但「有设计」和「是标准」是两回事。
CXF 实际搬运的是什么 — 明文私钥
从这里开始才是重点。CXF 规范中 Passkey 条目里,key 成员的定义是这样的:
key
The private key associated to this passkey instance.
The value MUST be PKCS#8 ASN.1 DER formatted byte string which is then
Base64url encoded.
The value MUST give the same public key value that was provided by the
original authenticator during registration.
规范附录 A 的示例载荷让这一点变得具体。(以下原样搬自 CXF PS + 勘误文档中的示例。)
{
"id": "akKA3Y0jQRuK7sKplB0Y9w",
"title": "WebAuthn.io",
"credentials": [
{
"type": "passkey",
"credentialId": "Y3JlZGVudGlhbElkRXhhbXBsZQ",
"rpId": "webauthn.io",
"username": "johndoe",
"userDisplayName": "John Doe",
"userHandle": "cnEzaNHWcYK3coWZjvoaV1Hj9gnI12mKe2dL2HZVFlY",
"key": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgARu_0sCt20EpgVxb4Puq3Ga5VVLpuTY75ngvZlyq3X6hRANCAASmdk1xLsK0oOlhxIPp0d1ZuS0sT9nf6BZtSelhqvLBW0fOL33l_bXgsr_STUHjCLn8l6gcRJwe7OQvbQubZ1dY"
}
]
}
key 字段里的这串字符就是一个 P-256 私钥。没有包装,没有加密,没有口令。同一个示例里,密码紧挨着以 "value": "securepassword123" 的形式、TOTP 种子以 "secret": "JBSWY3DPEHPK3PXP" 的形式,同样以明文出现。
这不是 bug,而是设计。CXF 只是一种格式,它在自己的 §1.2 Scope 里明确划了界线 — 「This document outlines the data structures and format needed to exchange credentials and does not make any assumptions about the protocol used for the transfer, such as the protocol outlined by CXP.」也就是说,它对传输不做任何假设。换句话说,单看 CXF 这一份文档,它的产物就是一份装着你全部私钥、密码和 TOTP 种子的明文 JSON。
那么机密性由谁负责
CXF 的 §5 Security Considerations 是这样回答这个问题的。
Note: [CXP] ensures that the credential exchange remains confidential between
the exporting provider and importing provider.
也就是说,CXF 说「机密性由 CXP 来保证」,而 CXP 说「我不打算作为任何实现的依据,也不具有任何官方地位」。这是一个已完成的规范把安全性托付给一个未完成规范的结构。这正是本文的核心。
CXF 意识到了这个空白,并留了一个出口。同一份 §5.1 规定:orchestrating party(居中调度交换的主体)可以(MAY)只实现 CXF 而不实现 CXP。但如果不实现 CXP,开发者就必须(MUST)把该凭据交换的安全机制文档化,并且必须(MUST)把该文档发布在一个公开网站上。发布的文档必须写明:中间方只是转发数据,不会保留数据或挪作他用,也不会篡改所转交的数据。
坦白讲,这是用各家厂商自行文档化的承诺,替代了一套标准化的加密协议。总比什么都没有强,但和可互操作的安全保证不是一回事。你得到的不是一个可验证的协议,而是一句「我们是这么做的」的公开声明。
实际发货的是什么 — 操作系统居中转发的同设备内传输
那么 Apple 和 Google 在 2026 年到底发布了什么?这里正是上面那种张力被化解的地方。
Apple。 ASCredentialExportManager 和 ASCredentialImportManager 加入了 iOS 26.0、iPadOS 26.0、macOS 26.0、Mac Catalyst 26.0、visionOS 26.0。Apple 开发者文档原样读下来,行为很明确。
The operating system acts as an intermediary to establish the identities of the
password manager apps involved and performs the exchange; this process doesn't
write any data to the file system.
操作系统作为中间方,确认双方密码管理器应用的身份,并直接执行这次交换,在此过程中 不会向文件系统写入任何数据。应用需要在扩展的 plist 里声明 SupportsCredentialExchange,并在 SupportedCredentialExchangeVersions 里以数组形式列出支持的版本,而根据 Apple 文档,目前可取的值只有 1.0 这一个。
看懂这个设计,就能明白为什么可以不需要 CXP。当操作系统在同一台设备内充当可信的中间方时,两个提供方就不需要互相认证、协商信道 — 明文的 CXF 载荷不会落到磁盘上,只在操作系统的边界内部流动。换句话说,Apple 并没有实现 CXP,而是 绕开了 CXP 本来要解决的问题。用平台的控制权取代了协议。
Google。 Google Play services v26.21(2026-06-01)发行说明里 Security & Privacy 一项写着 — "[Phone] You can now import and export passwords and passkeys between Google Password Manager and third-party password managers with the Credential Exchange standard."
这里要说准确。Google 的官方发行说明只写了「Credential Exchange standard」,并没有点名 CXP。有些媒体写成「Android 支持 CXP 和 CXF」,但那是二手信源的解读。Google 自己的措辞比这更含糊,标签也只有 [Phone]。而相关的 SDK 制品 com.google.android.gms:play-services-identity-credentials,在 Google 的 Maven 仓库上最新版本仍是 16.0.0-alpha11(最后更新于 2026-02-09)。从 alpha01 一路走到了 alpha11,还没到 beta。
综合起来看,双方实际发货的都是同一台设备内、由操作系统居中转发的应用间迁移。跨设备的传输、跨生态的传输 — 也就是本该由 CXP 负责的那部分 — 还没有。
无法搬运的那些 passkey
CXF 有两条实务者容易忽略的规范性限制。两条说的都是「有些 passkey 根本无法导出」。
第一,签名计数器不为零的 passkey。 CXF 规范 Passkey 一节里的 Note,原样搬过来是这样的。
Note: Passkeys using a non-zero signature counter MUST be excluded from the export
and the exporter SHOULD inform the user that such passkeys are excluded from the
export. Importers MUST set a zero value for the imported passkey signature counters
and MUST NOT increment them after the fact.
导出方必须(MUST)把签名计数器不为零的 passkey 排除在导出之外,导入方必须(MUST)把导入的 passkey 计数器设为零,并且之后不得(MUST NOT)递增。签名计数器本来是为了检测认证器被复制而设计的,这个目的和可移植性在原理上是冲突的 — 一个用来检测复制的机制,和一个用来合法复制凭据的功能,没法共存。规范把这个冲突解决成了偏向可移植性,代价是使用计数器的凭据被排除在交换之外。
这里需要精确地读规范。根据 WebAuthn Level 3 §6.1.1,RP 会比较存储的计数器和新收到的 signCount,而被判定为疑似复制,仅在「两者中至少有一个不为零(if either is non-zero),且新值小于或等于存储值」时才触发。也就是说,一个计数器始终为零的 passkey,本来就不会触发这项检查。规范也明确说明,不实现计数器的认证器会把 signCount 固定为零,所以零是规范预期中的正常值。
所以老实说,导入进来的 passkey 身上发生的事,不是「被误判锁住」,而是 单纯丢失了复制检测的信号。CXF 相当于用克隆检测换来了可移植性。不过,如果你运行的逻辑比规范更严格 — 比如直接实现了「计数器必须一直递增」— 情况就不一样了:那段逻辑会撞上规范认定为正常的值,把正常用户挡在外面。顺带一提,规范明确说明它不区分计数器不一致究竟是复制、故障还是竞态,把应对方式留给各个 RP 自己的风险容忍度。
第二,硬件安全密钥。 这一条更根本。在 FIDO Alliance 的官方公开反馈仓库(fido-alliance/credential-exchange-feedback)里,一位 CTAP 实现者问「要把硬件认证器上的凭据搬到另一个认证器上,该看哪份标准」(issue #33,2025-08-02)。fido-alliance GitHub 组织成员的回答是这样的。
No certified FIDO hardware authenticator allows a credential to leave its secure
element, so what you're asking for does not exist, by design.
没有任何一款经认证的 FIDO 硬件认证器允许凭据离开其安全元件,这是设计使然。在同一串讨论里,他明确表示凭据交换和 CTAP 毫无关系,目标是「credential manager 之间的迁移」。
把这两条限制放在一起,画面就清楚了。凭据交换是一个 软件凭据管理器之间的迁移工具。它不是、以后也不会是把 passkey 搬出 YubiKey 的故事。使用硬件密钥的组织如果看到「passkey 现在可移植了」这样的标题就据此制定备份策略,那是建立在错误的前提上。硬件密钥的备份策略依然是「注册两个以上」。
还没解决的问题 — 拒绝导出与锁定
可移植性规范到底能不能防止锁定,这是个悬而未决的问题。2025 年 1 月 21 日开的 issue #24「Risk of lock-in from export blocking」 正面提出了这个问题。要点是这样的 — CXP 看起来给提供方留了拒绝导出请求、或者有选择地批准导出的余地,这样一来,提供方 FOO 涨价之后,就可以专门挑着挡掉去竞争对手 BAR 的导出,用户的凭据就成了人质。提出者把这归结为「permissionlessly exportable」(无需当前提供方许可即可导出的性质)是否应该纳入规范范围的问题。
值得注意的是回复。一位挂名 CXF 贡献者的 FIDO 方参与者在 2025 年 3 月说「这个顾虑我们听得够多了,Alliance 内部也讨论了很多」,5 月又补充道:
The general vibe is supportive and language has been added to this effect, though
it looks like we haven't done a public working draft in some time so I don't think
that's externally visible yet.
也就是说,内部已经加入了相关措辞,但因为已经有一段时间没有发布公开工作草案了,所以外部还看不到。而截至 2026 年 7 月的现在,依然看不到 — 因为公开的最新 CXP,当时是、现在也仍是 2024 年 10 月 3 日的那份工作草案。issue 仍然开着。
这不是要指责规范流程。以会员保密方式推进工作是 FIDO 一贯的做法,标准原本就走得慢。但站在实务者的角度诚实地说,我们没法读到防锁定的措辞到底是怎么用的,而读不到的保证是没法写进计划里的。
同一条 issue 讨论串里的反方意见也值得记一笔。一方说「既然已经选择依赖第三方服务,不管哪种登录方式不都一样吗」;提出者反驳说,像 Firefox 这样的本地开源软件,即便更新阻止了导出,用户仍然可以安装旧版本或者自己改代码,但存在安全元件里的凭据在原理上就需要制造商或运营方的许可,并拿域名注册商与 ICANN 的关系作类比。这里不打算裁定哪一方对。但这场争论从 2025 年 1 月悬而未决至今这件事本身,说明可移植性首先是一个治理问题,其次才是技术问题。
那么现在应该怎么做
落到实务判断,是这样的。
现在可以指望的
- 在同一台设备内切换凭据管理器的场景。在 iOS/iPadOS/macOS/visionOS 26,以及 Android(Play services 26.21 及以上,目前标着
[Phone])上确实能用。 - 取代 CSV 导出。这才是 CXF 真正的成就。CXP 规范的序言本身就把 CSV 点名为要解决的问题 — 不安全的格式、缺乏标准结构导致的迁移失败。仅仅有了结构化的 JSON schema,这两点就都得到了改善。
- 把密码、TOTP 及其他类型的条目和 passkey 一起搬走。CXF 不是只针对 passkey,它定义了凭据管理器内多种条目类型。
目前还不该写进计划的
- 跨设备、跨生态的传输。把 passkey 直接从 iPhone 传到 Android,这是 CXP 的活儿,而 CXP 目前仍是公开工作草案。
- 硬件安全密钥的迁移或备份。按设计根本不存在。注册两个以上仍然是唯一的答案。
- 导出成文件、由用户自己保管的备份。目前发货的路径是操作系统居中转发的应用对应用,根据 Apple 文档,不会向文件系统写入任何东西。正如 issue #24 中一位参与者指出的,「我想要一份自己攥在手里的副本」这种诉求,和目前的设计方向不一致。
- 提供方不会拒绝导出的保证。公开的规范里目前还没有这个保证。
如果你在运营 RP,应该检查这些
- 签名计数器逻辑是不是比规范更严格。WebAuthn 的复制检测只在值不为零时触发,所以如果你按标准实现,导入的 passkey 会悄悄通过。但如果你直接实现了「计数器必须一直递增」,就会挡住正常用户。而且无论哪种情况,事实都不变:对于导入的 passkey,基于计数器的复制检测已经不再提供信号。
- 备份状态(BE/BS)标志是怎么用的。正如 issue #33 讨论串里 FIDO 方的回答所指出的,BE/BS 标志表示的不是「是否可共享」,而是这是同步型 passkey 还是设备绑定型 passkey,以及认证器目前是否认为自己已经备份。如果有把这个标志解读为可共享性的策略,那前提就错了。
- 账号恢复路径。可移植性变好并不能解决恢复问题。能够切换管理器,和丢失全部设备后还能找回账号,是两个不同的问题,后者依然是每个 RP 自己的功课。
结语
归纳一下:凭据交换是两份规范,完成度不一样。CXF 定义了交换什么,已经成为 Proposed Standard — 这是真实的成就,也会终结 CSV 的时代。CXP 本该定义怎么交换,却停在 2024 年 10 月的工作草案上,而那份文档自己写明它不是实现的依据,也不具有官方地位。然而 CXF 搬运的载荷里装着 PKCS#8 明文私钥,CXF 自己的安全考虑事项又把机密性托付给了 CXP。
各平台用控制权而不是协议填上了这个空白。操作系统成为中间方,在同一台设备内完成交换,磁盘上什么都不写。这是个聪明的解法,也确实能用,但这不是「passkey 变得可移植了」,而是「现在可以在一台设备内切换管理器了」。这两者之间的距离,正是 CXP 本该填补的位置,而那个位置目前仍是空的。
所以,请看规范目录里的文件名,而不是标题。cxp-v1.0-wd-20241003.html — 在这个日期改变之前,跨生态的 passkey 可移植性是路线图上的条目,还不是已经发货的功能。
参考资料
- FIDO Alliance 凭据交换规范目录 — CXF/CXP 所有版本的原始列表
- Credential Exchange Format 1.0 — Proposed Standard(已并入勘误,2026-03-09)
- Credential Exchange Format 1.0 — Proposed Standard 原版(2025-08-14)
- Credential Exchange Format 1.0 勘误(2026-03-09)
- Credential Exchange Protocol 1.0 — 工作草案(2024-10-03,最新公开版)
- FIDO Alliance — Credential Exchange 规范概览页
- fido-alliance/credential-exchange-feedback — 面向非会员公开的反馈仓库
- issue #24 — Risk of lock-in from export blocking(2025-01-21,未关闭)
- issue #33 — CTAP intersection with CXP(2025-08-02,未关闭)
- W3C WebAuthn Level 3 §6.1.1 — Signature Counter Considerations
- Apple Developer — ASCredentialExportManager(iOS/iPadOS/macOS/visionOS 26.0)
- Apple Developer — ASCredentialImportManager
- Google System Release Notes — Google Play services v26.21(2026-06-01)
- Google Maven — play-services-identity-credentials 版本列表(最新 16.0.0-alpha11)