fix(installer): 正式签名覆盖包内全部 EXE/DLL

此前只有 MetasequoiaImeServer.exe 拿到真实签名——它是唯一一个技术上必须签的,
uiAccess=true 没有可信 Authenticode 签名就会被 Windows 忽略。其余随包分发的
二进制全部未签名:设置页、表情面板、手写面板、键盘面板、看门狗、词库回放,
以及 libcurl/sqlite3/zlib/WebView2Loader 四个 DLL 和两个架构的 TSF DLL。

Microsoft Defender 会按信誉拦下这些未签名的可执行文件,用户看到的现象是输入法
装完了、某个面板却打不开。

package-simplysign.ps1 和 release workflow 现在都签 server_exe\ 与 tsf_dll\ 下的
全部 EXE/DLL,共 13 个文件,加上外层安装包。

所有文件在一次 signtool sign 调用里签完,而不是逐个调用:SimplySign 可能对每次
调用弹一次 PIN,逐个签会把本地打包变成十几次手机确认。签名配额按文件计,合并
调用不会多花配额;证书每月 3000 次,每次发布十几个仍然远在额度之内。
This commit is contained in:
fanlusky committed 2026-09-17 23:10:54 +08:00
1 parent fa799ff6bc
commit 9bc1c925b2
3 files changed
+81 -31

No files matched your search

