You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何将Bank-Verlag返回的外部CMS签名嵌入WebViewer创建的PDF签名域(PDFTron Node.js SDK处理DigitalSignatureField报错)

如何将Bank-Verlag返回的外部CMS签名嵌入WebViewer创建的PDF签名域(PDFTron Node.js SDK处理DigitalSignatureField报错)

我完全懂你现在的困扰——这种客户端创建签名占位符、服务端嵌入外部签名的自定义流程,很容易在PDFTron的域处理、签名格式转换上踩坑。结合你给出的技术栈和代码片段,我来一步步帮你把问题解决掉。

先排查核心报错原因

你提到处理DigitalSignatureField时报错,大概率是这几个点出了问题:

  1. 服务端获取签名域时没有正确转换为DigitalSignatureField类型
  2. Bank-Verlag返回的hex/base64编码签名没有转成PDFTron能识别的二进制数据
  3. 没有遵循自定义签名的增量保存规则,破坏了客户端预先创建的签名占位符
  4. 签名属性(reason、location等)没有正确关联到签名域

修正后的服务端核心代码(pdfUtils.js)

我把你的embedSignatureWithCustomSigning函数完全重写并做了注释,解决了上述所有问题:

const PDFNet = require('@pdftron/pdfnet-node');

async function embedSignatureWithCustomSigning(
  inputPdf,
  outputPdf,
  certificatePem,
  signatureRaw, // 接受Bank-Verlag返回的hex或base64编码签名
  signerInfo = {},
) {
  const { 
    fieldName = "signature_1", 
    reason = "Document Authentication", 
    location = "New York", 
    contactInfo = "" 
  } = signerInfo;

  const LICENSE_KEY = "demo:1755098064645:6193876d03000000006e39758a90c67f666b37a158ff597ce711813124";

  let doc = null;
  let signatureSuccess = false;

  try {
    await PDFNet.initialize(LICENSE_KEY);
    doc = await PDFNet.PDFDoc.createFromFilePath(inputPdf);
    await doc.initSecurityHandler();
    await doc.lock();

    // 1. 正确获取并验证DigitalSignatureField
    console.log(`🔍 查找签名域: ${fieldName}`);
    const field = await doc.getField(fieldName);
    if (!field) {
      throw new Error(`未找到签名域: ${fieldName}`);
    }

    // 必须转换为DigitalSignatureField类型,否则会报错
    const digsigField = await PDFNet.DigitalSignatureField.createFromField(field);
    if (!digsigField || !(await digsigField.isValid())) {
      throw new Error(`无法将域转换为有效DigitalSignatureField`);
    }

    // 2. 将Bank-Verlag返回的签名转换为二进制数据
    let signatureBuffer;
    if (signatureRaw.match(/^[0-9a-fA-F]+$/)) {
      // 如果是hex编码,转成Buffer
      signatureBuffer = Buffer.from(signatureRaw, 'hex');
    } else {
      // 如果是base64编码,转成Buffer
      signatureBuffer = Buffer.from(signatureRaw, 'base64');
    }

    // 3. 将二进制签名设置到签名域中(对应adbe.pkcs7.detached)
    await digsigField.setSignature(Buffer.from(signatureBuffer));

    // 4. 设置签名证书(可选,但建议设置,用于PDF阅读器显示签名者信息)
    if (certificatePem) {
      // 将PEM证书转换为PDFTron的X509证书
      const certClean = certificatePem.replace(/-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----/g, '').trim();
      const certBuffer = Buffer.from(certClean, 'base64');
      const x509Cert = await PDFNet.X509Certificate.createFromBuffer(certBuffer);
      await digsigField.setCert(x509Cert);
    }

    // 5. 设置签名的元数据(reason、location等)
    await digsigField.setReason(reason);
    await digsigField.setLocation(location);
    await digsigField.setContactInfo(contactInfo);

    // 6. 完成签名并增量保存(必须用增量保存,否则会破坏客户端创建的签名占位符)
    await doc.save(outputPdf, PDFNet.SDFDoc.SaveOptions.e_incremental);
    signatureSuccess = true;
    console.log(`✅ 签名成功嵌入,已保存到: ${outputPdf}`);
    return { success: true, message: "签名嵌入成功" };

  } catch (err) {
    console.error(`❌ 嵌入签名失败: ${err.message}`);
    throw err;
  } finally {
    if (doc) {
      await doc.unlock();
      await doc.close();
    }
    await PDFNet.terminate();
  }
}

module.exports = { embedSignatureWithCustomSigning };

客户端代码的小提醒

确保你在WebViewer中完成签名占位符创建后,正确获取增量保存后的PDF文件传递到服务端:
在WebViewer的代码中,doc.saveMemoryBuffer(Core.PDFNet.SDFDoc.SaveOptions.e_incremental)返回的就是带占位符的二进制数据,你需要把这个数据上传到服务端,而不是原文件。比如在客户端加一段代码:

// 在calculateDigest之后
const signedPlaceholderBuffer = await doc.saveMemoryBuffer(Core.PDFNet.SDFDoc.SaveOptions.e_incremental);
// 把这个buffer上传到服务端,作为inputPdf的源文件

关键注意事项

  1. 增量保存是核心:客户端创建占位符后用增量保存,服务端嵌入签名后也必须用增量保存,这样才不会破坏PDF的签名结构
  2. 签名编码转换:Bank-Verlag返回的hex/base64必须转成二进制,PDFTron的setSignature只接受二进制数据
  3. 域类型验证:获取域后必须用DigitalSignatureField.createFromField转换,直接用普通Field操作会报错
  4. SubFilter匹配:确保Bank-Verlag返回的是detached CMS/PKCS#7签名,完全匹配你要的adbe.pkcs7.detached SubFilter

内容来源于stack exchange

火山引擎 最新活动