用 Developer ID 自主分发意味着什么

将 macOS 应用交付给用户,大致有两条路。一条是通过 Mac App Store (MAS),另一条是由开发者自行制作 .dmg(或 .app)文件,让用户从网站或 GitHub 等地方下载的直接分发 (direct distribution)

直接分发有明显的优势:不必等待 App Store 审核,没有支付手续费,可以随时以自己想要的方式发布更新。但相应地,原本由 App Store 代劳的事情——代码签名公证 (notarization)自动更新——都需要开发者自行搭建。

如果不搭建这些,macOS 的安全机制 Gatekeeper 会阻止应用启动。用户第一次打开应用时会看到类似"无法打开该 App,因为它来自身份不明的开发者"的警告,大多数用户就此放弃安装。要让应用像正规分发的应用那样双击即可打开,就必须用 Developer ID 证书签名应用,并获得 Apple 的公证

本系列介绍的正是这套一次性准备流程。好消息是,这里涉及的配置大多只需做一次,即可在所有后续发布中复用

本系列将完成的内容

共三篇,逐步搭建以下内容:

  • (第 1 篇,本文) Developer ID Application 证书 + 公证用凭据
  • (第 2 篇) 用于 Sparkle 自动更新的 EdDSA 签名密钥
  • (第 3 篇) 托管更新 Feed 的公开仓库 + 收尾构建配置

系列结束时,你应该已经准备好以下内容。现在先浏览一下清单,每篇逐一完成即可。

  • macOS 钥匙串 (Keychain) 中的 Developer ID Application 证书
  • 存储在钥匙串中的公证用凭据配置文件
  • 用于 Sparkle 更新签名的 EdDSA 密钥对(公钥 + 私钥)
  • 私钥备份到安全位置
  • 用于托管更新 Feed (appcast) 的公开 GitHub 仓库 + GitHub Pages
  • 构建配置文件 (ExportOptions.plist) 与应用侧配置

示例应用 — FocusTimer

本系列以虚构的 macOS 应用 FocusTimer(一款管理专注时间的简单计时器应用)为例。命令和路径中出现的 FocusTimer、Bundle Identifier com.example.FocusTimer、域名 example.com、证书名称、Team ID 等均为示例值。实际操作时请替换为你自己的应用名称和账户信息。

本文基于 Xcode 26Sparkle 2.x(自动更新框架,第 2 篇中会用到)编写。Apple 时常调整界面布局和菜单位置,若按钮名称略有不同,整体流程不变。

前提条件 — 命令行工具

首先安装构建和发布自动化所需的两个命令行工具。前提是已安装 macOS 包管理器 Homebrew

brew install gh create-dmg
  • gh — GitHub 官方 CLI,稍后用于创建 GitHub Release。
  • create-dmg — 用于制作分发用 .dmg 磁盘镜像的工具,同时生成引导用户将应用拖入 Applications 文件夹的界面。

虽然现在还用不到,但发布自动化脚本通常会在开头检查这些工具是否存在,找不到就立即终止,因此提前安装好。

第 1 步 — 确认 Apple Developer Program

要申请 Developer ID 证书并对应用进行公证,需要 Apple Developer Program 会员资格。个人账户每年收费 $99。

如果已加入,只需确认状态是否为活跃即可。

  1. 访问 developer.apple.com/account
  2. Membership details 部分确认状态为 Active
  3. 在同一页面记下你的 Team ID(示例中为 ABCDE12345),后续步骤中会反复用到。

若尚未加入,会员资格审批通常需要约一天时间。审批完成前无法申请证书,建议优先处理。

第 2 步 — 申请 Developer ID Application 证书

Developer ID Application 证书是对分发用 .app.dmg 进行签名时使用的证书。macOS Gatekeeper 会将用此证书签名的应用识别为"已知开发者制作的应用"。

Apple 还有一种用于对 .pkg 安装包签名的 Developer ID Installer 证书,两者不同。本系列以 .dmg 方式分发,因此只涉及 Application 证书。

申请流程

最简单的方式是通过 Xcode 操作。

  1. 启动 Xcode → 菜单 Xcode → Settings… (⌘,)
  2. 选择 Accounts 标签 → 在左侧列表中点击你的 Apple ID
  3. 点击右下角的 Manage Certificates… 按钮
  4. 在新弹出的窗口左下角点击 + 按钮
  5. 从菜单中选择 Developer ID Application
  6. 一两秒后新证书出现在列表中,点击 Done

如此申请的证书会自动保存到 macOS 钥匙串 (Keychain) 中。证书与对应的私钥成对保存,有了这把私钥才能进行签名。

确认申请结果

在终端运行以下命令,确认证书是否已正确安装。

security find-identity -v -p codesigning | grep "Developer ID Application"

