# Windows 代码签名流程 本文档说明如何在本地对 Windows 安装包进行代码签名。 > ⚠️ **重要说明**:当前 Windows 客户端签名**仅支持本地手签方案**,不支持 CI/CD 自动化签名。 > > 原因:使用 Certum SimplySign 云证书,需要通过手机 APP 获取动态 token 进行二次验证,无法在无人值守的 CI 环境中完成。 ## 相关文件 | 文件 | 说明 | |------|------| | [sign-release-win.sh](./sign-release-win.sh) | 完整签名流程脚本 | | [sign-win.js](./sign-win.js) | 签名工具模块 | ## 签名流程概览 ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ Windows 客户端签名流程 │ ├──────────────────────────────────────────────────────────────────────────┤ │ │ │ GitHub CI │ │ ┌─────────┐ │ │ │ Build │ ─── 构建 unsigned 安装包 ───▶ GitHub Release │ │ └─────────┘ (未签名) │ │ │ │ 本地手签 │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │Download │───▶│ Sign │───▶│ Verify │───▶│ Upload │ │ │ │ (gh cli)│ │(signtool)│ │(signtool)│ │ (gh cli)│ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ unsigned/ unsigned/ signed/ GitHub Release │ │ (copy) (已签名) │ │ │ └──────────────────────────────────────────────────────────────────────────┘ ``` ## 前置要求 ### 1. SimplySign Desktop 客户端 Certum 云代码签名证书需要安装 SimplySign Desktop 客户端来加载证书。 **下载地址:** - Windows 64 位: https://www.certum.eu/data/other/SimplySign/SimplySignDesktop_x64.exe - Windows 32 位: https://www.certum.eu/data/other/SimplySign/SimplySignDesktop_x86.exe - Mac OS X: https://www.certum.eu/data/other/SimplySign/SimplySignDesktop.dmg **安装步骤:** 1. 运行安装程序,语言选择 **English** 2. 安装路径可自定义 3. 组件选择时,仅勾选 **SimplySign Desktop**(不需要 proCertum SmartSign) **登录配置:** 1. 输入注册邮箱 2. 打开手机 SimplySign APP 获取 token 密码 3. 输入 token 完成登录 **获取证书指纹:** 1. 登录后软件最小化到系统托盘 2. 右键图标 → Manage certificates → Certificate list 3. 双击证书查看指纹 (Thumbprint) ### 2. Windows SDK 包含 `signtool.exe` 签名工具。 - 安装路径: `C:\Program Files (x86)\Windows Kits\10\bin\{version}\x64\` - 下载: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/ ### 3. GitHub CLI 用于下载和上传 release 文件: ```bash gh auth status ``` ## 环境变量配置 在签名前需要设置以下环境变量: ```bash # 必需 - 证书指纹(从 SimplySign Desktop 中获取) export WINDOWS_CERTIFICATE_SHA1="" # 可选(有默认值) export WINDOWS_TIMESTAMP_URL="http://timestamp.sectigo.com" export WINDOWS_PUBLISHER_NAME="成都第二空间智能科技有限公司" ``` > 💡 建议将环境变量添加到 `~/.bashrc` 或 `~/.bash_profile` 中持久化保存。 ## 检查签名状态 在签名前,建议先检查 release 的签名状态,避免重复操作。 ### 方法一:检查 Release 文件列表 ```bash # 查看 release 中的 Windows 安装包 gh release view electron-v0.9.2 --repo qiming-ai/qimingclaw --json assets \ --jq '.assets[] | select(.name | test("QimingClaw.*\\.(exe|msi)$")) | .name' ``` **判断标准:** | 文件名模式 | 签名状态 | 操作 | |-----------|---------|------| | `*-unsigned.exe` / `*-unsigned.msi` | 未签名 | 需要执行签名 | | `QimingClaw-Setup-x.x.x.exe` / `QimingClaw-x.x.x.msi` | 已签名 | 无需操作 | ### 方法二:下载并验证签名 ```bash # 下载文件到临时目录 mkdir -p /c/tmp/qimingclaw-sign/check && cd /c/tmp/qimingclaw-sign/check gh release download electron-v0.9.2 --repo qiming-ai/qimingclaw \ --pattern "QimingClaw-Setup-*.exe" --pattern "QimingClaw-*.msi" # 验证签名 "/c/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x64/signtool.exe" \ verify //pa //all QimingClaw-Setup-0.9.2.exe ``` **输出解读:** - `Successfully verified` → 已签名 ✓ - `SignerTool does not support...` 或错误 → 未签名或签名无效 ## 使用方法 ### 完整流程(推荐) 下载 → 签名 → 验证 → 上传: ```bash cd crates/agent-electron-client ./scripts/build/sign-release-win.sh 0.9.2 ``` ### 跳过下载 如果已有未签名的文件: ```bash ./scripts/build/sign-release-win.sh 0.9.2 --skip-download ``` ### 跳过上传 仅本地签名,不上传到 GitHub: ```bash ./scripts/build/sign-release-win.sh 0.9.2 --skip-upload ``` ### 组合使用 ```bash ./scripts/build/sign-release-win.sh 0.9.2 --skip-download --skip-upload ``` ## 多次构建/签名场景 ### 场景一:同一版本重新打包后签名 如果 CI 重新构建了同一版本(修复构建问题等),release 中会出现新的 `-unsigned` 文件: ``` Release 资产列表: ├── QimingClaw-Setup-0.9.2.exe # 旧的已签名文件 ├── QimingClaw-0.9.2.msi # 旧的已签名文件 ├── QimingClaw-Setup-0.9.2-unsigned.exe # 新的未签名文件 (CI 重新构建) └── QimingClaw-0.9.2-unsigned.msi # 新的未签名文件 (CI 重新构建) ``` **处理方式:** 直接运行签名脚本,它会: 1. 下载新的 `-unsigned` 文件 2. 签名后覆盖旧的已签名文件 3. 删除 `-unsigned` 文件 ```bash ./scripts/build/sign-release-win.sh 0.9.2 # 正常执行即可 ``` ### 场景二:重新签名已签名的文件 如果需要重新签名(证书更新等),需要先让 CI 重新构建: ```bash # 1. 触发 CI 重新构建(推送 tag 或手动触发) # 2. CI 会生成新的 -unsigned 文件 # 3. 然后执行签名脚本 ./scripts/build/sign-release-win.sh 0.9.2 ``` > ⚠️ **注意**:不能直接对已签名的文件再次签名,需要从 CI 获取新的未签名文件。 ### 场景三:检查是否需要签名 ```bash # 快速检查 release 中是否有 -unsigned 文件 gh release view electron-v0.9.2 --repo qiming-ai/qimingclaw --json assets \ --jq '.assets[] | select(.name | test("-unsigned\\.(exe|msi)$")) | .name' # 有输出 → 需要签名 # 无输出 → 已签名或无 Windows 构建 ``` ## 目录结构 ``` C:\tmp\qimingclaw-sign\ ├── unsigned/ # 从 GitHub 下载的未签名文件 │ ├── QimingClaw-Setup-0.9.2-unsigned.exe │ └── QimingClaw-0.9.2-unsigned.msi │ └── signed/ # 已签名文件(重命名为正式名称) ├── QimingClaw-Setup-0.9.2.exe └── QimingClaw-0.9.2.msi ``` ## 文件命名规则 | 阶段 | EXE 文件名 | MSI 文件名 | |------|-----------|-----------| | CI 构建 | `QimingClaw-Setup-{version}-unsigned.exe` | `QimingClaw-{version}-unsigned.msi` | | 本地签名后 | `QimingClaw-Setup-{version}.exe` | `QimingClaw-{version}.msi` | ## 流程步骤说明 | 步骤 | 命令/工具 | 说明 | |------|-----------|------| | Download | `gh release download` | 从 GitHub Release 下载 `.exe` 和 `.msi` 到 `unsigned/` | | Sign | `signtool sign` | 使用证书进行代码签名(需要 SimplySign token) | | Verify | `signtool verify` | 验证签名有效性 | | Copy | `cp` | 复制已签名文件到 `signed/` | | Upload | `gh release upload` | 上传到 GitHub Release(覆盖未签名文件) | ## 手动签名(可选) 如果需要单独签名某个文件: ```bash # 设置 PATH(包含 signtool) export PATH="/c/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x64:$PATH" # 设置环境变量 export WINDOWS_CERTIFICATE_SHA1="" export WINDOWS_TIMESTAMP_URL="http://timestamp.sectigo.com" # 签名 node scripts/build/sign-win.js /path/to/file.exe # 验证 signtool verify //pa //all /path/to/file.exe ``` ## 常见问题 ### Q: 为什么不支持 CI 自动签名? Certum SimplySign 是云证书方案,需要通过手机 APP 获取动态 token 进行二次验证(2FA),这要求必须有人工参与,无法在无人值守的 CI 环境中完成。 ### Q: signtool 找不到? 确保已安装 Windows SDK,脚本会自动搜索以下路径: - `C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64` - `C:\Program Files (x86)\Windows Kits\10\bin\x64` ### Q: 证书未找到? 检查证书是否正确加载: ```powershell # PowerShell - 列出所有代码签名证书 Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.EnhancedKeyUsageList -match "Code Signing" } ``` 确保 SimplySign Desktop 已登录且证书已加载。 ### Q: 网络超时? 下载或上传可能因网络问题超时,可以: 1. 使用 `--skip-download` 跳过下载 2. 使用 `--skip-upload` 跳过上传,稍后手动上传 ### Q: MSI 签名失败? 确保下载的 MSI 文件完整(未截断)。检查文件大小是否合理(通常 > 100MB)。 ### Q: NSIS 安装包完整性错误? 如果签名后的安装包运行时出现 "Installer integrity check has failed" 错误: 1. 验证下载文件 SHA256 是否正确 2. 重新下载原始文件 3. 检查原始 CI 构建是否有问题 ## 参考链接 - [Certum SimplySign 使用教程](https://ssldun.net/html/guide_cn/show-1703.html) - [SimplySign 证书提取](https://ssldun.net/html/guide_cn/show-1702.html) ## 更新日志 | 日期 | 说明 | |------|------| | 2026-03-26 | 添加签名状态检查和多次构建/签名场景说明 | | 2026-03-26 | 创建签名流程文档,明确仅支持本地手签方案 |