crypto.subtle.sign用hmac-sha256签名需满足三点:secret必须用importkey("raw",...)导入为cryptokey;待签名数据须为uint8array(textencoder.encode转换);输出arraybuffer需转hex比对。

crypto.subtle.sign 本身不区分“HMAC签名”或“RSA签名”——它只按你传入的算法对象执行对应逻辑。关键不是选函数,而是传对 algorithm 和 key;传错一个,就直接报错或结果不一致。
crypto.subtle.sign 用 HMAC-SHA256 签名必须满足这三点
常见错误:后端验签失败、InvalidAccessError、TypeError: key is not a CryptoKey,基本都卡在这三步上:
-
secret必须用importKey("raw", ...)导入,不能直接传字符串;format写成"jwk"或漏掉hash字段会静默失败 - 待签名数据必须是
ArrayBuffer或Uint8Array,sign()不接受字符串;TextEncoder.encode("...")是最安全的转换方式 - 输出是
ArrayBuffer,要转 hex 才能和后端比对:Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2,"0")).join("")
示例(HMAC):
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode("my-secret-key"),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const sig = await crypto.subtle.sign("HMAC", key, encoder.encode('{"id":123}'));
const hexSig = Array.from(new Uint8Array(sig))
.map(b => b.toString(16).padStart(2, "0"))
.join(""); // → "a1b2c3..."
crypto.subtle.sign 用 RSA-PSS 签名要注意密钥格式和参数
RSA 签名不是把私钥当字符串塞进去就行。私钥必须是 "pkcs8" 格式导入,且 algorithm 必须显式带 maskGenAlgorithm,否则 Chrome/Safari 拒绝执行:
- 生成密钥对时用
{ name: "RSA-PSS", modulusLength: 2048, publicExponent: new Uint8Array([1,0,1]), hash: "SHA-256" } - 导入已有私钥(如 PEM):先去掉头尾、base64 解码为
Uint8Array,再调importKey("pkcs8", keyData, algorithm, false, ["sign"]) - 签名时
algorithm必须完整:{ name: "RSA-PSS", hash: "SHA-256", saltLength: 32 };saltLength要和后端一致,常见为 20 或 32
注意:RSA 签名输出长度固定(如 2048 位 → 256 字节),但 hex 字符串长度恒为 512,别误以为出错了。
ECDSA 签名最容易踩的坑是 namedCurve 和 hash 不匹配
ECDSA 在前端很常用,但 namedCurve 和 hash 必须和后端严格一致,否则 verify() 一定返回 false:
- 主流组合只有两个:
namedCurve: "P-256"+hash: "SHA-256",或"P-384"+"SHA-384";混用(如 P-256 + SHA-384)会静默失败 - 私钥导入必须用
"pkcs8"格式,哪怕它是 EC 私钥;"spki"只用于公钥,"jwk"容易因字段缺失导致解析失败 - 签名结果是 DER 编码的 ASN.1 结构,长度不固定;转 hex 后首字节通常是
30,如果开头是00或ff,大概率是编码前没截掉高位补零
验证时也必须用同一套参数调 crypto.subtle.verify(),不能只换公钥。
所有签名操作都依赖安全上下文,HTTP 下直接不可用
crypto.subtle 在 HTTP 页面里会直接抛 SecurityError: The operation is insecure,连 importKey 都进不去。这不是 bug,是浏览器强制策略:
- localhost 是唯一允许的非 HTTPS 环境;CI 测试或本地开发务必用
http://localhost:xxxx,别用http://127.0.0.1或自定义 host - HTTPS 页面里,如果资源(如 JS 文件)被降级加载(mixed content),
subtle也会失效;检查控制台是否有 “This page is not secure” 提示 - Node.js 或 Deno 环境没有
window.crypto.subtle,要用crypto.webcrypto或第三方库替代
真正容易被忽略的是:签名过程本身不防重放、不绑定时间戳、不加密 payload —— 它只保证“这段数据确实由持有该密钥的人签发”,其余全靠业务层补足。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











