代码签名存在一个密钥管理难题。常见的做法是把 .pfx 文件复制到每台需要为发布版本签名的构建代理上,或者插入 USB 令牌。私钥最终散落在那些运行不受信任构建脚本的机器上,没有任何记录说明签了什么、由谁签的,而轮换证书则意味着要逐台更动每个代理。sgcSign 2026.6 用一个全新的签名服务器解决了这个问题:一个自托管守护进程,通过 TLS REST API 对外提供签名服务,让你的流水线和开发者远程签名,而签名密钥始终只保留在一个地方。
密钥永远不会离开服务器。更妙的是,服务器可以充当硬件令牌或云 KMS 的前端,这样密钥根本不会以文件形式存在。每个请求都使用 API 密钥进行身份验证、施加速率限制,并写入防篡改的审计日志。本文将介绍该服务器能够签名的内容、如何配置、一份五分钟快速上手指南,以及调用它的两种方式:纯 curl 以及随附的 sgcsign 命令行客户端。
它能签什么
一台服务器、一套 API、八种签名格式。同样的端点形态(POST /api/v1/sign/<format>)同时涵盖可执行文件和文档:
- Authenticode — Windows PE 文件:
.exe、.dll、.sys、.msi、.cab、.ocx。可选地为旧版 Windows 进行 SHA-1 + SHA-256 双重签名。 - PAdES — PDF 文档,可附带原因、地点、联系人和签名者姓名。
- XAdES — XML,可选 enveloped、enveloping 或 detached 形式,支持各国电子发票配置文件,如 eIDAS、FacturaE、FatturaPA、KSeF、Peppol、VeriFactu 和 TicketBAI。
- CAdES — 针对任意载荷的分离式 PKCS#7(
.p7s)。 - ClickOnce — 应用程序和部署清单。
- NuGet —
.nupkg包签名。 - VSIX — Visual Studio 扩展包。
- PowerShell —
.ps1/.psm1/.psd1脚本(Authenticode SIP)。
每种格式都接受一个可选的 RFC 3161 时间戳 URL,这样即使签名证书过期,签名仍然有效。配套的 POST /api/v1/verify 端点可校验现有签名,并返回签名者主题、有效性和时间戳。
可插拔的密钥提供程序
提供程序(provider)是指向某个签名密钥的具名句柄。调用方在每个请求中指定提供程序的名称,但永远看不到密钥本身。你可以根据需要注册任意多个,同一台服务器既可以用本地 PFX 为内部工具签名,又可以用云端支持的 EV 证书为公开发布版本签名。共支持九种提供程序类型:
- Windows Certificate Store(
WinCertStore)— 按指纹或主题选择证书,非常适合在令牌上注册的 EV 证书。 - PFX 和 PEM — 磁盘上的证书和密钥文件。
- PKCS#11 / HSM(
PKCS11)— 硬件令牌(如 YubiKey)或网络 HSM。 - AWS KMS、Azure Trusted Signing、Google Cloud KMS、HashiCorp Vault 和 Certum SimplySign — 私钥存放在云服务中,永远不会到达服务器。
机密信息不会留在配置文件里。任何名称以 _env 结尾的参数都从环境变量读取,因此 PFX 密码、令牌 PIN 或云端机密都由服务运行环境提供,而不会提交到磁盘。
配置服务器
整个服务器由一个 JSON 文件驱动,即 sgcSignServer.conf.json(随附一份带注释的示例 sgcSignServer.conf.sample.json)。顶层结构由若干个块组成:
{
"server": {
"listen": "0.0.0.0",
"port": 8443,
"tls": {
"enabled": true,
"cert_file": "certs/server.crt",
"key_file": "certs/server.key"
},
"max_upload_mb": 512,
"firewall": {
"enabled": true,
"whitelist": ["10.0.0.0/8"],
"brute_force": { "enabled": true, "max_attempts": 5, "ban_duration_sec": 900 },
"rate_limit": { "enabled": true, "max_connections_per_ip": 30, "time_window_sec": 60 }
}
},
"storage": { "sqlite_path": "data/sgcsignserver.db" },
"admin": {
"initial_user": "admin",
"initial_password_env": "SGCSIGN_ADMIN_INIT_PW",
"session_timeout_minutes": 60
},
"audit": { "retention_days": 365 },
"providers": [
{
"name": "pfx-build",
"type": "PFX",
"params": { "file": "certs/build.pfx", "password_env": "SGCSIGN_PFX_PW" }
},
{
"name": "ev-release",
"type": "AzureTS",
"params": {
"tenant_id": "00000000-0000-0000-0000-000000000000",
"client_id": "00000000-0000-0000-0000-000000000000",
"client_secret_env": "SGCSIGN_AZURE_TS_SECRET",
"account": "your-account",
"certificate_profile": "your-profile",
"endpoint": "https://eus.codesigning.azure.net/"
}
}
]
}
这些块直接对应各项功能。server 设置绑定地址、端口(默认 8443)、TLS 证书以及内置防火墙(IP 允许和拒绝列表、暴力破解锁定、连接速率限制、路径遍历和载荷防护)。storage 指向保存用户、API 密钥、审计日志和 webhook 队列的 SQLite 数据库。admin 定义首个用户以及会话的有效时长。audit 设置保留时间窗口。providers 就是上面列出的那个列表。
引导用的管理员密码绝不会存储在文件中。在第一次启动、用户表为空时,服务器会读取由 initial_password_env(默认 SGCSIGN_ADMIN_INIT_PW)命名的环境变量,并创建管理员账户。在之后的每次启动中,该变量都会被忽略,因此它只用于初始化首次登录。
五分钟快速上手
安装服务器(安装向导会注册 Windows 服务,或运行 sgcSignServer.exe --install),然后:
1. 创建一个测试用代码签名证书并将其导出为 PFX。在真实部署中,你会导入由 CA 签发的证书,或者跳过此步,改为让提供程序指向你的令牌或云 KMS。
$cert = New-SelfSignedCertificate -Type CodeSigningCert `
-Subject "CN=sgcSign Quickstart" -KeyAlgorithm RSA -KeyLength 3072 `
-CertStoreLocation Cert:\CurrentUser\My -NotAfter (Get-Date).AddYears(1)
$pw = ConvertTo-SecureString "QuickStartPFX!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -Password $pw `
-FilePath "C:\Program Files\sgcSign Server\certs\quickstart.pfx"
2. 将机密设置为计算机级环境变量,以便服务账户能够读取它们:
setx /M SGCSIGN_ADMIN_INIT_PW "ChangeMeNow!"
setx /M SGCSIGN_PFX_PW "QuickStartPFX!"
3. 编写一份最小化配置,只含一个 PFX 提供程序(仅供 localhost 测试,关闭 TLS):
{
"server": { "listen": "127.0.0.1", "port": 8443, "tls": { "enabled": false } },
"storage": { "sqlite_path": "data/sgcsignserver.db" },
"admin": { "initial_user": "admin", "initial_password_env": "SGCSIGN_ADMIN_INIT_PW" },
"audit": { "retention_days": 365 },
"providers": [
{ "name": "pfx-quickstart", "type": "PFX",
"params": { "file": "certs/quickstart.pfx", "password_env": "SGCSIGN_PFX_PW" } }
]
}
4. 启动服务并确认它正在监听:
sc start sgcSignServer
sc query sgcSignServer
如果它在启动时停止,请用 sgcSignServer.exe --console --config <path> 在前台运行,以便实时查看错误(常见原因是缺少 SGCSIGN_ADMIN_INIT_PW 或 PFX 不可读)。--selftest-providers 标志会加载配置、初始化每个提供程序,然后退出,因此你可以在正式上线前验证密钥。
5. 登录并创建 API 密钥。打开 http://localhost:8443/admin,用引导密码以 admin 身份登录,并创建一个 API 密钥。明文密钥在创建时只显示一次;请立即复制。密钥带有前缀 sgcsk_。
6. 用 curl 为一个测试可执行文件签名:
curl -X POST http://localhost:8443/api/v1/sign/authenticode ^
-H "X-API-Key: sgcsk_..." ^
-F file=@app.exe ^
-F provider=pfx-quickstart ^
-F hash=sha256 ^
--output app-signed.exe
整个流程就是这样:配置一个提供程序、签发一个密钥、进行签名。从这里开始,你可以把自签名 PFX 换成真实证书,开启 TLS,并把防火墙收紧到只允许你的 CI 子网。
通过 REST 进行签名
每个签名请求都是一个携带文件和若干表单字段的 POST。身份验证只需一个请求头,可以是 X-API-Key: sgcsk_... 或 Authorization: Bearer sgcsk_...。响应是作为 application/octet-stream 返回的已签名工件,其中签名者主题和签名耗时分别在 X-Sgcsign-Signer-Subject 和 X-Sgcsign-Duration-Ms 请求头中返回。
为一个 PDF 签名,并带上可见的签名原因和地点:
curl -X POST https://sign.acme.local:8443/api/v1/sign/pades \
-H "X-API-Key: sgcsk_..." \
-F file=@invoice.pdf \
-F provider=qualified-eu \
-F tsa_url=http://timestamp.digicert.com \
-F reason=Approved \
-F location="Madrid, Spain" \
-F signer_name="Acme Billing" \
-o invoice-signed.pdf
用 eIDAS XAdES 配置文件为一个 XML 发票签名:
curl -X POST https://sign.acme.local:8443/api/v1/sign/xades \
-H "X-API-Key: sgcsk_..." \
-F file=@invoice.xml \
-F provider=qualified-eu \
-F profile=eidas \
-F xades_type=enveloped \
-o invoice-signed.xml
验证签名时返回的是 JSON 而非文件:
curl -X POST https://sign.acme.local:8443/api/v1/verify \
-H "X-API-Key: sgcsk_..." \
-F file=@app-signed.exe \
-F format=authenticode
{ "valid": true, "status": "valid",
"subject": "CN=Acme Inc., O=Acme, C=US",
"has_timestamp": true, "timestamp_time": "2026-06-15T13:01:17.000Z" }
sgcsign 命令行客户端
服务器随附 sgcsign,这是一个小巧的跨格式 CLI,带有四个动词:sign、verify、keys 和 health。服务器 URL、API 密钥和提供程序既可以来自命令行标志,也可以来自环境变量 SGCSIGN_SERVER、SGCSIGN_APIKEY 和 SGCSIGN_PROVIDER,从而让机密在 CI 中不会出现在命令行上:
set SGCSIGN_SERVER=https://sign.acme.local:8443
set SGCSIGN_APIKEY=sgcsk_...
sgcsign sign -f authenticode -p pfx-build -o app-signed.exe app.exe ^
--tsa http://timestamp.digicert.com --desc "Acme Installer"
sgcsign verify -f authenticode app-signed.exe
对于 Authenticode,还有一个节省带宽的技巧。在预哈希模式下(--prehash,默认开启),CLI 会在本地计算 PE 哈希并仅上传该哈希,也就是几百字节,而不是数兆字节的二进制文件。服务器对哈希进行签名并返回一个分离式 PKCS#7 数据块,客户端再把它嵌入文件。在带宽受限的 CI 代理上为一个大型安装程序签名时,这就把数兆字节的上传变成了一次极小的上传。
安全与运维
该服务器被设计为在真实签名密钥前无人值守地运行,因此运维层面的考量与加密本身同样重要:
- 管理控制台位于
/admin:仪表盘、API 密钥、提供程序、用户、项目以及审计轨迹,全部在浏览器中操作。 - API 密钥带有每密钥的速率限制和每日配额。达到限制的密钥会收到
429,并附带Retry-After请求头。 - 多租户项目,使得限定于某个团队的密钥无法使用另一个团队的提供程序。
- 审批工作流(可选):一种两步流程,签名请求会被搁置,直到操作员批准,适用于高价值的发布证书。
- 防篡改审计日志:每一次签名、验证、登录和配置更改都用哈希链接起来,因此记录在事后无法被篡改。
- Prometheus 指标位于
/api/v1/metrics,存活/就绪探针位于/api/v1/health,可随时接入你的监控栈。 - 出站 webhook,在每次签名操作时 POST 一个 JSON 事件,可把签名接入 Slack、SIEM 或发布看板。
- OpenAPI 3.1 规范位于
/api/v1/openapi.json,并在/api/v1/docs提供交互式 Swagger UI。 - TLS 1.2+,可选 ACME / Let's Encrypt 质询响应,外加用于 IP 过滤和滥用防护的内置防火墙。
为何要自托管
该服务器的意义在于,把一大批高风险、持有密钥的构建代理收敛为单个经过加固的端点。签名密钥只存在于服务器上,或存在于服务器所代理的 HSM 或云 KMS 中,永远不会落到运行你构建脚本的机器上。访问由带配额的每密钥 API 令牌把关,并限定到具体项目,而每一个签名都记录在你自己掌控的审计日志中。轮换证书、吊销某个已失陷代理的密钥,或者证明谁签了什么,都从原先的多处排查变成了只需看一处。
供应情况
sgcSign Server 是 sgcSign 2026.6.0 的一部分。它可作为 Windows 服务或控制台应用程序运行,由上面展示的那个单一 JSON 文件配置,并可通过 curl、随附的 sgcsign CLI 或任何借助其 OpenAPI 描述的 HTTP 客户端来驱动。
有疑问、反馈,或需要帮助完成搭建?联系我们。你将收到来自编写这些代码的人的回复。
