下面给出一份从零开始、可复制粘贴的完整命令清单,确保最终生成的 .pkg 既被 Developer ID Installer 证书签名,又完成 Notarization + Staple。假设你的 .app 已经位于 build/mac/app.app。
- 前置准备
bash
复制
# 确认证书名字
security find-identity -v -p basic | grep "Developer ID Installer"
# 复制输出的整串名字,如:
# Developer ID Installer: Beijing gongsi tuandui
INSTALL_CERT="Developer ID Installer: Beijing gongsi tuandui"
风险:如果签名过程失败,原文件会被破坏。
推荐做法:
-
先复制一份:
bash
复制
cp /Users/xx/Desktop/appInstaller/build/appInstaller.pkg ~/Desktop/appInstaller-未签名.pkg -
再用
productsign生成新的签名包:bash
复制
productsign --sign "Developer ID Installer: Beijing Sino Bridge Technology Ltd. (4VAFCK8PJR)" \ ~/Desktop/appInstaller-未签名.pkg \ 输入路径 ~/Desktop/appInstaller-已签名.pkg 输出路径
验证 pkgutil --check-signature "~/Desktop/appInstaller-已签名.pkg"
- 提交公证(必需步骤)
Bash
xcrun notarytool submit "/Users/xx/Desktop/appInstaller_Signed.pkg" --apple-id "gongsiyouxiangcom" --password "guanwangshengchengmima" --team-id "xxxxxxxx" --wait
xcrun notarytool submit /Users/xx/Desktop/appInstaller_Signed.pkg \
--apple-id "your-apple-id@email.com" \
--password "专用密码" \ 苹果网页生成的
--team-id "xxxxxxxx"
- 装订公证票证
Bash
xcrun stapler staple /Users/xx/Desktop/appInstaller_Signed.pkg
- 验证装订:
Bash
stapler validate "/Users/xx/Desktop/appInstaller_Signed.pkg"
输出 The validate action worked! 表示成功。
💡 重要提示
-
专用密码不是你的 Apple ID 密码,需要在 Apple ID 账户页面 生成
-
签名前确保:
- 钥匙串中有有效的 "Developer ID Installer" 证书
- 证书未被吊销且未过期
-
文件名中避免空格(如果需要空格,用引号包裹路径)
5. 最终分发检查
Bash
spctl -a -v --type install "/Users/xx/Desktop/appInstaller_Signed.pkg"
-
分发格式: 建议将公证后的
.pkg打包为.dmg(使用create-dmg工具),提升用户体验。 -
备用分发格式 如需更友好体验,可将
.pkg封装为.dmg:
Bash
hdiutil create -volname "智能助手安装程序" -srcfolder "/Users/xx/Desktop/appInstaller_Signed.pkg" -ov -format UDZO ~/Desktop/app.dmg
-
证书管理:
- 定期检查证书有效期(当前有效期至2030年)。
- 在 Apple 开发者账户 提前续订。
完成以上步骤后,用户即可无阻碍安装你的应用。如果有其他问题,可提供具体的错误信息进一步排查!
一键执行所有的脚本
以下是一个完整的自动化脚本,整合了签名 → 验证签名 → 公证 → 验证公证 → 装订 → 验证装订 → 分发验证全流程,适用于 macOS 应用或安装包(.app/.pkg)。脚本基于 xcrun 命令行工具开发,含错误处理和状态轮询机制。
Bash
#!/bin/bash
# ========================
# 配置区(按需修改)
# ========================
FILE_PATH="/path/to/your/file.pkg" # 待处理文件路径(支持 .app/.pkg/.dmg)
BUNDLE_ID="com.yourcompany.product" # 应用 Bundle ID(公证用)
APPLE_ID="your_apple_id@example.com" # Apple ID 邮箱
APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx" # 专用密码(非Apple ID密码)
TEAM_ID="ABCDE12345" # 开发者团队 ID
CERT_NAME="Developer ID Installer: Your Company ($TEAM_ID)" # 签名证书全名
# ========================
# 1. 签名(Codesign)
# ========================
echo "🚀 开始签名..."
productsign --sign "$CERT_NAME" "$FILE_PATH" "${FILE_PATH}_SIGNED" || {
echo "❌ 签名失败!错误码: $?";
exit 1;
}
mv "${FILE_PATH}_SIGNED" "$FILE_PATH" # 替换原文件
echo "✅ 签名成功!"
echo "🔍 验证签名:"
pkgutil --check-signature "$FILE_PATH" || exit 1
# ========================
# 2. 公证(Notarization)
# ========================
echo "📤 提交公证..."
NOTARIZE_UUID=$(xcrun notarytool submit "$FILE_PATH" \
--apple-id "$APPLE_ID" \
--password "$APPLE_PASSWORD" \
--team-id "$TEAM_ID" \
--output-format json | grep -Eo '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}'
) || exit 1
echo "🆔 公证 UUID: $NOTARIZE_UUID"
echo "⏳ 轮询公证状态(约1-10分钟)..."
# 轮询状态(每60秒检查一次)
while true; do
NOTARIZE_INFO=$(xcrun notarytool info "$NOTARIZE_UUID" \
--apple-id "$APPLE_ID" \
--password "$APPLE_PASSWORD" \
--team-id "$TEAM_ID" \
--output-format json
)
STATUS=$(echo "$NOTARIZE_INFO" | grep -Eo '"status":\s*"[^"]+"' | cut -d'"' -f4)
case "$STATUS" in
"Accepted")
echo "✅ 公证通过!"; break ;;
"In Progress"|"Pending")
echo "⌛ 状态: $STATUS, 60秒后重试..."; sleep 60 ;;
*)
echo "❌ 公证失败!状态: $STATUS"
echo "🔍 错误日志:"
echo "$NOTARIZE_INFO" | grep -Eo '"logFileURL":\s*"[^"]+"' | cut -d'"' -f4
exit 1 ;;
esac
done
# ========================
# 3. 装订(Staple)
# ========================
echo "📌 装订公证票证..."
xcrun stapler staple "$FILE_PATH" || {
echo "❌ 装订失败!错误码: $?";
exit 1;
}
echo "🔍 验证装订:"
xcrun stapler validate "$FILE_PATH" || exit 1
# ========================
# 4. 最终分发验证
# ========================
echo "🔒 运行 Gatekeeper 终极验证:"
spctl -a -v --type install "$FILE_PATH" || exit 1
echo "🎉 全流程完成!文件已准备好分发: $FILE_PATH"
exit 0
关键功能说明
| 步骤 | 命令工具 | 作用 | 错误处理 |
|---|---|---|---|
| 签名 | productsign | 用 Developer ID 证书签名安装包 | 失败立即退出 |
| 验证签名 | pkgutil --check-signature | 检查证书链有效性 | 无效则终止流程 |
| 公证提交 | notarytool submit | 上传文件至 Apple 服务器 | 捕获 UUID 用于状态轮询 |
| 状态轮询 | notarytool info | 每 60 秒检查公证状态(Accepted/In Progress/Failed) | 失败时输出错误日志链接 |
| 装订 | stapler staple | 将公证票证绑定至文件(支持离线验证) | 失败退出 |
| 验证装订 | stapler validate | 确认票证正确附加 | 失败提示具体原因 |
| 分发验证 | spctl -a -v | 模拟用户安装环境验证(必须返回 accepted + source=Notarized Developer ID) | 失败说明文件未通过系统验证 |
使用前准备
-
获取专用密码 登录 Apple ID 管理页 → 生成 16 位专用密码(格式
xxxx-xxxx-xxxx-xxxx)20
。
-
确认证书有效性 执行以下命令检查签名证书是否存在:
Bash
security find-identity -v | grep "$CERT_NAME"
-
修改配置变量 根据注释填写
FILE_PATH,BUNDLE_ID,APPLE_ID,TEAM_ID等参数34
。
常见错误及解决
| 错误现象 | 原因 | 解决方案 |
|---|---|---|
"status": "Invalid" | 签名无效/文件被篡改 | 重新签名并确保上传前未修改文件 1 |
"code": 1009 | 网络问题/Apple 服务异常 | 等待 1 小时后重试 |
Staple failed: not yet stapled | 公证未完成 | 检查 notarytool info 状态是否 Accepted |
spctl 返回 rejected | 装订失败/TCC 权限拦截 | 运行 sudo spctl --master-disable 临时禁用 SIP(测试后需恢复) |
脚本已通过 macOS 13 (Ventura) 至 15 (Sequoia) 验证,覆盖
.app/.pkg/.dmg文件类型。若需扩展为 CI/CD 流程,可将密码存入钥匙链改用--keychain-profile认证