Appearance
在 macOS 上使用 Electron Builder 集成 VSign 云签名
适用于已开通 TrustAsia 云签服务,需要在 macOS 上通过 Electron Builder 为 Windows 应用程序和 NSIS 安装包自动签名的用户。整个流程为:准备云签信息 → 安装操作员证书 → 配置环境变量和签名脚本 → 构建并签名 → 检查结果。
本文以 Electron Builder 26.18.0 为配置示例,通过 ssigncode-mac-x86_64 执行 SHA256 单签名并附加时间戳。签名对象为 Windows 程序;macOS 原生应用的 Apple 签名和公证请另行配置。
1. 准备工具和签名信息
开始前,请确认 Electron 项目可以正常构建,云签订单中的代码签名证书已签发,并准备好可联网的 Mac 和 macOS 版签名工具。工具安装包可联系技术支持获取。
设置 PIN、添加操作员、安装操作员证书和获取证书指纹的操作,沿用 在 macOS 上使用云签工具进行代码签名(云签)。若已在当前构建用户下完成安装,且现有证书和参数仍可正常使用,可直接复用。
设置 PIN 并获取安装 token
- 进入目标订单的 云签详情 → 签名安全设置,设置签名 PIN;已设置过的使用当前 PIN。
- 进入 云签详情 → 操作员管理,点击“添加”。
- 在新增操作员记录的 Token 列复制安装 token,供下一步安装操作员证书使用。


| 信息 | 获取位置与用途 |
|---|---|
| 签名 PIN | 在 云签详情 → 签名安全设置 中设置,对应安装和签名命令的 --key_pin。 |
| 操作员安装 token | 在 云签详情 → 操作员管理 中添加操作员后获取,对应安装命令的 --auth。 |
| 操作员证书指纹 | 安装成功后,在 操作员管理 → 证书哈希 列获取,对应签名命令的 -u。 |
| 签名证书指纹 | 在 订单详情 → 证书详情 → 指纹 中获取,对应签名命令的 --cert_hash。 |
| 签名服务器地址 | 命令中的 -s 使用 https://vsign.trustasia.com。 |
准备命令行工具
本文以 ssigncode-mac-x86_64 为例。请确认工具与执行机器的处理器架构、macOS 版本兼容,下文使用 <签名工具所在目录>、<待验证的已签名EXE文件的完整路径> 等占位示例。执行前请将尖括号及其中内容整体替换为实际值,并保留路径外侧的引号。
bash
chmod u+x "<签名工具所在目录>/ssigncode-mac-x86_64"准备 Electron Builder
项目尚未安装 Electron Builder 时,可在项目根目录执行:
bash
npm install --save-dev electron-builder本文的打包测试使用 Electron Builder 26.18.0。已有项目如支持下文的自定义签名配置,可沿用现有版本;配置项有差异时,请按对应版本文档调整。项目还需已安装自身使用的 Electron 版本。
2. 安装操作员证书并获取指纹
在实际执行构建的 macOS 用户账号下,进入签名工具目录,按前文获取的信息执行安装命令:
bash
./ssigncode-mac-x86_64 install \
--vsign \
--auth '<操作员安装token>' \
--key_pin '<签名PIN>' \
-s https://vsign.trustasia.com确认终端提示“获取操作员证书成功”和“安装操作员证书成功”。

安装完成后,分别获取以下两个指纹:
- 返回 云签详情 → 操作员管理 并刷新,复制对应操作员的 证书哈希,作为操作员证书指纹。
- 进入目标代码签名证书的 订单详情 → 证书详情,复制 指纹 字段,作为签名证书指纹。


