如何在JavaScript中验证欧盟新冠通行证的P-256 COSE数据签名
问题核心原因说明
ES256和P-256是匹配的:ES256算法就是指采用SHA-256哈希、P-256(secp256r1)曲线的ECDSA签名算法,你的公钥参数没有问题。验证失败主要来自两个底层错误:
- 待签名数据构造不符合COSE规范
- 签名格式不匹配加密库要求
问题1:待签名数据构造错误
COSE签名验证不能直接使用payload内容做哈希,必须按照COSE规范构造Sig_structure结构后,整体做CBOR编码再参与哈希:
Sig_structure固定为四元素数组:["Signature1", 受保护头原始Buffer, 外部附加数据空Buffer, payload原始Buffer]- 你之前仅传入
cbor_data做哈希,和签发时的待签名内容不一致,必然验证失败。
问题2:签名格式不匹配
COSE标准中使用的ECDSA签名是raw格式(r值+s值直接拼接,共64字节),而你用的ec-key库默认要求DER编码格式的签名,格式不匹配导致验证失败。
可运行的修正方案
推荐换用更符合规范的轻量加密库处理,修正后的代码示例如下:
const cbor = require("cbor"); const { p256 } = require("@noble/curves/p256"); const { sha256 } = require("@noble/hashes/sha256"); // 解码COSE消息 const result = cbor.decode(coseMessage); const [protectedHeadersRaw, unprotectedHeaders, payload, signatureRaw] = result.value; // 构造Sig_structure并CBOR编码 const sigStructure = [ "Signature1", protectedHeadersRaw, Buffer.alloc(0), // 欧盟新冠通行证无外部附加数据 payload ]; const sigStructureEncoded = cbor.encode(sigStructure); // 哈希待签名内容 const msgHash = sha256(sigStructureEncoded); // 验证签名,传入raw格式签名、公钥x/y坐标 const verified = p256.verify(signatureRaw, msgHash, { x: Buffer.from(pKey.x, 'base64url'), // 若x/y为普通base64格式可调整解码参数 y: Buffer.from(pKey.y, 'base64url') });
额外排查点
- 确认公钥的
x、y字段编码格式:JWK规范中默认是base64url编码,转换为Buffer时不要用普通base64解码 - 确认
protectedHeadersRaw是解码得到的原始Buffer,不要用解码后的JSON对象重新编码,否则会因为CBOR序列化的细微差异(比如整数长度、map键顺序)导致哈希不匹配
内容的提问来源于stack exchange,提问作者Simon H
相关产品推荐
相关产品推荐

