必须使用 rsa-pss 签名算法,因其是 nist、rfc 8017 和 gm/t 0015-2023 唯一合规方案;需指定 saltlength: 32、sha-256(或更高)、3072 位密钥及 publicexponent 为 65537,并确保报文编码与后端完全一致。

能,但必须用 RSA-PSS,不能用 RSA-SHA256 或手拼的签名流程;否则验签会失败,且无法与合规后端对齐。
为什么 RSA-PSS 是唯一推荐的签名算法
Web Crypto API 中的 RSA-PSS 是目前唯一被 NIST、RFC 8017 和国内 GM/T 0015-2023 明确要求用于数字签名的 RSA 填充方案。它通过随机盐值(salt)和掩码生成函数抵御长度扩展、重放等攻击,而旧式 RSA-PKCS1-v1_5(即常被误称为 “RSA-SHA256”)在签名场景中已被证明存在理论风险。
- 浏览器中调用
crypto.subtle.sign("RSA-PSS", ...)才算真正合规;用"RSASSA-PKCS1-v1_5"虽能运行,但多数金融/政务类后端会直接拒绝 - 签名时必须显式传入
{ name: "RSA-PSS", saltLength: 32 }——saltLength不填或填错(如设为 0)会导致后端验签失败 - SHA-256 是当前最低要求,不支持 SHA-1;若后端要求 SHA-384,需同步改
hash: "SHA-384"并确保密钥长度 ≥ 3072 位
generateKey 时必须指定 modulusLength 和 publicExponent
生成密钥对不是“随便选个长度就行”。RSA-PSS 签名对密钥强度有硬性依赖:
- 2048 位仅勉强满足基础场景,但部分国密合规平台已明确要求 3072 位起
-
publicExponent必须是new Uint8Array([0x01, 0x00, 0x01])(即 65537),不能用3或其他值,否则某些 FIPS 模块会拒收公钥 - 密钥导出时要用
exportKey("spki", publicKey)得到标准 PEM 公钥(Base64 编码的 ASN.1 结构),不能直接 JSON.stringify 密钥对象
签名前必须对原始报文做确定性编码
前端签名的对象不能是任意字符串或 JS 对象 —— 必须先序列化为字节流,且格式要与后端完全一致。常见错误是直接对 JSON 字符串签名,却忽略字段顺序、空格、换行等差异。
- 推荐做法:用
new TextEncoder().encode(JSON.stringify(payload)),但前提是后端也按相同方式序列化(例如 Go 的json.Marshal、Java 的 Jackson 默认无空格) - 更稳妥方式:约定报文结构为固定字段顺序的键值对数组,再拼接成
"key1=value1&key2=value2"形式,避免 JSON 解析歧义 - 绝对不要对未编码的 UTF-16 字符串(如直接传入中文字符串)调用
sign()——TextEncoder只处理 UTF-8,否则签名结果错乱
验签失败最常见的三个原因
即使签名逻辑看起来正确,crypto.subtle.verify() 返回 false 通常不是代码写错,而是环境或数据细节不匹配:
- 公钥格式不对:后端给的是 PEM(含
-----BEGIN PUBLIC KEY-----),前端没去掉头尾并 Base64 解码,而是直接传入字符串 - 签名值被二次 Base64 或 URL 安全转义:后端返回的 signature 是原始字节(Uint8Array),前端若用
btoa()再解码一次,就破坏了二进制完整性 - 时间戳或随机数未同步:若报文含
timestamp或nonce,前后端毫秒级时间差超窗口(如 5 分钟),验签必然失败,且不会报错,只返回 false
真正麻烦的不是写不出签名逻辑,而是密钥生成参数、报文编码规则、签名输入字节流这三者必须和后端逐字节对齐 —— 差一个空格、一个字节序、一个 salt 长度,整个链路就断在验签环节,且无明确错误提示。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











