认证(口令)
本服务对外,所有签名请求都必须带口令。没有口令或口令错误,一律返回
401 Unauthorized,文件不会被签名。
X-Sign-Secret: <口令>SIGN_SECRET,或 CI 的 Secretexport SIGN_SECRET='<向运维索取>'
curl -X POST "https://sign.firstarpc.com/sign?ext=.exe" \
-H "X-Sign-Secret: $SIGN_SECRET" \
--data-binary @NovaStudio.exe \
-o NovaStudio.signed.exe
SIGN_SECRET,否则会出现大批 401;网页上已登录的浏览器也会被要求重新登录。
网页登录
浏览器打开 https://sign.firstarpc.com/ 会先看到登录页,输入同一个口令即可进入签名页面。未登录时签名页面和拖拽功能都不显示, 页面内容也不会下发。
X-Sign-Secret 请求头概览
签名服务是一台插着 EV 证书 USB token 的 Windows 机器。它把你的文件交给
signtool.exe 签名并附加 GlobalSign 时间戳,然后原样返回。
内网直连:http://<签名机>:38186(不经过公网,但为明文)
X-Sign-Secret,向运维索取application/octet-stream),不是 multipart快速开始
四步:拿到口令 → 调 /health 确认服务在线 → 带口令 POST 文件 → 保存响应。
1. 拿到口令
向运维索取统一的共享口令,存进本机环境变量:
[Environment]::SetEnvironmentVariable("SIGN_SECRET", "<口令>", "User")
# 重开终端后生效
2. 确认服务在线
curl https://sign.firstarpc.com/health
# ok
3. 签名一个文件
export SIGN_SECRET='<向运维索取>'
curl -X POST "https://sign.firstarpc.com/sign?ext=.exe" \
-H "X-Sign-Secret: $SIGN_SECRET" \
--data-binary @NovaStudio.exe \
-o NovaStudio.signed.exe
4. 验证结果
signtool verify /pa /v NovaStudio.signed.exe
Successfully verified 即成功。
不想写代码?用网页签
打开 https://sign.firstarpc.com/, 输入口令登录后,把文件拖进虚线框即可。签完自动下载,支持多选,多个文件会依次排队。 登录状态保持 7 天,期间不用重复输入。
API 参考
GET /health
探活接口,不需要口令,只返回服务是否在线。可用于监控和 CI 的前置检查。
| 状态码 | 响应 | 说明 |
|---|---|---|
| 200 | ok | 服务在线 |
POST /sign
必须携带口令,否则直接 401,请求体不会被处理。
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
X-Sign-Secret | Header | 是 | 共享访问口令,缺失或错误返回 401 |
ext | Query | 否 | 文件扩展名,默认 .exe;必须以 . 开头且不超过 10 个字符,否则按 .exe 处理 |
| 文件内容 | Body | 是 | 原始字节,Content-Type: application/octet-stream |
| 状态码 | 响应体 | 含义 / 处理 |
|---|---|---|
| 200 | 签名后的文件 | 直接写回原路径即可 |
| 401 | 空 | 缺少或错误的 X-Sign-Secret |
| 500 | signtool 输出(文本) | 签名失败:token 未插、PIN 失效、证书过期等。把这段文本发给运维 |
接入示例
PowerShell
$secret = $env:SIGN_SECRET
$resp = Invoke-WebRequest -Method Post `
-Uri "https://sign.firstarpc.com/sign?ext=.exe" `
-Headers @{ "X-Sign-Secret" = $secret } `
-InFile ".\NovaStudio.exe" `
-ContentType "application/octet-stream"
[IO.File]::WriteAllBytes(".\NovaStudio.signed.exe", $resp.Content)
Node.js(18+,内置 fetch)
const fs = require('node:fs');
const path = require('node:path');
async function signFile(file) {
const ext = path.extname(file) || '.exe';
const res = await fetch(
`https://sign.firstarpc.com/sign?ext=${encodeURIComponent(ext)}`,
{
method: 'POST',
headers: {
'X-Sign-Secret': process.env.SIGN_SECRET,
'Content-Type': 'application/octet-stream',
},
body: fs.readFileSync(file),
},
);
if (!res.ok) {
throw new Error(`签名失败 ${res.status}: ${await res.text()}`);
}
fs.writeFileSync(file, Buffer.from(await res.arrayBuffer()));
return file;
}
await signFile('dist/NovaStudio.exe');
Python(requests)
import os
import requests
url = "https://sign.firstarpc.com/sign"
with open("NovaStudio.exe", "rb") as f:
resp = requests.post(
url,
params={"ext": ".exe"},
headers={"X-Sign-Secret": os.environ["SIGN_SECRET"]},
data=f,
timeout=600,
)
resp.raise_for_status()
with open("NovaStudio.signed.exe", "wb") as f:
f.write(resp.content)
electron-builder
在 win.json 里加入 sign 钩子。钩子会被调用多次(应用 exe、NSIS 安装包等),全部交给服务处理即可。
const fs = require('node:fs');
const path = require('node:path');
const sign = async ({ path: file }) => {
const ext = path.extname(file) || '.exe';
const res = await fetch(
`https://sign.firstarpc.com/sign?ext=${encodeURIComponent(ext)}`,
{
method: 'POST',
headers: {
'X-Sign-Secret': process.env.SIGN_SECRET,
'Content-Type': 'application/octet-stream',
},
body: fs.readFileSync(file),
},
);
if (!res.ok) {
throw new Error(`签名失败: ${await res.text()}`);
}
fs.writeFileSync(file, Buffer.from(await res.arrayBuffer()));
};
module.exports = sign;
CI 接入
把口令存进 CI 的 Secret(GitHub Actions 用 secrets.SIGN_SECRET),
在构建产物生成之后、上传 artifact 之前执行签名。
GitHub Actions
- name: Sign artifacts
env:
SIGN_SECRET: ${{ secrets.SIGN_SECRET }}
run: |
curl --fail --show-error \
-X POST "https://sign.firstarpc.com/sign?ext=.exe" \
-H "X-Sign-Secret: $SIGN_SECRET" \
--data-binary @dist/NovaStudio.exe \
-o dist/NovaStudio.exe.signed
mv dist/NovaStudio.exe.signed dist/NovaStudio.exe
常见问题
返回 401 Unauthorized
请求头 X-Sign-Secret 缺失或与签名机上的 SIGN_SECRET 不一致。
检查是否用了旧口令、环境变量是否在 CI 里正确注入,以及 header 名是否拼写正确。
返回 500,响应里有 signtool 输出
说明请求到了签名机,但签名本身失败。常见原因:USB token 未插入、token PIN 失效、 证书过期。把返回的文本原样发给运维,不要只发状态码。
请求很久没有响应 / 客户端超时
可能前面有别的签名任务在排队(服务是串行的),也可能文件很大导致上传慢。
服务端单次签名超时是 180 秒;客户端建议设置 10 分钟超时。如果持续超时,先访问
/health 看服务是否在线。
可以同时提交多个文件吗
可以。服务端会按到达顺序排队串行签名,请求不会失败,只是需要等待。 自己实现时建议也串行提交,避免在服务端堆积。
支持哪些文件类型
PE 文件:exe、dll、sys。不要传 zip、msi、脚本或驱动 CAB —— 它们不会被正确签名。 MSI 等安装包请先构建成 PE 后再签,或改用相应的签名流程。
签名后文件大小会变化吗
会变,通常增加几 KB 到几十 KB(证书链 + 时间戳)。这是正常的,不要按原始大小校验。
怎么验证签名是否有效
在 Windows 上执行:
signtool verify /pa /v NovaStudio.signed.exe
看到 Successfully verified 即成功。也可以用 Get-AuthenticodeSignature。
口令应该怎么保管
只放在个人环境变量或 CI Secret 里,不要提交到 Git,也不要贴在群里。
口令是共享的,一旦外泄请立即联系运维更换签名机上的 SIGN_SECRET,
换完之后旧口令立即失效——包括还在用它的人,所以更换后要同步通知所有构建机。
网页登录过期了 / 换台电脑还要再登录吗
需要。登录状态是保存在当前浏览器的 cookie,有效期 7 天,不会跨浏览器、跨电脑同步。 过期或清理浏览器数据后,重新打开页面输入口令即可。构建机 / CI 不使用登录,不受影响。
登录提示「尝试次数过多」
同一 IP 在 5 分钟内输错 12 次会暂停登录,等待 5 分钟后自动恢复。 请确认口令是从运维处获取的最新版本,注意不要带上多余的空格。
内网和公网地址怎么选
构建机在内网且能直连签名机时,用 http://<签名机>:38186,速度更快;
否则用 https://sign.firstarpc.com。两者接口完全一致。
支持
口令申请、token/PIN 异常、证书过期,或者这份文档没覆盖到的情况,直接找运维。
GET /health(无需口令)SIGN_SECRET