如何将Bank-Verlag返回的外部CMS签名嵌入WebViewer创建的PDF签名域(PDFTron Node.js SDK处理DigitalSignatureField报错)
如何将Bank-Verlag返回的外部CMS签名嵌入WebViewer创建的PDF签名域(PDFTron Node.js SDK处理DigitalSignatureField报错)
我完全懂你现在的困扰——这种客户端创建签名占位符、服务端嵌入外部签名的自定义流程,很容易在PDFTron的域处理、签名格式转换上踩坑。结合你给出的技术栈和代码片段,我来一步步帮你把问题解决掉。
先排查核心报错原因
你提到处理DigitalSignatureField时报错,大概率是这几个点出了问题:
- 服务端获取签名域时没有正确转换为
DigitalSignatureField类型 - Bank-Verlag返回的hex/base64编码签名没有转成PDFTron能识别的二进制数据
- 没有遵循自定义签名的增量保存规则,破坏了客户端预先创建的签名占位符
- 签名属性(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的源文件
关键注意事项
- 增量保存是核心:客户端创建占位符后用增量保存,服务端嵌入签名后也必须用增量保存,这样才不会破坏PDF的签名结构
- 签名编码转换:Bank-Verlag返回的hex/base64必须转成二进制,PDFTron的
setSignature只接受二进制数据 - 域类型验证:获取域后必须用
DigitalSignatureField.createFromField转换,直接用普通Field操作会报错 - SubFilter匹配:确保Bank-Verlag返回的是detached CMS/PKCS#7签名,完全匹配你要的
adbe.pkcs7.detachedSubFilter
内容来源于stack exchange




