macOS 公证(notarization)
口径说明
融云公开文档不覆盖 macOS 原生 SDK 与桌面交付链(dmg / 代码签名 / 公证 / 分发 / Gatekeeper);本页为本项目按 Apple 与 Electron 官方实践补齐的接入文档。
公证是 Apple 对分发包的自动安全检查:把已签名的 `.app`(或 `.dmg`)上传到 Apple 公证服务,审核通过后把票据 stapling 到包上。没有公证的包在其它 Mac 上会被 Gatekeeper 拦截。
前置条件
- 已用 Developer ID Application 证书签名,并开启 hardened runtime(`--options runtime`)。
- Apple Developer 账号已加入开发者计划,知道 Team ID。
- 生成 App 专用密码(Apple ID 管理页 → 安全 → App 专用密码),不要用账号主密码。
- 网络可访问 `apple.com` 公证服务;CI 上需要放行出站 HTTPS。
方式一:notarytool(推荐)
Shell
# 1) 先打 zip(公证接受 .zip / .dmg / .pkg)
ditto -c -k --keepParent "dist/mac/My Desktop.app" "dist/My Desktop.zip"
# 2) 提交并等待结果(首次通常 1~5 分钟)
xcrun notarytool submit "dist/My Desktop.zip" \
--apple-id "you@example.com" \
--team-id "ABCDE12345" \
--password "app-specific-password" \
--wait
# 3) 成功后将票据 stapling 到 .app(离线也能通过 Gatekeeper)
xcrun stapler staple "dist/mac/My Desktop.app"
# 4) 验证
spctl -a -vvv "dist/mac/My Desktop.app"
xcrun stapler validate "dist/mac/My Desktop.app"方式二:electron-builder 自动公证
Shell
# 通过环境变量提供凭据,electron-builder 会在签名后自动提交公证并 stapling
export APPLE_ID="you@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="app-specific-password"
export APPLE_TEAM_ID="ABCDE12345"
npx electron-builder --mac --x64 --arm64dmg 的公证
如果只公证 `.app`,dmg 内的应用仍带票据;更稳妥的做法是对最终 dmg 再提交一次公证并 stapling,确保用户挂载 dmg 后首次启动即通过。
Shell
xcrun notarytool submit "dist/My Desktop-0.1.0.dmg" \
--apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --wait
xcrun stapler staple "dist/My Desktop-0.1.0.dmg"
xcrun stapler validate "dist/My Desktop-0.1.0.dmg"查看公证日志
Shell
# 提交失败时,用返回的 id 拉取详细日志
xcrun notarytool log <submission-id> \
--apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD"
# 历史提交
xcrun notarytool history --apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD"常见失败原因
| 日志关键字 | 原因 | 处理 |
|---|---|---|
| `The binary is not signed with a valid Developer ID certificate` | 用了 ad-hoc 或开发证书 | 改用 Developer ID Application 证书 |
| `The executable does not have the hardened runtime enabled` | 未开启 hardened runtime | 签名加 `--options runtime` |
| `The signature does not include a secure timestamp` | 缺时间戳 | `codesign --timestamp` |
| `The bundle is damaged` / `Invalid` | 嵌套组件签名不一致 | 重新打包,避免手改包内容 |
| `You must first sign the relevant documents` | 凭据错误或未同意协议 | 检查 App 专用密码与开发者协议状态 |
公证不是“签名替代品”
顺序固定为:签名 → 公证 → stapling → 验证。任何一步重做后都要重新走完整链路。