- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
5.6 KiB
NomiFun 桌面 macOS 代码签名 + 公证(Gatekeeper)
解决「把安装包发给别人,对方打开提示已损坏,无法打开」的问题。
这跟
updater/(自动更新签名)是两套完全不同的密钥,别混。本目录只管 Apple 的 Developer ID 签名 + 公证(notarization),让 App 在任何 Mac 上双击即开。
发布责任边界
本仓库只提供签名脚本和无密钥模板。正式发布必须使用发布方自己的 Apple Developer 账号、Developer ID 证书、App Store Connect API Key,以及独立的 自动更新签名密钥。不要把 fork、本地开发机或历史测试密钥当成官方发布凭据。
为什么会「已损坏」
默认 bun run build 产出的 App 只是 ad-hoc 签名(Signature=adhoc,无
TeamIdentifier)。别人下载/传输后,文件被打上 com.apple.quarantine 隔离标记;在
Apple 芯片 Mac 上,被隔离 + 未正规签名公证的 App,Gatekeeper 直接判为「已损坏」。
根治办法只有一个:用 Developer ID Application 证书签名 → 提交 Apple 公证 → staple 把公证票据钉进 App。之后任何人下载双击即开,无任何提示。
密钥绝不入库(本仓库的约定)
| 东西 | 放哪 | 是否入库 |
|---|---|---|
模板 .env.signing.example |
本目录 | ✅ 入库(无密钥) |
真实 .env.signing(身份名 / Key ID / 路径) |
本目录 | ❌ 已 gitignore |
App Store Connect API Key AuthKey_*.p8 |
apps/desktop/.tauri/ 或仓库外 |
❌ 已 gitignore |
| Developer ID 证书私钥 | macOS 登录钥匙串(不是文件) | ❌ 不在仓库里 |
构建脚本 scripts/desktop-build-signed.sh(可入库,无密钥)在运行时 source 本地
.env.signing 注入环境变量,Tauri 据此签名 + 公证。
一次性准备(在 Apple 侧)
1. 生成 Developer ID Application 证书并装进钥匙串
- 最简单:用 Xcode(Settings → Accounts → 选中团队 → Manage Certificates →
+→ Developer ID Application),它会自动装进登录钥匙串。 - 或 developer.apple.com → Certificates →
+→ Developer ID Application → 按引导用 CSR 生成 → 下载.cer双击导入钥匙串。 - 验证已就位:
把引号里的全名填到
security find-identity -v -p codesigning # 应能看到: "Developer ID Application: Your Name (TEAMID1234)".env.signing的APPLE_SIGNING_IDENTITY。
2. 生成 App Store Connect API Key(用于公证,推荐)
- App Store Connect → Users and Access → Integrations → Keys → 生成一个 Developer 角色的 Key。
- 下载
AuthKey_XXXX.p8(只能下载一次),放到apps/desktop/.tauri/(已 gitignore) 或仓库外的安全目录。 - 记下两个值填进
.env.signing:- Issuer ID = keys 表格上方那串 UUID →
APPLE_API_ISSUER - Key ID = 表格 "Key ID" 列 →
APPLE_API_KEY .p8路径 →APPLE_API_KEY_PATH
- Issuer ID = keys 表格上方那串 UUID →
不想用 API Key 也可用 Apple ID 方式:
APPLE_ID+APPLE_PASSWORD(App 专用密码, 在 appleid.apple.com 生成)+APPLE_TEAM_ID。三选一组,二者填其一即可。
本地配置 + 构建
# 1. 复制模板(真实文件不入库)
cp apps/desktop/signing/.env.signing.example apps/desktop/signing/.env.signing
# 2. 按上面拿到的值填写 .env.signing,并把 AuthKey_*.p8 放到对应路径
# 3. 出带签名 + 公证的安装包(公证联网,首次几分钟,耐心等)
bun run build:signed
产物在 target/release/bundle/{macos,dmg}/。构建末尾会先由 Tauri 公证并 staple
.app,随后脚本会对最终分发用的 .dmg 再提交一次公证并 staple。
验证(发出去前自检)
APP=target/release/bundle/macos/NomiFun.app
DMG=target/release/bundle/dmg/NomiFun_0.1.0_aarch64.dmg
codesign -dvv "$APP" # 期望: Authority=Developer ID Application: ...
codesign --verify --deep --strict -v "$APP" # 期望: valid on disk / satisfies Designated Requirement
xcrun stapler validate "$APP" # 期望: The validate action worked!
spctl -a -vvv "$APP" # 期望: source=Notarized Developer ID → accepted
codesign --verify --strict -v "$DMG" # 期望: valid on disk / satisfies Designated Requirement
xcrun stapler validate "$DMG" # 期望: The validate action worked!
spctl -a -vvv -t open --context context:primary-signature "$DMG" # 期望: accepted
这些验证全过,就可以放心分发 DMG——别人下载双击即开,不再报「已损坏」。
常见报错
The binary is not signed with a valid Developer ID certificate:钥匙串里没有 Developer ID Application 证书,或APPLE_SIGNING_IDENTITY名字写错。重看准备步骤 1。ambiguous (matches ... login.keychain-db and ... System.keychain):同名 Developer ID Application 证书同时存在于多个钥匙串。删除多余副本,或把security find-identity -v -p codesigning输出中的 SHA-1 哈希填入APPLE_SIGNING_IDENTITY。APPLE_API_KEY_PATH 必须指向 AuthKey_*.p8:APPLE_API_KEY_PATH是 App Store Connect API Key 路径,不要填 Developer ID.p12证书路径。- 公证被拒 /
Invalid状态:多为「未启用 hardened runtime」或缺 entitlements。 Tauri 用 Developer ID 签名时默认开启 hardened runtime;若 App 需要特殊能力(JIT、 加载第三方动态库等),在tauri.conf.json的bundle.macOS.entitlements指定 plist。 查看具体原因:xcrun notarytool log <submission-id> --key ... --key-id ... --issuer ...。 - 只签名没公证:别人会看到「无法验证开发者」(不是「已损坏」)。补上公证变量即可。