uninstaller is not signed by electron-builder reason=custom NSIS script is used
时间: 2025-08-05 14:06:33 浏览: 7
<think>我们正在解决的是:使用electron-builder时,由于自定义NSIS脚本导致卸载程序未签名的问题。
根据引用[3]我们知道,electron-builder在打包时会自动对应用中的exe文件进行签名,但有些exe文件(比如第三方库)可能不能签名,否则会导致报错。同时,electron-builder默认会对安装程序和卸载程序进行签名。但是,当我们自定义NSIS脚本时,可能会干扰electron-builder的默认签名行为,导致卸载程序未被签名。
问题分析:
electron-builder在构建过程中会自动生成安装程序(setup.exe)和卸载程序(uninstall.exe)。默认情况下,这些可执行文件都会被签名。但是,如果我们使用了自定义的NSIS脚本(通过`include`选项引入),那么electron-builder可能不会自动对卸载程序进行签名,因为自定义脚本可能改变了默认的构建流程。
解决方案:
1. 确保electron-builder的版本支持自定义脚本后的签名操作(较新版本通常支持)。
2. 在自定义NSIS脚本中,确保没有删除或修改卸载程序的相关部分,因为electron-builder需要知道卸载程序的位置才能签名。
3. 如果electron-builder没有自动签名卸载程序,我们可以手动在构建后步骤中对卸载程序进行签名。
具体步骤:
步骤1:检查electron-builder配置
在`package.json`的`build`配置中,确保已经配置了签名选项。例如:
```json
"build": {
"appId": "com.example.app",
"win": {
"target": "nsis",
"signingHashAlgorithms": ["sha256"],
"certificateFile": "path/to/certificate.pfx",
"certificatePassword": "password",
"signDlls": false
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"include": "build/installer.nsh"
}
}
```
步骤2:在自定义NSIS脚本中保留卸载程序的生成
确保自定义脚本(如`installer.nsh`)没有删除或覆盖卸载程序的生成。卸载程序通常由electron-builder自动生成,文件名为`uninstall.exe`,位于安装目录下。
步骤3:手动签名卸载程序(如果自动签名失败)
如果发现卸载程序没有被自动签名,我们可以通过electron-builder的`afterPack`钩子来手动签名。在`package.json`中配置`afterPack`脚本:
```json
"build": {
...,
"afterPack": "./afterPack.js"
}
```
然后创建`afterPack.js`文件,内容如下:
```javascript
const path = require('path');
const { sign } = require('electron-builder/out/codeSign/windowsCodeSign');
const fs = require('fs');
module.exports = async function (context) {
// 获取构建输出目录
const outDir = context.appOutDir;
// 卸载程序路径(根据你的项目结构,卸载程序可能在应用根目录)
const uninstallerPath = path.join(outDir, 'uninstall.exe');
// 检查卸载程序是否存在
if (fs.existsSync(uninstallerPath)) {
console.log(`Signing uninstaller: ${uninstallerPath}`);
// 使用electron-builder的签名函数进行签名
await sign({
path: uninstallerPath,
cert: 'path/to/certificate.pfx', // 证书路径
password: 'password', // 证书密码
hash: 'sha256', // 哈希算法
});
} else {
console.log('Uninstaller not found, skipping signing.');
}
};
```
注意:`electron-builder`的`sign`函数需要证书文件路径和密码。这里我们直接调用electron-builder内部的签名函数,确保签名方式一致。
步骤4:如果手动签名不可行,也可以使用独立的签名工具(如`signtool`)在构建后步骤中签名。修改`afterPack.js`:
```javascript
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
module.exports = async function (context) {
const outDir = context.appOutDir;
const uninstallerPath = path.join(outDir, 'uninstall.exe');
if (fs.existsSync(uninstallerPath)) {
console.log(`Signing uninstaller: ${uninstallerPath}`);
// 使用signtool进行签名
const signCommand = `signtool sign /f "path/to/certificate.pfx" /p "password" /fd SHA256 /tr "http://timestamp.digicert.com" /td SHA256 "${uninstallerPath}"`;
try {
execSync(signCommand, { stdio: 'inherit' });
} catch (e) {
console.error('Signing uninstaller failed', e);
}
}
};
```
注意:需要确保构建机器上安装了Windows SDK(包含signtool),并且路径在环境变量中。
步骤5:重新构建应用
运行构建命令,检查卸载程序是否被正确签名。
总结:
自定义NSIS脚本可能导致electron-builder的默认签名流程跳过卸载程序。我们可以通过构建后钩子手动对卸载程序进行签名,确保所有可执行文件都被正确签名。
引用说明:
1. 关于electron-builder的签名配置,可参考官方文档[^1]。
2. 自定义NSIS脚本的配置方法参考了引用[2]。
3. 关于签名第三方库的问题,引用[3]提供了思路,但本问题中我们主要关注卸载程序的签名。
相关问题:
§§相关问题§§
1. electron-builder如何配置自动签名?
2. 如何在electron应用中使用自定义NSIS脚本?
3. 如何验证Windows应用程序的签名是否成功?
4. 使用electron-builder打包时,如何处理第三方可执行文件的签名问题?</think>### 解决 Electron-Builder 自定义 NSIS 脚本导致卸载程序未签名的问题
#### 问题分析
当使用自定义 NSIS 脚本时,Electron-Builder 的默认签名流程会被绕过,导致卸载程序(uninstall.exe)未被签名[^3]。主要原因包括:
1. **签名范围限制**:Electron-Builder 默认只签名主安装程序(setup.exe)和核心文件
2. **自定义脚本干扰**:NSIS 脚本添加的外部资源未被纳入签名范围
3. **路径依赖问题**:卸载程序路径在自定义脚本中可能发生变化
---
### 解决方案
#### 1. 配置强制签名卸载程序
在 `package.json` 的构建配置中添加 `signUninstaller` 选项:
```json
{
"build": {
"win": {
"target": "nsis",
"signingHashAlgorithms": ["sha256"],
"signUninstaller": true // 关键配置
},
"nsis": {
"include": "build/installer.nsh",
"oneClick": false
}
}
}
```
#### 2. 手动签名卸载程序
在构建后使用 SignTool 手动签名:
```powershell
# 在构建脚本中添加
signtool sign /fd SHA256 /f "cert.pfx" /p "password" /tr http://timestamp.digicert.com "dist\win-unpacked\uninstall.exe"
```
#### 3. 修改 NSIS 脚本确保路径正确
在 `installer.nsh` 中明确定义卸载程序路径:
```nsis
!macro customUnInstall
; 确保卸载程序路径正确
SetOutPath "$INSTDIR"
File "uninstall.exe"
ExecWait '"$INSTDIR\uninstall.exe" _?=$INSTDIR'
!macroend
```
#### 4. 扩展签名范围
在构建配置中添加 `sign` 字段指定额外文件:
```json
{
"build": {
"win": {
"sign": {
"signingHashAlgorithms": ["sha256"],
"certificateFile": "cert.pfx",
"certificatePassword": "password",
"signDlls": false,
"signUninstaller": true,
"targets": [
"uninstall.exe", // 显式指定卸载程序
"resources/*.exe" // 签名资源目录中的其他程序
]
}
}
}
}
```
#### 5. 验证签名结果
构建后运行验证命令:
```cmd
signtool verify /v /pa "dist\win-unpacked\uninstall.exe"
```
---
### 完整配置示例
```json
// package.json
{
"build": {
"appId": "com.example.app",
"win": {
"target": "nsis",
"sign": {
"signingHashAlgorithms": ["sha256"],
"certificateFile": "cert.pfx",
"certificatePassword": "password",
"signUninstaller": true,
"targets": ["uninstall.exe", "resources/*.exe"]
}
},
"nsis": {
"include": "build/installer.nsh",
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"extraResources": [
{"from": "build/third-party.exe", "to": "resources/"}
],
"afterPack": "scripts/sign-uninstaller.js"
}
}
```
```javascript
// scripts/sign-uninstaller.js (二次签名脚本)
const { execSync } = require('child_process');
const path = require('path');
module.exports = async (context) => {
const uninstallerPath = path.join(context.appOutDir, 'uninstall.exe');
const signCmd = `signtool sign /fd SHA256 /f cert.pfx /p password ${uninstallerPath}`;
execSync(signCmd, { stdio: 'inherit' });
};
```
---
### 关键注意事项
1. **签名顺序**:先构建后签名,确保在 `afterPack` 钩子中执行签名
2. **时间戳服务**:必须添加 `/tr` 参数保证签名长期有效
3. **路径转义**:NSIS 脚本中使用 `$INSTDIR` 变量避免硬编码路径
4. **证书存储**:推荐使用环境变量存储证书密码,不要明文写在配置中
> 通过以上配置,可解决自定义 NSIS 脚本导致的卸载程序未签名问题,同时保持第三方程序的功能完整性[^2][^3]。
阅读全文
相关推荐

