+7 -3
View File
@@ -4,7 +4,7 @@ name: Release
#
# The whole cycle runs inside the one run that the original push started. land-release-pr.sh merges with GITHUB_TOKEN, whose pushes do not start workflow runs, so the merge cannot produce a second Release run queued behind this one on the concurrency group.
#
# This spends a code signing signature on every releasable merge to main. The certificate allows 3000 signatures a month, so the quota is not what constrains this; what it spends is release hygiene -- see docs/product-release.md.
# This spends code signing signatures on every releasable merge to main -- one per payload EXE/DLL plus one for the installer, so roughly fifteen per release rather than one. The certificate allows 3000 signatures a month, so the quota is still not what constrains this; what it spends is release hygiene -- see docs/product-release.md.
#
# workflow_dispatch stays, and is now a repair path rather than the normal one. It builds an existing draft, which is what a run that failed after creating the draft leaves behind.
on:
@@ -499,7 +499,11 @@ jobs:
--windows-commit "$env:WINDOWS_COMMIT"
--output installer/app_data/product-manifest.json
- name: Sign the uiAccess server
# Every payload EXE/DLL, not just the uiAccess server. The server is the only one that
# technically requires a signature (uiAccess=true is ignored without one), but Microsoft
# Defender blocks the unsigned helper processes -- settings page, emoji panel, watchdog --
# and the user sees an input method that installs and then cannot open its own panels.
- name: Sign the payload binaries
if: ${{ steps.signing.outputs.signing_enabled == 'true' }}
shell: pwsh
working-directory: installer
@@ -508,7 +512,7 @@ jobs:
CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PASSWORD }}
CERTIFICATE_THUMBPRINT: ${{ vars.WINDOWS_SIGNING_CERTIFICATE_THUMBPRINT }}
TIMESTAMP_URL: ${{ vars.WINDOWS_SIGNING_TIMESTAMP_URL || 'http://timestamp.digicert.com' }}
run: ../scripts/ci/sign-binaries.ps1 -Path 'server_exe/MetasequoiaImeServer.exe'
run: ../scripts/ci/sign-binaries.ps1 -Recurse -Path 'server_exe', 'tsf_dll'
- name: Install the Chinese language file for Inno Setup
shell: pwsh
+10 -3
View File
@@ -2,7 +2,7 @@
本目录的脚本从 Windows 合仓目录收集产物、签名、用 Inno Setup 打成安装包。它服务两条流程:
- **正式发布**:由 `MSIME-Windows` 的 release workflow 驱动,用真实证书签名 `MetasequoiaImeServer.exe`(保证 `uiAccess` 生效)和最终安装包;产物是挂在 Release 上的 `MetasequoiaIME_Setup_v<版本>.exe`,并携带与 Release 二进制匹配的 PDB 符号文件。见下面「CI 契约」
- **正式发布**:由 `MSIME-Windows` 的 release workflow 驱动,用真实证书签名包内全部 EXE/DLL 和最终安装包;产物是挂在 Release 上的 `MetasequoiaIME_Setup_v<版本>.exe`,并携带与 Release 二进制匹配的 PDB 符号文件。见下面「CI 契约」
- **本地测试**:手工跑,用本机自签名证书,用来在自己机器上验证安装流程。本文其余部分讲的是这条
版本号不是固定的,由 `Prepare-PackageFiles.ps1` 的 `-TargetVersion` 决定,默认 `0.0.1`;正式发布时 CI 传入真实版本。
@@ -86,10 +86,17 @@ pwsh -File .\package-simplysign.ps1 1.2.3
命令行版本会同时写入本轮 TSF DLL 的版本资源和安装包元数据(脚本结束后恢复源码模板)。脚本会
增量编译 TSF、Server 和设置页,制作完整包,用 `http://time.certum.pl` 的 RFC 3161 时间戳签名
`MetasequoiaImeServer.exe`(使 `uiAccess=true` 生效),编译 Inno Setup 安装包,再签名并校验
最终安装包。产物位于 `Output\MetasequoiaIME_Setup_v1.2.3.exe`;脚本只产出文件,不会自动启动
`server_exe\` 和 `tsf_dll\` 下的**全部 EXE/DLL**,编译 Inno Setup 安装包,再签名并校验最终
安装包。产物位于 `Output\MetasequoiaIME_Setup_v1.2.3.exe`;脚本只产出文件,不会自动启动
安装程序。
**为什么整包都要签。** `MetasequoiaImeServer.exe` 是唯一一个技术上必须签的——`uiAccess=true`
没有可信 Authenticode 签名就会被 Windows 忽略。但其余辅助进程(设置页、表情面板、手写面板、
键盘面板、看门狗、词库回放)和随包分发的 DLL 不签名会被 Microsoft Defender 按信誉拦下,
用户看到的现象是输入法装完了、某个面板却打不开。所有文件在一次 `signtool sign` 调用里签完:
SimplySign 可能对每次调用弹一次 PIN,逐个文件调用会变成十几次手机确认;签名配额按文件计,
合并调用不会多花配额。
默认不把 PDB 放入安装包;需要与正式 CI 一致的符号包时加 `-IncludeSymbols`。如果 SimplySign
同时暴露了多张有效的 Certum Code Signing 证书,脚本会列出它们并要求用
`-CertificateThumbprint <指纹>` 明确选择。可用 `-Reconfigure` 强制重新配置 CMake,也可用
+64 -25
View File
@@ -125,36 +125,77 @@ function Get-SimplySignCertificate {
function Invoke-SimplySign {
param(
[Parameter(Mandatory)][string]$LiteralPath,
[Parameter(Mandatory)][string[]]$LiteralPath,
[Parameter(Mandatory)]$Certificate
)
if (-not (Test-Path -LiteralPath $LiteralPath -PathType Leaf)) {
throw "待签名文件不存在:$LiteralPath"
}
# 最外层的 @() 不能省:只有一个待签文件时管道结果是标量,.Count 在严格模式下会报错。
$resolvedPaths = @(
@(
foreach ($item in $LiteralPath) {
if (-not (Test-Path -LiteralPath $item -PathType Leaf)) {
throw "待签名文件不存在:$item"
}
(Resolve-Path -LiteralPath $item).Path
}
) | Sort-Object -Unique
)
$resolvedPath = (Resolve-Path -LiteralPath $LiteralPath).Path
$thumbprint = $Certificate.Thumbprint -replace '\s', ''
Write-Host "正在使用 SimplySign 签名:$resolvedPath"
& $script:signTool sign /sha1 $thumbprint /s My /fd sha256 /tr $TimestampUrl /td sha256 /v $resolvedPath
Write-Host "正在使用 SimplySign 签名 $($resolvedPaths.Count) 个文件:"
$resolvedPaths | ForEach-Object { Write-Host " $_" }
# 一次 signtool 调用签完所有文件。SimplySign 可能对每次调用弹一次 PIN,逐个文件调用会让
# 整包签名变成十几次手机确认。签名配额按文件计,合并调用不会多花配额。
& $script:signTool sign /sha1 $thumbprint /s My /fd sha256 /tr $TimestampUrl /td sha256 /v @($resolvedPaths)
if ($LASTEXITCODE -ne 0) {
throw "signtool.exe 签名失败,退出码:$LASTEXITCODE;文件:$resolvedPath"
throw "signtool.exe 签名失败,退出码:$LASTEXITCODE"
}
& $script:signTool verify /pa /all /v $resolvedPath
if ($LASTEXITCODE -ne 0) {
throw "signtool.exe 签名校验失败,退出码:$LASTEXITCODE;文件:$resolvedPath"
foreach ($resolvedPath in $resolvedPaths) {
& $script:signTool verify /pa /all /v $resolvedPath
if ($LASTEXITCODE -ne 0) {
throw "signtool.exe 签名校验失败,退出码:$LASTEXITCODE;文件:$resolvedPath"
}
$signature = Get-AuthenticodeSignature -LiteralPath $resolvedPath
if (-not $signature.SignerCertificate -or
(($signature.SignerCertificate.Thumbprint -replace '\s', '') -ine $thumbprint)) {
throw "签名证书指纹与选择的 SimplySign 证书不一致:$resolvedPath"
}
if (-not $signature.TimeStamperCertificate) {
throw "签名中没有可信时间戳:$resolvedPath"
}
Write-Host "签名有效:$resolvedPath"
}
}
# 包内所有随安装包落到用户磁盘上的 EXE/DLL。只签 MetasequoiaImeServer.exe 是不够的:
# 未签名的辅助进程(设置页、表情面板、看门狗等)和随包分发的 DLL 会被 Microsoft Defender
# 的 SmartScreen/信誉判定拦下,而它们是用户实际会启动的程序。
function Get-PayloadBinaryPaths {
$packageDirectories = @(
(Join-Path $PSScriptRoot 'server_exe'),
(Join-Path $PSScriptRoot 'tsf_dll')
)
foreach ($directory in $packageDirectories) {
if (-not (Test-Path -LiteralPath $directory -PathType Container)) {
throw "待签名目录不存在,请先运行 Prepare-PackageFiles.ps1:$directory"
}
}
$signature = Get-AuthenticodeSignature -LiteralPath $resolvedPath
if (-not $signature.SignerCertificate -or
(($signature.SignerCertificate.Thumbprint -replace '\s', '') -ine $thumbprint)) {
throw "签名证书指纹与选择的 SimplySign 证书不一致:$resolvedPath"
$targets = @(
@(
foreach ($directory in $packageDirectories) {
Get-ChildItem -LiteralPath $directory -Recurse -File |
Where-Object { $_.Extension -in @('.exe', '.dll') }
}
) | Sort-Object FullName -Unique
)
if ($targets.Count -eq 0) {
throw '没有找到待签名的 EXE 或 DLL,请先运行 Prepare-PackageFiles.ps1。'
}
if (-not $signature.TimeStamperCertificate) {
throw "签名中没有可信时间戳:$resolvedPath"
}
Write-Host "签名有效:$($signature.SignerCertificate.Subject)"
return @($targets.FullName)
}
# Fail before compiling anything if SimplySign is not connected. Merely having its desktop process
@@ -218,12 +259,10 @@ try {
-NoticesDirectory . `
-HelpCodeDirectory engine/helpcode
# This is the only payload binary requiring a real signature: uiAccess=true is ignored by
# Windows unless the executable carries a trusted Authenticode signature. The outer installer is
# signed after compilation. This deliberately matches the formal release workflow.
Invoke-SimplySign `
-LiteralPath (Join-Path $PSScriptRoot 'server_exe\MetasequoiaImeServer.exe') `
-Certificate $certificate
# 整包签名,而不只是 MetasequoiaImeServer.exe。它是唯一一个「必须」签的——uiAccess=true
# 没有可信签名就会被 Windows 忽略——但其余辅助进程和 DLL 不签名会被 Defender 拦截,
# 用户看到的就是输入法装完之后某个面板打不开。外层安装包在编译之后单独签。
Invoke-SimplySign -LiteralPath (Get-PayloadBinaryPaths) -Certificate $certificate
& (Join-Path $PSScriptRoot 'Compile-Installer.ps1') -IsccPath $IsccPath
if (-not (Test-Path -LiteralPath $compiledInstallerPath -PathType Leaf)) {