
一、背景:为什么需要远程签名网关
随着信创改造与移动办公推进,越来越多业务系统要求关键操作(合同盖章、电子处方、财务审批、代码发布)必须具备不可否认的数字签名。但现实环境里,签名主体往往不在机房、不在办公桌前:研发在外地远程接入内网,财务在分行柜台用瘦终端,运维在出差途中用笔记本。
这类"无桌面、跨网络、弱信任"的场景,给签名这件事带来三个难题:
- 私钥放哪? 把用户SM2私钥直接存在应用服务器上,等于把印章交给机房保管,任何能读到磁盘镜像的人都能代签。
- 谁来确认? 服务端一旦拿到私钥就能"自动"签名,木马或越权后台进程可以静默伪造用户意愿。
- 怎么留证? 签名动作离开了用户本地设备,事后很难证明"当时确实是持卡人本人操作"。
远程签名网关的核心思想,就是把"签名运算"从服务端挪回用户手里,只让数据在网络上流动、不让私钥离开硬件。它不改变SM2签名算法本身,而是重构了"谁发起、谁计算、谁确认"的信任链。这种思路与零信任架构里"从不信任、始终校验"的原则天然契合:服务端不再默认拥有签名能力,每次运算都要回到用户侧硬件并接受显式授权。
从密码学视角看,签名的安全性由两个层面决定——算法强度和密钥管理。国密SM2本身基于椭圆曲线离散对数难题,在合理参数下具有足够安全余量;但再强的算法,只要私钥以明文形式出现在服务端内存或磁盘,等效于把保险箱密码写在门上。远程签名网关要解决的,正是密钥管理的最后一公里。
二、远程签名网关的架构拆解
远程签名网关采用四层链式结构,自上而下串起业务系统、网关、本地代理与UKey硬件:
[业务服务端] --内网通道--> [远程签名网关]
|
转发签名请求
|
[本地代理进程] <--长连接/隧道--> [远程签名网关]
|
| 本地 USB/PCSC 调用
v
[用户UKey硬件] ---- 私钥永不出芯片,SM2 运算在卡内完成
2.1 各层职责
| 层级 | 角色 | 关键职责 | 信任假设 |
|---|---|---|---|
| 业务服务端 | 签名请求发起方 | 构造待签原文、业务上下文、用户标识 | 不直接持有任何用户私钥 |
| 远程签名网关 | 路由与策略中枢 | 鉴权、防重放、会话绑定、审计落库 | 可信执行环境、独立密钥保护 |
| 本地代理 | 用户侧桥梁 | 维持隧道、弹出确认、调用UKey | 运行在用户可信终端 |
| UKey硬件 | 密码运算锚点 | SM2签名、私钥不可导出 | 物理持有即身份 |
2.2 信任链如何建立
整个通道的安全性依赖一条"由下而上"的信任链:
- 硬件锚点:UKey内置国密安全芯片,私钥在出厂或首次注册时于芯片内生成,CPU永远读不到明文私钥。
- 设备绑定:每张UKey有全球唯一KeyID,首次注册时把 KeyID 与用户主体(UserName)做强绑定并写入用户目录。
- 会话绑定:本地代理上线时,网关校验设备指纹+用户口令+UKey挑战应答,建立一条与本次会话绑定的短时效隧道。
- 请求闭环:每一次签名都必须回到本地代理,由UKey完成运算并回传签名值,网关仅做转发与审计。
值得补充的是,硬件锚点的可靠性取决于芯片自身。合格的国密安全芯片具备抗侧信道(功耗分析、时序分析)与防篡改设计,私钥生成与运算全程不离开安全边界。这也是"智能密码钥匙"区别于普通USB存储设备的根本:后者只是载体,前者是运算主体。
三、设备绑定与身份校验
要让远程网关知道"这次签名到底是谁的UKey在算",必须有可靠的设备绑定机制。实践中常见四级强度递增的方案:
| 方案 | 认证因子 | 说明 | 适用场景 |
|---|---|---|---|
| KeyID | 仅设备标识 | 网关按UKey序列号识别用户 | 内网低敏感 |
| UserName+KeyID | 账号+设备 | 双因子绑定,换设备即失效 | 通用业务 |
| 签名验签 | 设备+挑战应答 | 网关发随机数,UKey用私钥签名证明持有 | 高安全 |
| CA证书 | 设备+数字证书 | UKey内预置个人证书,走完整证书链校验 | 强合规 |
以安当UKey为例,其支持从 KeyID 到 CA证书的四级认证梯度:Web双因素登录可用 UserName+KeyID 模式,而远程签名网关这类强确认场景更适合"签名验签+CA证书"两级叠加,既验证设备持有,又验证证书合法性,避免仅凭证件被盗用。
设备绑定在数据侧体现为一张映射表,注册流程可抽象为:
1. 用户插入UKey,本地代理读取 KeyID 与设备指纹
2. 用户输入 PIN / 生物二次校验
3. 本地代理向网关提交 {KeyID, DeviceFingerprint, UserName}
4. 网关调用身份目录校验,写入绑定关系
5. 后续每次会话,网关用 KeyID 反查用户主体,拒绝未绑定设备
需要强调的是,绑定关系必须落在网关侧独立存储,而非写在客户端。若绑定表可被本地篡改,攻击者换一张UKey就能冒充他人。更进一步,绑定关系还应与用户生命周期联动:员工离职时由身份目录统一解绑,防止幽灵设备长期在线。
身份校验中的"挑战应答"值得展开:网关随机生成一串 nonce 发给本地代理,UKey用私钥对其做SM2签名后回传,网关用设备公钥验签。由于 nonce 不可预测且一次有效,即便攻击者截获了一次应答也无法重放,这是抵御中间人伪造设备的关键手段。
四、签名请求的二次确认:防木马静默签名
远程签名最大的隐患不是传输,而是"本地代理被控制后替用户自动答应"。木马一旦劫持了长连接,就能伪造业务上下文、诱使UKey对恶意原文签名。解决方案是二次确认——所有签名动作必须回到用户本人这一环做显式授权。
二次确认有三类实现:
- 弹窗确认:本地代理在用户桌面弹出"是否对以下业务单据签名"的可见窗口,展示原文摘要,用户点击确认后才调用UKey。
- 物理按键:部分智能密码钥匙带实体确认键,签名前需按一下,硬件层面阻断静默签名。
- 摘要比对:把待签原文做SM3杂凑,把摘要前若干字节展示给用户,用户对摘要负责而非对整篇长文负责。
代码层面,本地代理的确认逻辑可以这么写(伪代码):
def on_sign_request(req):
# req 来自网关,含原文hash与业务上下文
digest = sm3(req.payload) # SM3 摘要
shown = digest[:8] # 取前8字节给用户看
user_ok = show_confirm_dialog(
title="远程签名确认",
biz=req.biz_context,
digest_short=shown
)
if not user_ok:
return reject("user_abort")
# 只有用户确认后,才把原文送进UKey做SM2签名
sig = ukey.sm2_sign(req.payload)
return deliver(sig)
这里的关键是:UKey的签名函数调用,必须发生在用户确认之后、且位于本地进程内。任何试图把签名请求"先缓存后自动执行"的设计都违背了二次确认初衷。在工程实现上,建议把确认状态与签名调用放进同一个不可被打断的代码段(例如一个只能由用户UI事件触发的回调),并对超时做硬性拒绝——确认窗口停留过久应视为用户放弃,而非默认通过。
五、私钥永不出硬件:与"私钥上云"的风险对照
很多早期方案为图省事,把用户SM2私钥加密后存到服务器(所谓"私钥上云"),签名时下载到内存算出结果再擦除。表面看私钥"加密了",实则埋下系统性风险。下表从五个维度对照两种路线:
| 风险维度 | 私钥上云(服务端托管) | 远程签名网关(UKey本地算) |
|---|---|---|
| 私钥暴露面 | 磁盘镜像/内存快照/备份皆可还原 | 仅存于芯片,CPU读不到明文 |
| 越权代签 | 后台进程拿到解密密钥即可代签 | 无私钥,服务端无法代签 |
| 木马静默 | 内存中可自动完成签名 | 受二次确认/物理键约束 |
| 集中爆破 | 一处失守,全员私钥泄露 | 单点失守仅影响一张UKey |
| 合规举证 | 难证明"是本人意愿" | 设备绑定+确认+留痕可举证 |
可以看到,差距本质是私钥的"位置":放在服务端,攻击面随用户数线性扩大;放在每人的硬件里,风险被物理隔离到单设备。国密算法本身很安全,但密钥管理不当会让算法优势归零。
再深入一层:即便服务端对私钥做了硬件安全模块(HSM)加密托管,只要签名运算发生在服务端,就意味着"服务端能代表用户签名"。这在强合规场景(如《电子签名法》要求的可靠电子签名)下难以满足"签名制作数据由电子签名人专有控制"的要件。把运算锚点下沉到用户持有的UKey,正是为了让"专有控制"这一法律要件在技术上可落地。
六、请求与响应流程示例
下面给出远程签名网关一次完整SM2签名的时序,便于工程落地时对照实现。
【1】业务服务端
POST /api/remote-sign
body: { userId, bizType, plainText, nonce }
【2】远程签名网关
- 校验 userId 会话合法性
- 用 nonce 防重放
- 查 KeyID 绑定关系,定位用户本地代理隧道
- 转发 {plainText, bizType, reqId} 到本地代理
【3】本地代理
- 弹窗二次确认
- 调 UKey: SM2_Sign(plainText) // 私钥不出芯片
- 得到 sig
【4】本地代理 -> 网关
body: { reqId, sig, keyId, timestamp }
【5】远程签名网关
- 用 UKey 内公钥做 SM2 验签,确保签名真实
- 审计落库 {reqId, userId, keyId, bizType, ts}
- 回传 sig 给业务服务端
业务服务端调用示例(Python风格,网关地址由配置中心下发,不写死):
import hashlib, requests, json
def get_gateway_addr():
# 实际从内部配置中心或环境变量读取,避免硬编码
return CONFIG["sign_gw_addr"]
def request_remote_sign(user_id, plain_text):
nonce = hashlib.sha256(plain_text.encode()).hexdigest()[:16]
payload = {
"userId": user_id,
"bizType": "CONTRACT_SIGN",
"plainText": plain_text,
"nonce": nonce,
}
r = requests.post(get_gateway_addr(), json=payload, timeout=30)
if r.status_code != 200:
raise RuntimeError("签名网关返回异常: %s" % r.text)
data = r.json()
# 业务侧建议再用UKey公钥本地验签一次,做纵深防御
return data["sig"]
防重放方面,nonce 之外通常还要配合时间戳窗口:网关只接受时间戳在 ±N 秒内的请求,过期即拒。这样既防止重放攻击,也避免攻击者截获旧请求后延迟提交。
七、本地代理与UKey的调用示例
本地代理需通过PCSC或厂商C动态库与UKey通信。以国密UKey常见的C动态库接口风格为例:
/* 伪代码:本地代理调用UKey完成SM2签名 */
#include "ukey_api.h"
int do_remote_sign(const char* plain, unsigned char* sig_out) {
UKEY_HANDLE h = ukey_open(0); /* 打开第0张UKey */
if (h == NULL) return -1;
if (ukey_verify_pin(h, user_pin) != 0) {/* PIN校验 */
ukey_close(h);
return -2;
}
/* 私钥在芯片内,调用即卡内运算,明文私钥不回传 */
int len = ukey_sm2_sign(h, (const unsigned char*)plain,
strlen(plain), sig_out);
ukey_close(h);
return len > 0 ? 0 : -3;
}
工程上几点提醒:
- PIN校验失败应有次数锁定,防止暴力猜解。
- 签名函数返回的是签名值,不是私钥;若某接口返回了"私钥字节",应立即停用并上报。
- 本地代理进程应以最小权限运行,避免被其他进程注入。
- 多UKey并发场景下,
ukey_open(0)的索引应改为按 KeyID 精确匹配,防止插错设备签错人。
以安当UKey为例,其对外提供约两千三百个RESTful接口以及配套C动态库,本地代理既可走HTTP风格网关对接,也可直接LoadLibrary调用底层签名函数,两种形态在远程签名网关里可以并存:服务端用RESTful做编排,本地用C库做硬件交互。其固件本身带有固件签名,启动时校验完整性,可防范固件被替换导致的底层风险。
八、操作留痕与可举证
零信任通道不是"签完就完",而是要留下能还原当时场景的证据链。远程签名网关应至少记录以下字段:
| 字段 | 含义 | 用途 |
|---|---|---|
| reqId | 请求唯一ID | 串联全链路 |
| userId / keyId | 用户与设备 | 绑定关系举证 |
| bizType | 业务类型 | 区分合同/审批/发布 |
| plainDigest | 原文SM3摘要 | 证明签的是这份内容 |
| confirmType | 确认方式 | 弹窗/物理键/摘要 |
| ts / ip / geo | 时间位置 | 行为还原 |
| sig | 签名值 | 验签复核 |
留痕要满足"不可篡改",建议写入只追加(append-only)的审计存储,并定期做哈希链固化。当发生争议时,可拿出"设备绑定 + 用户确认 + 原文摘要 + 时间戳"四件套,证明签名确实由持卡人本人在当时场景下发起。
从法务角度,这条证据链还要能对外出示:审计记录应保留足够时长,且哈希链的根值可周期性写入独立存证系统,使任何一方都无法在事后悄悄修改某条签名记录而不被发现。这把"技术可信"进一步转化为"司法可采"。
需要特别说明的是,审计字段中的 plainDigest 必须用与签名一致的SM3算法对同一份原文计算,且原文在网关与本地代理之间传输时不得被中转环节改写。实践中常见做法是业务服务端在发起请求前先算好摘要并随请求带上,本地UKey签名时也基于同一原文,网关回程再做一次摘要比对,三处摘要一致才能认定"签的确实是这份、且没被掉包"。这一步虽小,却是防止"请求被篡改后签名"的最后一道闸。
九、部署与配置要点
落地远程签名网关时,配置上要守住几条底线:
- 隧道双向认证:网关与本地代理之间必须mTLS,单向往来会被中间人替换请求。
- 短时效会话:隧道票据设置分钟级过期,降低被长期劫持的风险。
- 原文不下沉隐私:网关只转发原文供UKey签名,不应在网关侧落库业务原文,仅留摘要。
- 失败即阻断:PIN错、设备未绑定、确认超时,一律返回拒绝,不做降级签名。
- 信创适配:在国产化操作系统与CPU平台上验证UKey驱动与PCSC服务可用,避免到现场才发现掉驱动。
一段最小化的网关策略配置示意:
remote_sign_gateway:
tunnel:
mtls: true
ticket_ttl: 300s # 会话票据5分钟过期
policy:
require_confirm: true # 强制二次确认
allow_plain_persist: false # 不落库业务原文
pin_lock_threshold: 5 # PIN错误5次锁定
audit:
append_only: true
hash_chain: true
性能上需要提醒:SM2签名本身在芯片内是毫秒级,整条链路时延主要花在隧道往返与二次确认的等待上。若业务峰值高,可通过"会话预热""批量确认"等方式降低人为等待占比,但绝不能为了吞吐而跳过确认环节——确认是安全性的硬约束,不是性能优化的牺牲品。
十、典型误区与规避
- 误区一:把UKey当单纯存储盘。UKey的价值在"芯片内运算",若仅用来存一个可导出的密钥文件,安全等级退回软证书。
- 误区二:确认弹窗可绕过。把确认做成"默认同意"或超时自动通过,等于取消二次确认。
- 误区三:网关代算。为性能把签名挪回网关、用导出的私钥算,直接破坏"私钥不出硬件"前提。
- 误区四:忽略留痕。只存签名值不存确认方式与设备绑定,事后无法举证。
- 误区五:绑定表放客户端。客户端可改则身份可被冒用,绑定必须服务端统管。
- 误区六:软硬混用无区分。同一业务同时允许软证书与UKey签名,攻击会专挑软证书路径,应强制硬件通道。
十一、软证书与硬件签名的本质差异
不少团队会问:既然都是SM2签名,为什么不直接用软证书?差异集中在三点。
第一,私钥生命周期。软证书私钥是一个文件,生成、备份、迁移都发生在通用计算环境,任何能读该文件的人都能签;硬件私钥在芯片内生成、不可导出,备份只能以"再烧一张同权UKey"的受控方式进行。
第二,运算位置。软证书签名用CPU算,私钥明文必然进内存;硬件签名在芯片内完成,内存里见不到私钥,侧信道与内存取证都拿不到关键材料。
第三,意愿绑定。软证书签名可由任意进程发起,难以证明"是本人";硬件签名配合PIN、物理键与弹窗确认,把"意愿"锚定在持卡人当场行为上。
这三点叠加,决定了在远程接入、强合规、高价值操作的场景下,硬件级签名是不可替代的底座能力,而非锦上添花。
补充一个工程权衡:硬件签名并非没有代价。每笔签名都依赖用户在线、设备插入、本人确认,这在离线或无人值守批处理场景下会成为瓶颈。对此常见的折中是"高频低风险用软证书、低频高风险用硬件签名"的分级策略,但必须保证两类路径在策略上彼此隔离、不可相互降级,否则攻击者仍会专挑软证书路径突破。分级的边界应由业务风险而非性能便利来划定。
方案参考
面向无桌面、跨网络的远程接入签名场景,落地硬件级国密签名时建议从以下维度做选型与风险规避:
架构选型要点
- 优先选择"服务端发请求、本地UKey运算"的网关模式,避免私钥集中托管带来的批量泄露面。
- 明确信任锚点放在用户侧硬件,网关只承担路由、策略与审计,不持有任何用户私钥。
- 在信创环境下提前验证UKey驱动、PCSC中间件与目标操作系统的兼容性,把适配问题消灭在上线前。
身份与确认设计
- 根据业务敏感度选择认证梯度:低敏感可用 KeyID 绑定,高敏感应叠加签名验签乃至CA证书。
- 二次确认必须回到用户本人在场环节,提供可见的原文摘要与业务上下文,杜绝后台静默签名。
- PIN等因子需具备错误锁定与尝试次数限制,防止本地暴力破解。
通道与策略
- 网关与本地代理之间使用双向认证隧道,会话票据短时效、可撤销。
- 失败一律阻断,不提供"降级软签"兜底,防止被诱导走非硬件路径。
- 审计只追加、做哈希链固化,记录请求ID、用户、设备、业务类型、原文摘要、确认方式与时空信息,支撑事后举证。
风险规避清单
- 任何把私钥导出到服务端内存或磁盘的方案都应一票否决。
- 不信任客户端上报的绑定关系,设备—用户映射须由独立目录统一管辖。
- 业务服务端建议用UKey公钥再做一次本地验签,形成纵深防御。
- 定期复核留痕完整性与UKey固件签名,防范固件被替换导致的底层风险。
- 员工离职或设备遗失时,由身份目录统一解绑,避免幽灵设备长期在线。

802

被折叠的 条评论
为什么被折叠?



