更快更简单的 Mac App 公证
Faster and simpler notarization for Mac apps
2021年6月10日
一句话判断
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 弹窗更频繁——公证不再是可选项。