Appearance
调研:从 1Password 和 Apple 密码迁移(2026-11-16)
结论先说:
- Apple 密码用 CSV 就够了。
- 1Password 走
.1pux。 - 通行密钥(passkey)只能经 FIDO Credential Exchange(CXF/CXP)迁移,而且接收方必须是签名过的原生 App,带 AutoFill credential provider 扩展。
1Password .1pux
压缩包结构: 一个账户导出一个 <accountUUID>.1pux,是 zip 包,里面有:
export.attributes,内容形如{"version":3,"description":"1Password Unencrypted Export","timestamp":…}。官方文档写的字段名是createdAt,实际样本里是timestamp。export.data,JSON 正文。files/,存附件,命名为files/<documentId>__<fileName>。
JSON 结构: accounts[] → vaults[] → items[]。
- 保险库类型:
P个人、E所有人、U用户自建。 - 条目 UUID 只在所在保险库内唯一。
- 条目字段:
uuid、favIndex、createdAt/updatedAt(Unix 秒)、state(active或archived)、categoryUuid。 overview:title、url、urls[{label,url}]、tags[]。details:loginFields、notesPlain、sections、passwordHistory[{value,time}]、documentAttributes、password(仅「密码」类别)。
类别编号:
| 编号 | 类别 | 编号 | 类别 |
|---|---|---|---|
| 001 | Login | 104 | OutdoorLicense |
| 002 | CreditCard | 105 | Membership |
| 003 | SecureNote | 106 | Passport |
| 004 | Identity | 107 | RewardsProgram |
| 005 | Password | 108 | SSN |
| 006 | Document | 109 | WirelessRouter |
| 100 | SoftwareLicense | 110 | Server |
| 101 | BankAccount | 111 | EmailAccount |
| 102 | Database | 112 | API_Credential |
| 103 | DriversLicense | 113 | MedicalRecord |
| 114 | SSH_Key |
字段:
loginFields由value、fieldType、designation(username / password)组成。fieldType取值为T、E、U、N、P、A、TEL、C。sections[].fields[]结构为{title,id,value:{<类型>:…},guarded,multiline}。类型有:string、concealed、totp(裸密钥或otpauth://,可以有多个)、url、email、phone、date、monthYear、menu、address、creditCardType、creditCardNumber、reference、sshKey(PKCS#8 + 公钥 + 指纹)、file。
限制:
- 桌面版导出不含通行密钥。1Password 只在 iOS 26+ 和 Android 14+ 上支持经 CXP 导出通行密钥。
- 1Password 的 CSV 导出只有登录和密码两类,没有自定义字段和附件。
现有导入器(都是 GPL,只能参考,不能复制)
| 导入器 | 范围 | 丢失的内容 |
|---|---|---|
Bitwarden libs/importer/.../onepassword-1pux-importer.ts | 只读第一个账户 | 保险库结构、附件、多余的 TOTP、多余的标签 |
KeePassXC src/format/OPUXReader.cpp | 只读第一个账户;覆盖最全:保险库变组,所有标签、URL、TOTP 和附件都保留 | 密码历史 |
Proton Pass 1pux.reader.ts | 读所有账户 | 把「已归档」当成「已删除」 |
Apple 密码
CSV 表头: Title,URL,Username,Password,Notes,OTPAuth。旧版 Safari 导出的表头是 Title,Url,Username,Password。
CSV 里导不出的内容:
- Apple 官方说明导不出的:Wi-Fi 密码、别人共享给你的群组密码、通过 Apple 登录的账户。
- 第三方资料提到、Apple 未确认的:通行密钥、第一个之后的其余 URL、分组。
Credential Exchange(macOS / iOS 26 起):
- 用
ASCredentialImportManager。 - 接收方条件:
- App 带 AutoFill credential provider 扩展。
- 扩展的 Info.plist 里设
SupportsCredentialExchange = YES、SupportedCredentialExchangeVersions = ["1.0"]。 NSUserActivityTypes包含ASCredentialExchangeActivityType。- 启动时读
ASCredentialImportToken,再调用importCredentials(token:)。
- 只能由导出方发起,全程不落地成文件。
- 支持的类型:basicAuthentication、passkey、totp、note、creditCard、apiKey、address、customFields、driversLicense、generatedPassword、identityDocument、passport、personName、sshKey、wifi。没有文件类型,所以附件迁不过来。
FIDO CXF / CXP
- CXF 1.0 是 Proposed Standard(2026-03 有勘误);CXP 还是工作草案。Apple 用它自己的系统通道传 CXF 数据。
bitwarden/credential-exchange:MIT 许可,crate 是credential-exchange-format,版本 0.4.0。覆盖所有 CXF 类型,但仍处在早期开发阶段,也没有内置 zeroize。可以作为依赖使用。bitwarden/sdk-internal的bitwarden-exporters/src/cxf/带有真实的 1Password 和 Dashlane CXF 样本,但它是 GPL,只能参考。
通用字段映射
| 通用字段 | 1pux | Apple CSV | CXF |
|---|---|---|---|
| title | overview.title | Title | item.title |
| urls[] | overview.urls[].url | URL(只有一个) | scope.urls / rpId |
| username/password | loginFields 里的 designation → 名为 username/password 的分区字段 → details.password | Username/Password | basic-auth |
| totp[] | value.totp | OTPAuth | totp |
| notes | notesPlain | Notes | note |
| 自定义字段 | sections + 其余 loginFields | – | custom-fields |
| 附件 | files/ + documentAttributes | – | file(Apple API 不支持) |
| 容器 | vault | – | collection |
| 标签/收藏/归档 | tags/favIndex/state | – | tags/favorite |
| 历史 | passwordHistory | – | – |
| 通行密钥 | – | – | passkey |
实施建议
- 1Password: 自己用 Rust 写 1pux 解析器。读全部账户;保险库映射成容器;所有标签、URL、TOTP、附件、历史、归档状态和带类型的字段都保留;映射不了的字段放进自定义字段,一律不丢。
- Apple: v1 用 CSV。通行密钥等 macOS / iOS 原生 App 带上 provider 扩展后再走 CXF。
- 核心库: 只写一个 CXF 到内部模型的映射,基于
credential-exchange-format,配合 zeroize。平台层只负责传 JSON。 - 需要人做的: 导出真实的测试样本。CXP 测试还需要 Apple Developer 签名。