本文目标 :帮助你把本地的 Electron 应用打包成 macOS 的
.dmg,并做到打开不再被 Gatekeeper 拦截(不再提示"来自身份不明的开发者/无法验证是否含有恶意软件")。适用对象:个人开发者 & 小团队。
🧩 一、问题场景
当你满心欢喜地将精心开发的 Electron 应用打包分发给用户,却接到反馈:在 macOS 上无法打开,系统弹窗冷冰冰地提示"无法验证开发者",文件被直接移入废纸篓。
如果这个场景让你感同身受,那么你正遭遇 macOS 强大的 Gatekeeper 安全机制。别担心,这不是你的代码有问题,而是应用缺少一张合法的"身份证"------苹果的开发者签名。本指南将带你一步步为你的 Electron 应用完成代码签名,彻底告别"不安全软件"的提示。
🚀 二、从 0 到 1 的整体流程(必做)
| 阶段 | 目标 | 核心工具 | 产出 |
|---|---|---|---|
| 1. 证书准备 | 让 Apple 识别你的身份 | Apple Developer、Keychain | Developer ID Application 证书 |
| 2. 签名 | 给 .app 加数字签名 |
electron-builder/codesign |
已签名的 .app/.dmg |
| 3. 公证 | 让 Apple 云端扫描并背书 | notarytool |
Accepted 的公证结果 |
| 4. staple | 把公证票据装订回产物 | xcrun stapler |
可离线验证的 .app/.dmg |
| 5. 验证 | 本地三板斧校验 | codesign/spctl/stapler |
放心分发 |
🧱 三、准备工作
1) Apple Developer 账号
- 开通 developer.apple.com 开发者计划($99/年)。
- 建议以组织(Organization)形式创建,便于分权协作。
2) 正确的证书类型
不要 用
Mac Development/Apple Development。它们只用于调试。
- ✅ 需要:
Developer ID Application(给.app/.dmg签名用) - (可选):
Developer ID Installer(制作.pkg安装器才用)
可能的问题:
- 创建页里没有 "Developer ID Application" → 你不是 Account Holder/Admin 或选错了 Team。让 Account Holder 升级角色或代为创建。
3) 获取 Developer ID Application 证书的详细步骤
步骤 3.1:在本地生成证书签名请求(CSR)
- 打开 macOS 的 钥匙串访问 (Keychain Access)
- 菜单栏选择:钥匙串访问 → 证书助理 → 从证书颁发机构请求证书...
- 填写信息:
- 用户电子邮件地址 :填写你的 Apple ID 邮箱(例如:
developer@example.com) - 常用名称 :填写你的名字或公司名(例如:
Example Developer) - CA 电子邮件地址:留空
- 选择:存储到磁盘
- 勾选:让我指定密钥对信息
- 用户电子邮件地址 :填写你的 Apple ID 邮箱(例如:
- 点击 继续
- 在密钥对信息中:
- 密钥大小:2048 位
- 算法:RSA
- 保存 CSR 文件(例如:
CertificateSigningRequest.certSigningRequest)到桌面或其他位置
⚠️ 注意 :生成 CSR 后,钥匙串中会自动生成一个对应的私钥,请勿删除!证书下载后需要与这个私钥配对使用。
步骤 3.2:在 Apple Developer 网站申请证书
- 登录 Apple Developer
- 进入:Certificates, Identifiers & Profiles → Certificates
- 点击左上角 + 按钮创建新证书
- 选择证书类型:
- 滚动到 Production 区域
- 选择 Developer ID Application
- 点击 Continue
- 上传 CSR:
- 点击 Choose File
- 选择刚才生成的
.certSigningRequest文件 - 点击 Continue
- 下载证书:
- 点击 Download 下载
.cer文件(例如:developerID_application.cer)
- 点击 Download 下载
步骤 3.3:将证书导入钥匙串
-
双击下载的
.cer文件 -
系统会自动打开钥匙串访问并导入证书
-
验证导入成功:
- 打开 钥匙串访问
- 选择 登录 钥匙串
- 在 我的证书 分类中查找
- 应该能看到类似
Developer ID Application: Your Name (TEAM_ID)的证书 - 展开证书,确认下方有对应的私钥(🔑 图标)
-
验证证书可用性:
bash
# 列出所有可用的签名身份
security find-identity -v -p codesigning
# 应该能看到类似输出:
# 1) ABCD1234EFGH5678IJKL9012MNOP3456QRST7890 "Developer ID Application: Your Name (TEAM_ID)"
⚠️ 常见问题:
- 证书导入后没有私钥:说明 CSR 不是在本机生成的,或者私钥被删除了。需要重新生成 CSR 并申请新证书。
- 无法在钥匙串中找到证书 :确认导入到了 登录 钥匙串而不是 系统 钥匙串。
4) App Store Connect API Key(用于公证)
步骤 4.1:创建 API Key
- 登录 App Store Connect
- 导航路径:Users and Access → Keys → App Store Connect API
- 点击页面中的 + 按钮(或 Generate API Key)
- 填写信息:
- Name :给 Key 起个名字(例如:
Notarization Key或CI/CD Key) - Access :选择 Developer(公证只需要 Developer 权限)
- Name :给 Key 起个名字(例如:
- 点击 Generate
🔐 权限说明:
- Admin:最高权限,可管理所有内容
- Developer:可提交公证请求(推荐用于自动化)
- 如果只做公证,选择 Developer 即可
步骤 4.2:下载和保存 API Key
-
创建成功后,页面会显示:
- Key ID (例如:
KEYID12345)← 记录此值 - Issuer ID (页面顶部,例如:
00000000-1111-2222-3333-444444444444)← 记录此值 - Download API Key 按钮
- Key ID (例如:
-
立即下载
.p8文件:- 点击 Download API Key
- 文件名格式:
AuthKey_XXXXXXXX.p8(例如:AuthKey_ABCD1234.p8) - ⚠️ 此文件只能下载一次! 下载后妥善保管,丢失需要重新创建 Key
-
保存到项目目录:
bash
# 建议的项目结构
your-electron-app/
├── build/
│ ├── AuthKey_ABCD1234.p8 # API Key 文件
│ ├── entitlements.mac.plist # 权限配置
│ └── afterSign.js # 可选:自动补签脚本
├── electron-builder.yml
└── package.json
- 安全建议 :
- 将
.p8文件添加到.gitignore,避免提交到 Git - 在 CI/CD 中使用环境变量或加密存储
- 将
bash
# .gitignore 示例
build/*.p8
*.p8
步骤 4.3:记录三项关键信息(mock 示例)
完成后,你应该拥有以下三项信息:
| 项目 | 说明 | Mock 示例 |
|---|---|---|
| API Key 文件 | .p8 文件路径 |
build/AuthKey_ABCD1234.p8 |
| Key ID | 在 App Store Connect 中显示 | KEYID12345 |
| Issuer ID | 在 App Store Connect 顶部显示 | 00000000-1111-2222-3333-444444444444 |
可能的问题:
- App Store Connect看不到 Keys 菜单 → 你不是 Account Holder ;请让 Account Holder 创建并分享
.p8/KeyID/IssuerID给你。 - .p8 文件丢失 → 无法重新下载,只能删除旧 Key 并创建新的。
- 权限不足 → 如果公证失败提示权限问题,尝试使用 Admin 权限的 Key。
⚙️ 四、项目配置(使用 electron-builder)
1) 完整的项目结构
bash
your-electron-app/
├── build/ # 构建资源目录
│ ├── AuthKey_ABCD1234.p8 # ⚠️ API Key(不要提交到 Git)
│ ├── entitlements.mac.plist # macOS 权限配置
│ ├── afterSign.js # 可选:签名后处理脚本
│ └── icon.icns # 应用图标
├── src/ # 源代码
│ ├── main.js # Electron 主进程
│ └── renderer/ # 渲染进程
├── scripts/
│ └── check-sign.sh # 签名自检脚本
├── .env # ⚠️ 环境变量(不要提交到 Git)
├── .gitignore
├── electron-builder.yml # electron-builder 配置
└── package.json
2) package.json 配置(完整示例)
json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "My Electron App",
"main": "src/main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder",
"build:mac": "electron-builder --mac",
"build:mac:dmg": "electron-builder --mac dmg",
"build:dir": "electron-builder --mac --dir",
"pack": "electron-builder --dir",
"dist": "electron-builder",
"check-sign": "bash scripts/check-sign.sh dist/mac/MyApp.app"
},
"build": {
"appId": "com.example.myapp",
"productName": "MyApp",
"extends": "electron-builder.yml"
},
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.9.1"
},
"author": "Your Name",
"license": "MIT"
}
💡 脚本说明:
build:mac:构建 macOS 版本(包括 dmg 和 zip)build:mac:dmg:只构建 dmgbuild:dir:不打包,只构建目录(用于测试签名)check-sign:运行签名自检脚本
3) electron-builder.yml 配置(三种方式)
方式 A:直接硬编码(适合个人本地开发)
electron-builder.yml(mock 示例)
yaml
appId: com.example.myapp
productName: MyApp
mac:
category: public.app-category.productivity
target:
- dmg
hardenedRuntime: true
gatekeeperAssess: false
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
notarize:
tool: "notarytool"
appleApiKey: "build/AuthKey_ABCD1234.p8" # mock:你的 .p8 文件路径
appleApiKeyId: "KEYID12345" # mock:你的 Key ID
appleApiIssuer: "00000000-1111-2222-3333-444444444444" # mock:你的 Issuer ID
dmg:
title: "${productName} ${version}"
icon: "build/icon.icns"
background: "build/background.png" # 可选:DMG 背景图
window:
width: 540
height: 380
contents:
- x: 140
y: 200
type: "file"
- x: 400
y: 200
type: "link"
path: "/Applications"
⚠️ 注意:此方式会把敏感信息写在配置文件中,不要提交到公开仓库!
方式 B:使用环境变量(推荐用于 CI/CD)
electron-builder.yml
yaml
appId: com.example.myapp
productName: MyApp
mac:
category: public.app-category.productivity
target:
- dmg
hardenedRuntime: true
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
notarize:
tool: "notarytool"
# 留空,通过环境变量传递
.env(本地开发,不要提交到 Git)
bash
# Apple Developer 证书相关
APPLE_ID=developer@example.com
APPLE_ID_PASSWORD=your-app-specific-password
# App Store Connect API Key(推荐方式)
APPLE_API_KEY=build/AuthKey_ABCD1234.p8
APPLE_API_KEY_ID=KEYID12345
APPLE_API_ISSUER=00000000-1111-2222-3333-444444444444
# 或者使用 base64 编码的 .p8 内容(CI/CD 推荐)
# APPLE_API_KEY_BASE64=<base64-encoded-content>
使用环境变量的构建命令
bash
# 方式 1:使用 dotenv
npm install dotenv-cli --save-dev
npx dotenv electron-builder --mac dmg
# 方式 2:直接导出环境变量
export APPLE_API_KEY_ID="KEYID12345"
export APPLE_API_ISSUER="00000000-1111-2222-3333-444444444444"
export APPLE_API_KEY="build/AuthKey_ABCD1234.p8"
npm run build:mac:dmg
方式 C:使用 electron-builder 的环境变量自动识别
electron-builder 会自动读取以下环境变量:
| 环境变量 | 说明 | Mock 示例 |
|---|---|---|
APPLE_API_KEY |
API Key 文件路径或 base64 内容 | build/AuthKey_ABCD1234.p8 |
APPLE_API_KEY_ID |
Key ID | KEYID12345 |
APPLE_API_ISSUER |
Issuer ID | 00000000-1111-2222-3333-444444444444 |
APPLE_ID |
Apple ID 邮箱(旧方式) | developer@example.com |
APPLE_APP_SPECIFIC_PASSWORD |
App-specific password(旧方式) | xxxx-xxxx-xxxx-xxxx |
💡 推荐:在 CI/CD 中使用环境变量方式,避免敏感信息泄露。
build/entitlements.mac.plist(建议最小集)
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key><true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
<key>com.apple.security.cs.disable-library-validation</key><true/>
<key>com.apple.security.app-sandbox</key><false/>
<key>com.apple.security.network.client</key><true/>
</dict>
</plist>
可能的问题:
hardenedRuntime未启用会导致公证失败。entitlements缺项(JIT/unsigned-executable-memory)会导致 Electron 无法正常运行或公证失败。
4) CI/CD 集成示例
GitHub Actions 配置示例
.github/workflows/build-macos.yml
yaml
name: Build macOS App
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
jobs:
build:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
# 准备签名证书(方式1:使用 .p12 文件)
- name: Import signing certificate
env:
CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE_BASE64 }}
CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
# 创建临时 keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# 导入证书
echo $CERTIFICATE_BASE64 | base64 --decode > certificate.p12
security import certificate.p12 -k $KEYCHAIN_PATH -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# 设置为默认 keychain
security list-keychain -d user -s $KEYCHAIN_PATH
# 清理
rm -f certificate.p12
# 准备 API Key
- name: Prepare API Key
env:
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
run: |
mkdir -p build
echo $APPLE_API_KEY_BASE64 | base64 --decode > build/AuthKey.p8
# 构建应用
- name: Build and notarize
env:
APPLE_API_KEY: build/AuthKey.p8
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: npm run build:mac:dmg
# 上传构建产物
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: macos-dmg
path: dist/*.dmg
准备 GitHub Secrets
在 GitHub 仓库设置中添加以下 Secrets(Settings → Secrets and variables → Actions):
| Secret Name | 说明 | 如何获取 |
|---|---|---|
APPLE_API_KEY_BASE64 |
.p8 文件的 base64 编码 | base64 -i AuthKey_ABCD1234.p8 |
APPLE_API_KEY_ID |
Key ID | 从 App Store Connect 复制 |
APPLE_API_ISSUER |
Issuer ID | 从 App Store Connect 复制 |
MACOS_CERTIFICATE_BASE64 |
证书 .p12 的 base64 | base64 -i certificate.p12 |
MACOS_CERTIFICATE_PASSWORD |
.p12 文件的密码 | 导出证书时设置的密码 |
如何导出 .p12 证书:
bash
# 1. 在钥匙串访问中找到证书
# 2. 右键点击证书 → 导出
# 3. 文件格式选择 "个人信息交换 (.p12)"
# 4. 设置密码(用于 CI/CD)
# 5. 保存后,转换为 base64:
base64 -i certificate.p12 | pbcopy # 复制到剪贴板
🔐 五、安全管理最佳实践
1) .gitignore 配置(必做)
bash
# .gitignore
# macOS 构建相关
*.p8
*.p12
*.cer
*.mobileprovision
build/*.p8
build/AuthKey_*.p8
# 环境变量
.env
.env.local
.env.production
# 构建产物
dist/
out/
release/
# 临时文件
*.dmg
*.zip
*.pkg
2) 敏感信息管理建议
| 场景 | 推荐方案 | 不推荐 |
|---|---|---|
| 本地开发 | .env 文件 + .gitignore |
硬编码在配置文件中 |
| 团队协作 | 1Password / Bitwarden 等密码管理器 | 通过聊天工具传递 |
| CI/CD | GitHub Secrets / GitLab Variables | 明文存储在配置文件 |
| 多环境 | 不同的 API Key(dev/prod) | 共用同一个 Key |
3) API Key 权限最小化
yaml
# 生产环境:只给 Developer 权限
production_key:
access: Developer
scope: Notarization only
# 开发环境:可以给更多权限方便调试
development_key:
access: Developer
scope: Notarization + TestFlight
4) 证书过期管理
bash
# 检查证书有效期
security find-identity -v -p codesigning | grep "Developer ID Application"
openssl x509 -inform DER -in developerID_application.cer -noout -dates
# 证书有效期通常为 5-7 年,提前 1 个月续签
5) CI/CD 安全检查清单
- ✅ 确保
.p8和.p12文件已添加到.gitignore - ✅ 使用 base64 编码存储在 CI Secrets 中
- ✅ 构建后自动清理临时 keychain
- ✅ 不在日志中打印敏感信息
- ✅ 使用短期有效的临时 keychain
- ✅ 限制 CI/CD 的访问权限(只给必要的人员)
🏗️ 六、打包与自动公证
bash
# 安装依赖(示例)
npm i -D electron-builder
# 一条龙:签名 + 提交公证 + staple
npx electron-builder --mac dmg
公证状态查看(任选其一)
bash
# 查看历史
xcrun notarytool history \
--key build/AuthKey_ABCD1234.p8 \
--key-id KEYID12345 \
--issuer 00000000-1111-2222-3333-444444444444
# 查看指定请求
xcrun notarytool info <REQUEST_ID> \
--key build/AuthKey_ABCD1234.p8 \
--key-id KEYID12345 \
--issuer 00000000-1111-2222-3333-444444444444
当状态为 Accepted 后,若未自动 staple:
bash
xcrun stapler staple "dist/mac/MyApp.app"
xcrun stapler staple "dist/mac/MyApp.dmg"
🔎 七、公证前自检脚本(强烈推荐)
在提交公证前,先扫描 .app 内所有 Mach-O 文件并验证签名,可大幅提高一次通过率。
scripts/check-sign.sh
bash
#!/usr/bin/env bash
set -euo pipefail
APP="${1:-dist/mac/MyApp.app}"
echo "== 1) 枚举 Mach-O 可执行体 =="
find "$APP" -type f -print0 \
| xargs -0 file \
| grep -E 'Mach-O|dynamically linked shared library|bundle with Mach-O' \
| sed 's/:.*$//' \
| tee /tmp/macho.list
echo
echo "== 2) 对每个 Mach-O 执行严格签名校验 =="
rc=0
while IFS= read -r f; do
echo "-> $f"
if ! codesign --verify --strict --verbose=4 "$f" >/dev/null 2>&1; then
echo "⚠️ 未签名或签名无效: $f"
rc=1
fi
done < /tmp/macho.list
echo
echo "== 3) 校验 .app 整体签名 =="
if ! codesign --verify --strict --deep --verbose=4 "$APP"; then
echo "⚠️ .app 整体签名校验失败"
rc=1
fi
echo
echo "== 4) Gatekeeper 预检 =="
if ! spctl -a -vv "$APP"; then
echo "⚠️ Gatekeeper 预检未通过"
rc=1
fi
echo
echo "== 5) 列出解包原生模块(高风险区) =="
if [ -d "$APP/Contents/Resources/app.asar.unpacked" ]; then
find "$APP/Contents/Resources/app.asar.unpacked" -type f \( -name "*.node" -o -name "*.dylib" -o -name "*.jnilib" -o -name "*.so" \) -print
fi
echo
echo "== 6) 检查主可执行体是否启用 Hardened Runtime =="
MAIN_BIN="$APP/Contents/MacOS/$(defaults read "$APP/Contents/Info" CFBundleExecutable 2>/dev/null || echo '')"
if [ -n "$MAIN_BIN" ] && ! codesign -d --verbose=4 "$MAIN_BIN" 2>&1 | grep -qi 'runtime'; then
echo "⚠️ 未检测到 hardened runtime 选项(需要 mac.hardenedRuntime: true 且使用 --options runtime)"
rc=1
fi
echo
if [ "$rc" -ne 0 ]; then
echo "❌ 自检未通过,请根据上方提示修复后再提交公证"
exit 1
else
echo "✅ 自检通过,建议开始 Notarization"
fi
使用方式
bash
chmod +x scripts/check-sign.sh
scripts/check-sign.sh dist/mac/MyApp.app
🛠️ 八、自动补签(afterSign.js,可选)
若第三方依赖解包出了 .dylib/.jnilib/.node 且未签名,可在 electron-builder.yml 中挂载:
yaml
afterSign: build/afterSign.js
build/afterSign.js(简版示例)
js
const { execFile } = require("child_process");
const fs = require("fs");
const path = require("path");
function run(cmd, args) {
return new Promise((resolve, reject) => {
execFile(cmd, args, (err, stdout, stderr) => {
if (err) return reject(new Error(stderr || stdout || err?.message));
resolve();
});
});
}
function walk(dir, exts, out) {
if (!fs.existsSync(dir)) return;
for (const name of fs.readdirSync(dir)) {
const p = path.join(dir, name);
const st = fs.lstatSync(p);
if (st.isSymbolicLink()) continue;
if (st.isDirectory()) walk(p, exts, out);
else if (exts.some(e => p.endsWith(e))) out.push(p);
}
}
module.exports = async (context) => {
if (context.electronPlatformName !== "darwin") return;
const appPath = path.join(context.appOutDir, `${context.packager.appInfo.productFilename}.app`);
const entitlements = path.join(process.cwd(), "build/entitlements.mac.plist");
const identity = "Developer ID Application";
const targets = [];
walk(path.join(appPath, "Contents/Frameworks"), [".dylib", ".jnilib", ".node", ".so"], targets);
walk(path.join(appPath, "Contents/Resources", "app.asar.unpacked"), [".dylib", ".jnilib", ".node", ".so"], targets);
for (const f of targets) {
try {
// 已签名会通过;未签名会抛异常,进入 catch 重新签名
await run("codesign", ["--verify", "--strict", "--verbose=2", f]);
} catch {
console.log("🔧 重新签名:", f);
await run("codesign", ["--force", "--sign", identity, "--timestamp", "--options", "runtime", "--entitlements", entitlements, f]);
}
}
};
🧪 九、三板斧验证(公证后)
bash
codesign -dv --verbose=4 "dist/mac/MyApp.app" # Authority 应为 Developer ID Application: ...
spctl -a -vv "dist/mac/MyApp.app" # 应为 accepted
xcrun stapler validate "dist/mac/MyApp.dmg" # The validate action worked!
🔁 十、何时需要重新公证?
- 改动
.app内任何内容 (包括图标.icns、Info.plist、资源、代码、Electron 版本等)→ ✅ 需要重新签名+公证。 - 只改
.dmg的外观/背景 (不改.app)→ ❌ 无需重公证,但建议对新.dmg再次stapler staple。
🧱 十一、常见坑位速查表
| 现象/报错 | 核心原因 | 处理 |
|---|---|---|
| 看不到 "Keys" 创建入口 | 不是 Account Holder | 让 Account Holder 代建 API Key |
-25294 无法导入证书 |
证书与私钥不匹配或钥匙串未解锁 | 重新用本机 CSR 申请,或导入 .p12 |
Archive contains critical validation errors |
包内存在未签名的 .dylib/.jnilib/.node |
自检脚本找出 → afterSign.js 补签或排除 |
does not have a ticket stapled |
未装订公证票据 | xcrun stapler staple 到 .app/.dmg |
长时间 In Progress |
队列繁忙或网络慢 | 稍等几分钟或重试 |
| 首次运行仍弹提示 | 其实公证未通过/未装订 | 走三板斧验证,确保 Accepted + staple |
🧭 十二、复盘总结
本文从问题场景 出发,按证书 → 签名 → 公证 → 装订 → 验证 的顺序,解决 Electron .dmg 在 macOS 上的合法性问题,并提供:
- ✅ 详细的证书获取步骤:从生成 CSR、申请证书到导入钥匙串的完整流程;
- ✅ API Key 获取指南:创建、下载和管理 App Store Connect API Key 的详细说明;
- ✅ 三种 Electron 项目配置方式:硬编码、环境变量、CI/CD 集成的完整示例;
- ✅ GitHub Actions CI/CD 配置:包括证书导入、秘密管理的生产级配置;
- ✅ 安全管理最佳实践:.gitignore 配置、敏感信息管理、权限最小化等;
- ✅ 可直接落地的
electron-builder.yml+entitlements.mac.plist模板(均为 mock 示例); - ✅ 公证前自检脚本:在提交前发现未签名的原生库;
- ✅ 自动补签 的
afterSign.js(适用于第三方原生依赖); - ✅ 多个常见坑点与权限问题(Keys 不可见、证书导入失败等)的定性与修复。
按本文执行后,你的
.dmg将具备签名 + 公证 + 票据装订 的完整链路,用户安装时不再被系统拦截。无论是本地开发还是 CI/CD 自动化,都能找到对应的解决方案。祝分发顺利!