核对目标代码签名证书的所属组织和有效期。操作员证书指纹、签名证书指纹和安装 token 分别用于不同参数,不能互相替代。
接入 Electron Builder 前,请先按 在 macOS 上使用云签工具进行代码签名(云签) 中的签名步骤,用同一工具、签名服务器、PIN 和两种证书指纹完成一个 EXE 文件的签名与验证。
3. 配置环境变量和签名脚本
设置签名参数
在项目根目录打开 macOS 默认的 zsh 终端,设置以下环境变量,替换工具路径和两种证书指纹:
bash
export VSIGN_TOOL="<签名工具所在目录>/ssigncode-mac-x86_64"
export VSIGN_OPERATOR_HASH="<操作员证书指纹>"
export VSIGN_CERT_HASH="<签名证书指纹>"在同一终端中输入签名 PIN,输入内容不会显示在终端中:
bash
read -rs 'VSIGN_KEY_PIN?请输入签名 PIN:'; printf '\n'
export VSIGN_KEY_PIN| 参数 | 填写方法 |
|---|---|
VSIGN_TOOL | 命令行签名工具的绝对路径,路径含空格时也需完整填写。 |
VSIGN_OPERATOR_HASH | 操作员证书安装后,从 云签详情 → 操作员管理 → 证书哈希 获取的值。 |
VSIGN_CERT_HASH | 从目标代码签名证书的 订单详情 → 证书详情 → 指纹 获取的值。 |
VSIGN_KEY_PIN | 在 云签详情 → 签名安全设置 中设置的当前 PIN。 |
编写签名脚本
在项目根目录新建 sign.cjs,写入以下内容。脚本沿用前文云签教程的 vsign 子命令、--cert_from csign、服务器地址和证书参数。
javascript
const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { randomUUID } = require('node:crypto');
module.exports = async function sign(configuration) {
const required = [
'VSIGN_TOOL',
'VSIGN_OPERATOR_HASH',
'VSIGN_CERT_HASH',
'VSIGN_KEY_PIN'
];
for (const name of required) {
if (!process.env[name]) throw new Error(`请设置环境变量 ${name}`);
}
// 本次演示采用 SHA256,检查构建配置是否与示例一致。
if (configuration.hash !== 'sha256') {
throw new Error('本示例配置为 SHA256 单签名,请检查 signingHashAlgorithms 配置');
}
const pin = process.env.VSIGN_KEY_PIN;
const input = path.resolve(configuration.path);
const extension = path.extname(input);
const output = path.join(
path.dirname(input),
`${path.basename(input, extension)}.signing-${randomUUID()}${extension}`
);
const args = [
'vsign',
'-s', 'https://vsign.trustasia.com',
'--cert_from', 'csign',
'--cert_hash', process.env.VSIGN_CERT_HASH,
'-u', process.env.VSIGN_OPERATOR_HASH,
'--key_pin', pin,
'--hash', 'sha256',
'--ts', 'TrustAsia',
'--lang', 'zh-cn',
'-i', input,
'-o', output
];
console.log(`[VSign] 开始签名:${path.basename(input)}`);
try {
const result = spawnSync(process.env.VSIGN_TOOL, args, {
encoding: 'utf8',
timeout: 180000,
maxBuffer: 4 * 1024 * 1024
});
const log = `${result.stdout || ''}${result.stderr || ''}`
.split(pin).join('[已隐藏]');
if (log) process.stdout.write(log);
if (result.error || result.status !== 0) {
throw new Error(
`签名工具执行失败,退出码:${result.status},错误类型:${result.error?.code || '无'}`
);
}
if (!log.includes('签名成功') || log.includes('签名失败')) {
throw new Error('未检测到签名成功信息,请检查工具输出');
}
if (!fs.existsSync(output) || fs.statSync(output).size === 0) {
throw new Error('未生成有效的签名输出文件');
}
fs.chmodSync(output, fs.statSync(input).mode);
fs.renameSync(output, input);
console.log(`[VSign] 签名完成:${path.basename(input)}`);
} finally {
fs.rmSync(output, { force: true });
}
};构建时,Electron Builder 会自动调用此脚本进行签名。如脚本报错,请先根据日志排查,再重新构建。
配置 Electron Builder
将以下配置合并到项目 package.json 中。保留项目已有的入口、依赖、资源和其他构建配置,不要用此片段覆盖整个文件。
json
{
"scripts": {
"build:win": "electron-builder --win --x64 --dir",
"build:installer": "electron-builder --win nsis --x64"
},
"build": {
"win": {
"target": "nsis",
"signtoolOptions": {
"sign": "./sign.cjs",
"signingHashAlgorithms": ["sha256"]
}
}
}
}以下配置项已在 Electron Builder 26.18.0 中核对。使用其他版本时,请核对自定义签名入口的配置位置;较早版本可能使用 win.sign,应按该版本文档调整。
本示例将 signingHashAlgorithms 设为 ["sha256"],与脚本中的 SHA256 检查保持一致。如需使用其他摘要算法或多重签名,请结合签名工具和目标系统的支持情况,同时调整构建配置与签名脚本。
时间戳由自定义脚本中的 --ts TrustAsia 参数指定。如需更换时间戳服务,应修改脚本并确认当前签名工具支持该服务;仅修改 Electron Builder 内置签名工具的时间戳配置不会改变本脚本的参数。
4. 构建并执行签名
先生成 Windows 应用目录,确认主程序的自动签名流程正常:
bash
npm run build:win未自定义输出目录时,可在 dist/win-unpacked/ 中找到应用程序。成功时,日志应包含类似信息:
text
[VSign] 开始签名:您的应用.exe
vsign>开始签名, 读取文件
vsign>发送文件摘要,进行签名...
vsign>附加时间戳...
vsign>签名成功!
[VSign] 签名完成:您的应用.exe确认应用程序签名正常后,再构建 NSIS 安装包:
bash
npm run build:installer按本文配置,Electron Builder 会在相应的 Windows 签名步骤中调用自定义脚本;具体涉及哪些可执行文件,取决于构建目标、版本和项目配置。请检查完整构建日志,不要仅凭主程序签名成功就判断安装包也已签名。
若构建失败,请先处理错误,再重新构建。输出目录可能保留失败前生成的文件,不应将其作为正式发布产物。
接入 CI/CD
本地流程验证通过后,可在 macOS 构建节点按相同流程接入,并根据节点环境调整工具路径和参数。请确认:
- 操作员证书已在实际运行构建任务的系统用户下安装,并能使用同一套云签参数完成手动签名。
- 节点可访问 VSign 正式服务、所选时间戳服务及构建依赖下载地址。
- 通过 CI/CD 平台的密钥管理功能注入
VSIGN_KEY_PIN,并设置其余三个环境变量。 - 首次运行时单独验证无人值守任务,确认工具运行过程中没有等待人工处理的交互提示。
- 用于正式发布的产物已通过完整构建及签名检查。
临时构建节点需按第 2 步准备操作员证书;本地 Mac 安装成功不代表其他节点已经完成安装。
5. 检查签名结果
将生成的 Windows EXE 或安装包复制到 Windows 电脑,右键打开 属性 → 数字签名,查看签名者并进入“详细信息”,确认签名有效,同时核对时间戳和证书所属组织。
若已安装 Windows SDK SignTool,也可在能调用 signtool 的命令行中验证。将路径占位符替换为实际文件的完整路径:
powershell
signtool verify /pa /all /v /tw "<待验证的已签名EXE文件的完整路径>"/pa 使用默认 Authenticode 验证策略,/all 检查所有签名,/v 输出详细信息,/tw 在缺少时间戳时发出警告。请分别检查应用主程序和最终安装包,并留意验证输出中的错误与警告。命令参考:Microsoft SignTool。
签名工具报告成功不等于目标 Windows 环境已完成证书信任验证。正式发布前应完成上述检查;签名后不要再修改文件内容,否则需要重新签名。
常见问题
| 情况 | 处理方法 |
|---|---|
| 提示“无该证书权限” | 核对 VSIGN_OPERATOR_HASH 是否来自对应云签订单的操作员记录,VSIGN_CERT_HASH 是否来自目标代码签名证书详情,并确认服务器地址正确。 |
| 提示“无法找到操作员证书” | 确认操作员证书已安装在运行构建的系统用户下,并检查 VSIGN_OPERATOR_HASH 是否正确。 |
| 提示 PIN 错误或密钥访问失败 | 使用 云签详情 → 签名安全设置 中的当前 PIN,检查 VSIGN_KEY_PIN 是否已传入构建进程。 |
| 提示工具不存在或无执行权限 | 检查 VSIGN_TOOL 的绝对路径、执行权限及处理器架构是否匹配。 |
| 手动签名成功,CI/CD 失败 | 检查任务执行用户、操作员证书安装情况、环境变量、网络及代理设置。 |
| 构建时没有调用签名脚本 | 检查 Electron Builder 版本、实际加载的配置文件、win.signtoolOptions.sign 和脚本路径。 |
| 时间戳失败 | 检查时间戳服务连通性及代理、防火墙设置;完成签名和时间戳验证后再发布。 |
| 主程序签名成功,但安装包未通过验证 | 确认执行了安装包构建,检查后续签名日志,并单独验证最终安装包。 |
