Faster and simpler notarization for Mac apps
App Store & Distribution 入门 20m

更快更简单的 Mac App 公证

Faster and simpler notarization for Mac apps

2021年6月10日

在 Apple 官方观看视频

一句话判断

macOS Monterey 的公证(Notarization)流程从”提交等几分钟查邮件”简化成了”一条命令搞定”——notarytool 替代了 altool,支持提交、等待和装订(Staple)一步完成。

这场 Session 讲了什么

Session 介绍了 macOS App 公证流程在 2021 年的改进。公证是 Apple 要求所有在 Mac App Store 之外分发的 App 必须经过的安全验证——Apple 的服务器扫描你的 App 检查恶意代码,通过后在 App 中嵌入一个公证票据(Notarization Ticket)。

之前用 altool 提交公证需要三个步骤:提交 App 等待处理、轮询检查状态、装订票据。而且 altool 使用用户名密码认证,安全性差。新的 notarytool 把三步合为一步,支持 API Key 认证(更安全),并且在 Xcode 中直接集成了公证流程。

还介绍了 Xcode 13 中新的分发工作流:Archive > Distribute App > Notarize,整个过程在 Xcode GUI 中完成,不需要命令行。

值得深挖的点

notarytool vs altool 的差异

altool 使用 Apple ID + 密码 + App-specific password 认证。密码存在脚本或 CI 环境变量中,泄露风险高。notarytool 支持 API Key 认证——你创建一个 App Store Connect API Key,下载 .p8 文件,用 Key ID + Issuer ID + .p8 文件认证。API Key 可以设置权限粒度(只允许公证),比 Apple ID 密码安全得多。

装订(Stapling)的重要性

公证通过后,票据存在 Apple 的服务器上。macOS 验证 App 时默认会在线查询票据。但如果用户离线,验证就会失败——App 无法打开。装订(stapler staple)把票据直接嵌入到 App 包中,这样即使离线也能通过验证。之前的流程中很多人忘了装订这一步,导致离线用户无法使用 App。notarytool--staple 选项在公证完成后自动装订,不再需要单独跑 stapler

代码片段

使用 notarytool 完成公证

// 1. 用 notarytool 提交并等待公证
// notarytool submit MyApp.dmg \
//   --apple-id developer@example.com \
//   --password @keychain:AC_PASSWORD \
//   --team-id TEAM123456 \
//   --wait  // 等待完成(不再需要轮询)

// 或使用 API Key 认证(推荐)
// notarytool submit MyApp.dmg \
//   --key AuthKey.p8 \
//   --key-id ABC1234 \
//   --issuer-id 12345678-1234-1234-1234 \
//   --wait

// 2. 查看公证历史
// notarytool history \
//   --key AuthKey.p8 --key-id ABC1234 --issuer-id 12345678

// 3. 查看某次公证的详细日志
// notarytool log <submission-id> \
//   --key AuthKey.p8 --key-id ABC1234 --issuer-id 12345678

在 Xcode 中直接公证

// Xcode 13 的公证流程:
// 1. Product > Archive
// 2. Distribute App
// 3. 选择 "Copy App" 或 "Custom" > "Notarize"
// 4. Xcode 自动提交、等待、装订

// 或在 CI 中用 xcodebuild:
// xcodebuild -exportArchive \
//   -archivePath build/MyApp.xcarchive \
//   -exportOptionsPlist ExportOptions.plist \
//   -exportPath build/export \
//   -notarizeApp \
//   -notaryKey AuthKey.p8 \
//   -notaryKeyID ABC1234 \
//   -notaryIssuerID 12345678

CI 脚本中的完整公证流程

// CI 完整流程脚本
// #!/bin/sh

// // 1. 构建
// xcodebuild archive \
//   -scheme MyApp -archivePath build/MyApp.xcarchive

// // 2. 导出
// xcodebuild -exportArchive \
//   -archivePath build/MyApp.xcarchive \
//   -exportOptionsPlist ExportOptions.plist \
//   -exportPath build/export

// // 3. 创建 DMG
// hdiutil create -volName "MyApp" \
//   -srcfolder build/export/MyApp.app \
//   -ov -format UDZO build/MyApp.dmg

// // 4. 公证 + 装订(一步完成)
// notarytool submit build/MyApp.dmg \
//   --key $NOTARY_KEY_PATH \
//   --key-id $NOTARY_KEY_ID \
//   --issuer-id $NOTARY_ISSUER_ID \
//   --wait \
//   --staple

// // 5. 验证
// spctl --assess --type open --context context:primary-signature \
//   build/MyApp.dmg

最佳实践

CI 配置: 在 CI 中用 notarytool 配合 API Key 认证。把 .p8 文件存储为 CI 的 secure file,Key ID 和 Issuer ID 作为环境变量。公证通常需要 2-5 分钟,--wait 会阻塞直到完成。

本地开发: 用 Xcode 的 Distribute App 流程。如果用命令行,用 --apple-id + Keychain 存储的密码(@keychain:AC_PASSWORD)避免明文密码。

还有什么值得关注

  • notarytool 的输出是 JSON 格式,方便脚本解析。
  • 公证失败时 notarytool log 会显示具体的拒绝原因(如使用了不安全的 API)。
  • macOS Monterey 的 Gatekeeper 对未公证的 App 弹窗更频繁——公证不再是可选项。
WWDC 2021