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

Node.js签名PDF配置正确但Adobe Acrobat验证无效求助

问题:Node.js签名PDF在Adobe Acrobat中无效且未正确识别TSA时间戳

背景信息

  • 项目需求:使用Node.js对PDF进行数字签名,要求使用.pfx证书并添加TSA时间戳
  • 已完成操作:
    • 使用@signpdf/signpdf库执行签名
    • 通过@signpdf/placeholder-plain添加ByteRange占位符
    • 配置TSA地址为https://freetsa.org/tsr
    • 确认.pfx证书与私钥匹配
  • 异常现象:
    • 签名后的PDF可通过巴西官方ITI验证器验证
    • 在Adobe Acrobat中显示签名无效,且提示“文档使用计算机本地时间签名”(已配置TSA)

相关代码

const fs = require('fs');
const { plainAddPlaceholder } = require('@signpdf/placeholder-plain');
const { SignPdf } = require('@signpdf/signpdf');
const { P12Signer } = require('@signpdf/signer-p12');

// Paths
const pdfPath = '../resources/pdf-entrada.pdf'; // Input PDF
const pfxPath = '../resources/certificado.pfx'; // PFX certificate
const signedPath = '../resources/output-signed.pdf'; // Final signed PDF
const pfxPassword = 'your_password_here'; // Certificate password
const tsaUrl = 'https://freetsa.org/tsr'; // TSA URL

async function signPdfWithTSA() {
  try {
    console.log('--- Start of Signing Process ---');
    console.log('[1] Reading the original PDF...');
    const pdfBuffer = fs.readFileSync(pdfPath);
    console.log(`[1] Original PDF size: ${pdfBuffer.length} bytes`);

    console.log('[2] Adding ByteRange (Placeholder for signature)...');
    const pdfWithPlaceholder = plainAddPlaceholder({
      pdfBuffer,
      reason: 'Digital Signature',
      contactInfo: 'email@example.com',
      name: 'Your Name',
      location: 'City / Country',
    });
    console.log(`[2] PDF with Placeholder size: ${pdfWithPlaceholder.length} bytes`);

    console.log('[3] Reading the PFX certificate...');
    const pfxBuffer = fs.readFileSync(pfxPath);
    console.log(`[3] PFX certificate size: ${pfxBuffer.length} bytes`);

    console.log('[4] Configuring signer with TSA...');
    const signer = new P12Signer(pfxBuffer, { passphrase: pfxPassword, tsaUrl });
    const signPdf = new SignPdf();

    console.log('[5] Signing the PDF...');
    const signedPdfBuffer = await signPdf.sign(pdfWithPlaceholder, signer);
    console.log(`[5] Signed PDF size: ${signedPdfBuffer.length} bytes`);

    console.log('[6] Saving the signed PDF...');
    fs.writeFileSync(signedPath, signedPdfBuffer);
    console.log(`[6] Signed PDF saved at: ${signedPath}`);
    console.log('--- End of Signing Process ---');
  } catch (error) {
    console.error('Error while signing the PDF:', error.message || error);
  }
}

signPdfWithTSA();

执行日志

--- Start of Signing Process ---
[1] Reading the original PDF...
[1] Original PDF size: 22368 bytes
[2] Adding ByteRange (Placeholder for signature)...
[2] PDF with Placeholder size: 39989 bytes
[3] Reading the PFX certificate...
[3] PFX certificate size: 9200 bytes
[4] Configuring signer with TSA...
[5] Signing the PDF...
[5] Signed PDF size: 39989 bytes
[6] Saving the signed PDF...
[6] Signed PDF saved at: ../resources/output-signed.pdf
--- End of Signing Process ---

可能的原因及排查步骤

  1. TSA时间戳未正确嵌入签名字典

    • Adobe要求时间戳必须嵌入到签名字典的TimeStamp字段中,需确认@signpdf/signer-p12是否正确将TSA返回的RFC 3161格式时间戳令牌写入该字段。可使用PDF解析工具(如PDFtk)查看签名字典结构,检查是否存在TimeStamp条目。
  2. 占位符生成未包含TSA相关字段

    • plainAddPlaceholder生成的签名模板可能缺少TimeStamp相关的预留配置,导致签名后无法被Adobe识别为带时间戳的签名。尝试手动扩展签名字典,添加TimeStamp占位字段,或使用支持TSA的占位符生成方法。
  3. @signpdf库的TSA实现存在兼容性问题

    • 部分版本的@signpdf/signer-p12可能存在TSA请求/响应处理的bug,建议升级到库的最新版本测试。也可替换为node-signpdf等其他成熟PDF签名库对比结果,排除库本身的问题。
  4. Adobe对TSA证书的信任限制

    • Adobe的信任列表可能未包含freetsa.org的根证书,导致无法验证时间戳有效性。可尝试手动导入该TSA的根证书到Adobe的信任存储,或更换为Adobe信任的TSA服务进行验证。
  5. 签名字节范围替换异常

    • 日志显示签名前后PDF大小完全一致,正常情况下嵌入签名值和时间戳后文件应略有增大。需检查SignPdf.sign方法是否正确替换了ByteRange占位符,是否存在签名值未写入PDF的情况。

内容的提问来源于stack exchange,提问作者Julio Martins

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 17:03:21