You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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)

错误原因

  1. 格式混淆:.p12(PKCS#12)是包含私钥、证书的容器格式,并非PKCS#8格式的纯私钥,不能直接用pkcs8参数导入
  2. 文件读取错误:读取.p12文件时使用了utf-8编码,二进制文件被文本编码损坏
  3. 不必要的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:18:09