使用xadesjs从.p12导入私钥实现XAdES-BES XML签名报错求助
解决xadesjs导入.p12私钥签名XML的解码错误
问题背景
在使用xadesjs实现XAdES-BES格式XML签名时,官方示例通过生成RSA密钥对完成签名,但实际需求是从本地.p12文件导入私钥,执行时出现以下错误:
Error: End of input reached before message was fully decoded (inconsistent offset and length values)
错误原因
- 格式混淆:.p12(PKCS#12)是包含私钥、证书的容器格式,并非PKCS#8格式的纯私钥,不能直接用
pkcs8参数导入 - 文件读取错误:读取.p12文件时使用了
utf-8编码,二进制文件被文本编码损坏 - 不必要的PEM处理:.p12是二进制DER格式,无需移除PEM的BEGIN/END标记,该操作会破坏文件结构
修正方案
步骤说明
- 以二进制格式读取.p12文件
- 使用
pkcs12格式导入整个容器,而非pkcs8 - 从导入结果中提取私钥和证书,用于XML签名
修正后的完整代码
const fs = require("fs"); const { Crypto } = require("@peculiar/webcrypto"); const xadesjs = require("xadesjs"); // 初始化加密引擎 const crypto = new Crypto(); xadesjs.Application.setEngine("NodeJS", crypto); // 签名算法配置(需与.p12中私钥算法匹配) const hash = "SHA-1"; const alg = { name: "RSASSA-PKCS1-v1_5", hash: { name: hash }, }; // .p12文件路径与密码(如果.p12有密码保护) const path = require("path"); const p12Path = path.resolve(__dirname, "cert.p12"); const p12Password = "your_p12_password"; // 替换为实际密码,无密码则传空字符串 // 读取二进制.p12文件 const p12Buffer = fs.readFileSync(p12Path); const p12Der = new Uint8Array(p12Buffer).buffer; // 导入PKCS#12容器,提取私钥和证书 xadesjs.Application.crypto.subtle.importKey( "pkcs12", p12Der, { name: "RSASSA-PKCS1-v1_5" }, // 指定算法类型 false, ["sign"] // 只导入签名权限的密钥 ) .then((keyPair) => { // keyPair中包含privateKey和certificate链 const privateKey = keyPair.privateKey; const certificate = keyPair.certificates[0]; // 获取第一个证书 // 待签名的XML字符串 const xmlString = '<player bats="left" id="10012" throws="right">\n\t<!-- Here\'s a comment -->\n\t<name>Alfonso Soriano</name>\n\t<position>2B</position>\n\t<team>New York Yankees</team>\n</player>'; // 调用签名函数 return SignXml(xmlString, privateKey, certificate, alg); }) .then((signedDocument) => { console.log("Signed document:\n\n", signedDocument); }) .catch((e) => { console.error(e); }); // 签名函数(基于官方示例修改) function SignXml(xmlString, privateKey, certificate, algorithm) { return Promise.resolve() .then(() => { const xmlDoc = xadesjs.Parse(xmlString); const signedXml = new xadesjs.SignedXml(); return signedXml.Sign( algorithm, privateKey, xmlDoc, { keyValue: certificate.publicKey, // 使用证书中的公钥 references: [ { hash: "SHA-256", transforms: ["enveloped"] } ], productionPlace: { country: "Country", state: "State", city: "City", code: "Code", }, signingCertificate: certificate // 直接传入证书对象,无需手动编码 } ); }) .then(signature => signature.toString()); }
关键修改点
- 读取.p12时使用默认的二进制读取(不指定encoding参数),避免文件损坏
- 导入密钥时指定格式为
pkcs12,而非pkcs8 - 从PKCS#12容器中提取私钥和证书,签名时使用证书的公钥和证书对象
- 处理Promise异步流程,确保密钥导入完成后再执行签名
内容的提问来源于stack exchange,提问作者Humberto Cueva
相关产品推荐
相关产品推荐