如果输出如下一行,则表示成功。

  1) A1B2C3D4E5F6...  "Developer ID Application: Gildong Hong (ABCDE12345)"

引号内的字符串是证书的正式名称。Gildong Hong 是注册在 Apple 账户中的姓名,括号内的 ABCDE12345 是第 1 步记下的 Team ID。这个名称在后续自动化代码签名时会原样使用,请再次核对。

证书续期

Developer ID 证书的有效期为 5 年。临近过期时,Xcode 的 Manage Certificates 列表中会显示 Expired。届时按照相同步骤重新申请新证书即可。

好消息是,已用旧证书签名并分发的应用不会在证书过期时立即失效。只有之后新建的构建才需要用新证书签名。因此无需过分担心过期问题。

第 3 步 — 注册用于公证的 App 专用密码

什么是公证 (notarization)

公证是在分发前将应用上传至 Apple 服务器进行恶意软件扫描的流程。通过扫描后,Apple 会颁发"公证票据",应用附带此票据后,Gatekeeper 才会无警告地开启应用。如果说签名证明"谁制作了它",那么公证就是"Apple 已扫描过一次"的独立证明流程。

公证提交使用 Apple 的 notarytool 命令,每次提交都会要求输入 Apple ID 密码。为避免每次手动输入,需要创建一个 App 专用密码 (App-Specific Password) 并提前存入钥匙串。

3-1. 申请 App 专用密码

App 专用密码是一种 16 位密码,用于替代主 Apple ID 密码,仅供特定用途使用。

  1. 访问 appleid.apple.com → 使用加入了 Apple Developer Program 的 Apple ID 登录(示例中为 [email protected]
  2. 登录与安全 (Sign-In and Security) → 选择 App 专用密码 (App-Specific Passwords)
  3. 点击 + 按钮 → 输入密码名称(如 focustimer-notarize
  4. 点击 创建 (Create) → 再次输入 Apple ID 密码
  5. 屏幕上显示 abcd-efgh-ijkl-mnop 格式的 16 位密码。

关闭此页面后将无法再次查看该密码。 在进入下一步之前,请先将其复制到密码管理器等安全位置。

3-2. 存入 notarytool 配置文件

将申请到的密码保存为钥匙串配置文件。执行以下命令前,请将 --password 的值替换为刚才获得的实际密码。

xcrun notarytool store-credentials "focustimer-notarize" \
  --apple-id "[email protected]" \
  --team-id "ABCDE12345" \
  --password "abcd-efgh-ijkl-mnop"
  • 第一个参数 "focustimer-notarize" — 为此配置文件指定的名称,之后公证时将通过此名称加载凭据。
  • --apple-id — Apple Developer Program 账户的邮箱
  • --team-id — 第 1 步中的 Team ID
  • --password — 3-1 中申请的 App 专用密码

出现如下输出即为成功。

Credentials saved to Keychain.

凭据已存入钥匙串,App 专用密码的原始文本不再需要保留。(但若计划在其他电脑上也进行构建,建议保存在密码管理器中以备用。)

3-3. 验证

确认已保存的配置文件是否实际可用。

xcrun notarytool history --keychain-profile "focustimer-notarize"

若出现 Successfully received submission history. 的消息,则表示正常。其下的提交历史可能为空(因为尚未公证过任何内容,这是正常的),也可能有之前的记录。

关键陷阱 — Apple ID 与 GitHub 账户是不同的

这是首次配置直接分发时最容易卡住的地方。

  • Apple Developer Program 账户 — 用于申请证书和公证(示例:[email protected]
  • GitHub 账户 — 第 3 篇中用于托管更新文件(示例:[email protected]

两者通常使用不同的邮箱。特别注意:notarytool store-credentials--apple-id 必须填写 Apple Developer 账户邮箱。若填写 GitHub 账户邮箱,认证会失败,而且错误信息不够友好,难以找到原因。

若两个账户的邮箱容易混淆,建议记录下哪个是 Apple 用、哪个是 GitHub 用。这一区别在本系列中会反复出现。

第 1 篇小结

跟到这里,你现在已经准备好以下内容:

  • ✅ 发布自动化用命令行工具(ghcreate-dmg
  • ✅ 活跃状态的 Apple Developer Program 会员资格
  • ✅ 存储在钥匙串中的 Developer ID Application 证书
  • ✅ 存储在钥匙串中的公证用凭据配置文件focustimer-notarize

至此,签名公证应用的准备工作已完成。但几乎没有应用只发布一次就结束。你需要不断推出修复 Bug、增加功能的新版本。如果是 App Store 应用,自动更新由 App Store 代劳;而直接分发则需要自行搭建这一机制。

下一篇将为 macOS 应用事实上的标准自动更新框架 Sparkle 创建 EdDSA 签名密钥。这是独立于证书之外、专门用于验证更新文件的机制。