指南 / Apple 签名凭据
Apple 签名证书不等于私钥:macOS CI 的 .p12 与临时钥匙串
更新于 2026-11-02 · 官方资料核对于 2026-11-02
把 Apple 签名证书复制到 CI 后,签名仍提示找不到身份?常见原因是只复制了证书,没有对应的私钥。Apple 把两者的组合称为签名身份:证书包含公钥,签名操作还需要与之匹配的私钥。先弄清这几种文件和钥匙串的关系,再决定凭据该放在哪里、由哪个任务使用。
先分清证书、私钥和签名身份
| 对象 | 包含什么 | 在签名流程中的作用 |
|---|---|---|
| 证书(常见为 .cer) | 公开的证书数据,含公钥及签发者、主体和有效期等信息。 | 说明公钥与开发者身份的关联;单独的证书不能替你生成签名。 |
| 私钥 | 与证书公钥匹配的秘密密钥,通常由生成 CSR 的 Mac 保存在钥匙串中。 | 签名时由签名工具使用。下载证书不会把原 Mac 上的私钥一并带到新机器。 |
| 签名身份(digital identity) | 证书和对应私钥的组合;Apple 工具常用 PKCS#12 文件(通常为 .p12)在设备间导入导出。 | 只有证书和私钥都能被当前签名用户访问时,才能完成对应签名。 |
Apple 的 TN3161 明确区分证书与签名身份:证书只包含公钥,签名还需要匹配的私钥。私钥通常在创建证书签名请求时生成并留在钥匙串中。如果最初创建 CSR 的 Mac 不再可用,只有下载的 .cer 文件并不能还原私钥。若需要迁移完整身份,应由有权限的团队成员从钥匙串导出受密码保护的身份文件,并按团队凭据流程保管。
先选签名方式,再决定是否需要 .p12
若使用 Xcode Cloud,Apple 的云管理签名流程由 Apple 管理证书和私钥;不要把自托管 CI 的 .p12 导入步骤硬套到云管理流程。若使用自托管 macOS runner,团队可能需要把本地创建的签名身份交给构建任务,此时才评估通过 .p12 导入。
导出的 .p12 和它的密码都应当视为凭据,而且 .p12 密码与 CI 临时钥匙串的密码是两项不同的凭据。Apple 提醒,拿到导出身份并知道密码的人可能用它签署看起来来自该开发者账户的软件。不要把它们提交到仓库、构建产物、共享缓存、镜像或普通工作区,也不要把密码写进 workflow YAML。若身份泄露,应按证书类型与账户角色查看 Apple 的撤销流程;仅删除工作目录里的副本,不等于撤销已泄露的身份。
在 GitHub Actions 中缩小凭据可见范围
- 让秘密只进入签名任务。GitHub repository secret 可供该仓库的 workflow 使用;environment secret 仅向引用该 environment 的 job 提供。若项目把签名作为发布步骤,可把签名凭据放在范围更窄的发布环境,并按需要设置审批和分支规则。
- 不要把日志遮罩当作保险。GitHub 会遮罩识别出的 secret 值,但它不能保证所有变形值、错误输出或工具诊断都不会泄露。不要主动打印证书包、密码或编码后的秘密;审查成功和失败路径的日志。
- 保护运行签名任务的 runner。来自 fork 的 pull request workflow 通常拿不到 Actions secrets(`GITHUB_TOKEN` 有单独规则),但自托管 runner 不是隔离容器。Environment 审批也不会把不可信代码变成安全代码。不要让不可信的 PR 代码和签名凭据在同一自托管环境中执行。
- 只给工作流需要的令牌权限。将 `GITHUB_TOKEN` 的默认权限保持在完成任务所需的最低级别,并只在确有需要的 job 提高权限。
用作业专用钥匙串,并在结束时清理
自托管 runner 需要访问本机钥匙串中的签名身份。macOS 的 `security(1)` 工具提供创建、导入、锁定和删除钥匙串的命令;`codesign(1)` 说明签名身份必须位于当前用户可搜索的钥匙串中。遇到“找不到身份”时,可在任务账户下运行 `security find-identity -v -p codesigning`,确认该用户能看到预期身份;不要把完整输出贴进公开日志。可将身份导入为本次 job 专用的钥匙串,限定签名步骤使用它,并在 job 收尾时删除钥匙串及临时文件。
不要照搬把密码直接写在命令行参数中的脚本:macOS `security(1)` 手册把 `create-keychain` 的 `-p` 密码参数标为不安全;GitHub 也提醒命令行参数可能被同机其他进程看到。按目标 macOS 版本的内置手册选择受支持的非命令行传递方式,并避免输出 shell 跟踪或调试内容。钥匙串解锁失败时,先确认 job 使用哪个用户和钥匙串;不要为了消除提示而默认允许所有应用访问私钥。
如果钥匙串里能找到身份,但 `codesign` 仍无法访问私钥,再检查该私钥的访问控制。当前 `security(1)` 手册说明,要让 `/usr/bin/codesign` 使用私钥,partition list 中必须包含 `apple:`;不要用允许任意应用访问的 `security import -A` 来绕过授权提示,Apple 将该选项标为不安全。不同 macOS 版本的参数可能不同,执行前核对本机手册,并确保密码不会出现在命令记录或日志中。
清理步骤应覆盖正常成功和失败退出,但不要把它描述成安全擦除或主机隔离的保证。若 runner 被强制终止,磁盘上的临时文件可能来不及清理;复用硬件时还要按团队的 runner 清理策略检查残留状态。更多 runner 生命周期边界见iOS CI 与 runner 指南。
签名前后的检查清单
- 在任务账户下确认目标签名身份可见(例如使用 `security find-identity -v -p codesigning`),而非只有同名证书;避免把输出复制到公开日志。
- 确认签名凭据只传给可信的发布 job;其他工作流不需要时不要开放访问。
- 将 .p12、密码和 provisioning profile 分开管理,并检查仓库、artifact、缓存与日志没有意外副本。
- 确认临时钥匙串和文件在成功、失败及取消路径中如何清理;不要将此当成对恶意同机任务的隔离。
- 发生泄露时,删除或限制受影响的 workflow log,并按 GitHub 与 Apple 官方流程轮换秘密、撤销或替换证书。
常见问题
Apple 下载的 .cer 可以直接拿来签名吗?
不行。Apple 文档说明,证书含公钥;签名还需要与其匹配的私钥。两者组成签名身份。
把秘密放进 GitHub Environment 就足以保护自托管 runner 吗?
不够。Environment 可以限制哪些 job 访问秘密,也可以设置审批;GitHub 同时说明,自托管 runner 不会因此运行在隔离容器中。代码信任边界和 runner 清洁状态仍须单独设计。
使用 Xcode Cloud 也要自己导出 .p12 吗?
不一定。Apple 的 cloud-managed signing 由 Apple 管理证书和私钥;应按 Xcode Cloud 对应的签名流程配置,而不是假设私钥能导出到自托管 runner。
若你还在决定托管 CI 与自托管 Mac 的职责边界,可阅读iOS CI 工作流指南。若还要控制人如何登录 Mac,则查看SSH 与屏幕共享安全指南;本文讲的是代码签名身份,不是 SSH 登录密钥。
资料来源
以下官方资料于 2026-11-02 核对;Apple 与 GitHub 文档可能更新,按使用时的页面和 macOS 版本复查。
- Apple Developer:TN3161 — Inside Code Signing: Certificates
- Apple Developer Help:Create a certificate signing request
- Apple Support:Import and export keychain items using Keychain Access on Mac
- Apple Developer:Synchronizing code-signing identities with your developer account
- Apple Developer Help:Revoke a certificate
- Apple Developer:Xcode Cloud
- GitHub Docs:Using secrets in GitHub Actions
- GitHub Docs:Secure use reference
- GitHub Docs:Deployments and environments
用于具体命令选项时,还应查看目标 Mac 自带的 `security(1)` 与 `codesign(1)` 手册。本文不评测第三方密钥同步工具,也不推断任何服务商如何保存签名凭据。