代码签名 #
一、代码签名概述 #
1.1 为什么需要代码签名 #
| 原因 | 说明 |
|---|---|
| 安全验证 | 证明应用来源可信 |
| 防止篡改 | 确保应用未被修改 |
| 系统信任 | 避免安全警告 |
| 应用商店 | 上架必要条件 |
| 自动更新 | 某些平台要求签名 |
1.2 签名要求 #
| 平台 | 签名类型 | 说明 |
|---|---|---|
| macOS | Developer ID | 分发到 App Store 外 |
| macOS | App Store | 上架 App Store |
| Windows | 代码签名证书 | EV 或 OV 证书 |
二、macOS 签名 #
2.1 准备工作 #
text
1. Apple Developer 账号
2. 开发者证书
3. Provisioning Profile(可选)
2.2 获取证书 #
bash
# 查看本地证书
security find-identity -v -p codesigning
# 输出示例
# 1) ABC123... "Developer ID Application: Your Name (TEAM_ID)"
# 2) DEF456... "Developer ID Installer: Your Name (TEAM_ID)"
2.3 electron-builder 配置 #
yaml
# electron-builder.yml
mac:
hardenedRuntime: true
gatekeeperAssess: false
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
provisioningProfile: build/embedded.provisionprofile
type: distribution
target:
- target: dmg
arch:
- x64
- arm64
dmg:
sign: true
# 环境变量
# CSC_LINK - 证书文件路径 (.p12)
# CSC_KEY_PASSWORD - 证书密码
2.4 权限配置 #
xml
<!-- build/entitlements.mac.plist -->
<?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.automation.apple-events</key>
<true/>
</dict>
</plist>
2.5 公证(Notarization) #
yaml
# electron-builder.yml
afterSign: scripts/notarize.js
javascript
// scripts/notarize.js
const { notarize } = require('@electron/notarize');
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
const appName = context.packager.appInfo.productFilename;
return await notarize({
appBundleId: 'com.example.myapp',
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PASSWORD,
teamId: process.env.APPLE_TEAM_ID
});
};
2.6 签名命令 #
bash
# 设置环境变量
export CSC_LINK=/path/to/certificate.p12
export CSC_KEY_PASSWORD=your-password
export APPLE_ID=your@email.com
export APPLE_ID_PASSWORD=app-specific-password
export APPLE_TEAM_ID=your-team-id
# 构建
npm run build:mac
三、Windows 签名 #
3.1 准备工作 #
text
1. 代码签名证书(EV 或 OV)
2. 证书文件 (.pfx)
3. 证书密码
3.2 证书类型 #
| 类型 | 说明 | 信任度 |
|---|---|---|
| EV 证书 | 扩展验证 | 高,立即受信任 |
| OV 证书 | 组织验证 | 中,需要建立声誉 |
| 自签名 | 自己签发 | 低,仅用于测试 |
3.3 electron-builder 配置 #
yaml
# electron-builder.yml
win:
certificateFile: build/certificate.pfx
certificatePassword: ${env.WIN_CERT_PASSWORD}
signingHashAlgorithms:
- sha256
signAndEditExecutable: true
target:
- target: nsis
arch:
- x64
nsis:
oneClick: false
createDesktopShortcut: true
3.4 自定义签名脚本 #
javascript
// scripts/sign.js
const { execSync } = require('child_process');
const path = require('path');
module.exports = async function(configuration) {
const certPath = process.env.WIN_CERT_PATH;
const certPassword = process.env.WIN_CERT_PASSWORD;
// 使用 signtool 签名
const signTool = 'C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.19041.0\\x64\\signtool.exe';
const args = [
'sign',
'/fd', 'sha256',
'/a',
'/f', certPath,
'/p', certPassword,
'/tr', 'http://timestamp.digicert.com',
'/td', 'sha256',
configuration.path
];
execSync(`"${signTool}" ${args.join(' ')}`);
};
3.5 配置签名脚本 #
yaml
# electron-builder.yml
win:
sign: scripts/sign.js
3.6 签名命令 #
bash
# 设置环境变量
export WIN_CERT_PATH=/path/to/certificate.pfx
export WIN_CERT_PASSWORD=your-password
# 或在 PowerShell 中
$env:WIN_CERT_PATH = "C:\path\to\certificate.pfx"
$env:WIN_CERT_PASSWORD = "your-password"
# 构建
npm run build:win
四、CI/CD 签名 #
4.1 GitHub Actions - macOS #
yaml
# .github/workflows/build.yml
name: Build
on:
push:
tags:
- 'v*'
jobs:
mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Decode certificate
env:
CERTIFICATE_BASE64: ${{ secrets.MAC_CERT_BASE64 }}
run: |
echo $CERTIFICATE_BASE64 | base64 -d > certificate.p12
- name: Build
env:
CSC_LINK: ./certificate.p12
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: npm run build:mac
- uses: actions/upload-artifact@v4
with:
name: mac-build
path: dist/
4.2 GitHub Actions - Windows #
yaml
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Decode certificate
env:
CERTIFICATE_BASE64: ${{ secrets.WIN_CERT_BASE64 }}
run: |
[System.IO.File]::WriteAllBytes("certificate.pfx", [System.Convert]::FromBase64String($env:CERTIFICATE_BASE64))
- name: Build
env:
WIN_CERT_PATH: ./certificate.pfx
WIN_CERT_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}
run: npm run build:win
- uses: actions/upload-artifact@v4
with:
name: win-build
path: dist/
五、签名验证 #
5.1 macOS 验证 #
bash
# 验证签名
codesign --verify --deep --strict --verbose=2 /path/to/App.app
# 验证公证
spctl --assess --verbose /path/to/App.app
# 查看签名信息
codesign -dvvv /path/to/App.app
5.2 Windows 验证 #
powershell
# 使用 signtool 验证
signtool verify /pa /all /path/to/App.exe
# 查看签名信息
Get-AuthenticodeSignature /path/to/App.exe
六、常见问题 #
6.1 macOS 签名失败 #
bash
# 错误: no identity found
# 解决: 确保证书已正确安装
security find-identity -v -p codesigning
# 错误: code object is not signed
# 解决: 确保所有依赖都已签名
codesign --verify --deep --strict /path/to/App.app
6.2 Windows 签名失败 #
powershell
# 错误: SignTool Error: No certificates were found
# 解决: 确保证书路径和密码正确
# 错误: SignTool Error: An unexpected internal error has occurred
# 解决: 检查证书格式和签名参数
6.3 公证失败 #
bash
# 查看公证状态
xcrun notarytool history --apple-id your@email.com --password app-specific-password --team-id team-id
# 查看公证日志
xcrun notarytool log submission-id --apple-id your@email.com --password app-specific-password --team-id team-id
七、最佳实践 #
7.1 证书管理 #
markdown
- [ ] 安全存储证书文件
- [ ] 使用环境变量存储密码
- [ ] 定期更新证书
- [ ] 使用 CI/CD 密钥管理
7.2 签名检查清单 #
markdown
- [ ] 所有可执行文件已签名
- [ ] 安装包已签名
- [ ] macOS 已公证
- [ ] Windows 时间戳有效
- [ ] 签名验证通过
八、总结 #
8.1 核心要点 #
| 要点 | 说明 |
|---|---|
| macOS 签名 | Developer ID + 公证 |
| Windows 签名 | 代码签名证书 |
| CI/CD | 使用 Secrets 管理证书 |
| 验证 | 构建后验证签名 |
8.2 下一步 #
现在你已经掌握了代码签名,接下来让我们学习 多平台构建,深入了解跨平台构建的技巧!
最后更新:2026-03-28