如何基于PDFBox为PDF签名添加外部可信时间戳?需更新哪些字典?
用PDFBox给现有签名添加可信第三方时间戳的方案
实现步骤
你要做的不是新增独立的文档时间戳,而是把第三方TSA(时间戳服务器)的可信时间戳嵌入已有的签名字典中,核心流程如下:
提取原签名的待验证哈希数据
从已签名PDF中取出签名覆盖的原始字节数据——也就是生成原签名时的待哈希内容,PDFBox可以通过PDSignature.getSignedContent(document)直接获取。对接可信TSA获取时间戳令牌
用PDFBox自带的TSAClientBouncyCastle类对接你的可信TSA服务器,把第一步得到的哈希值(要和原签名用的哈希算法一致,比如SHA-256)发过去请求时间戳令牌。如果TSA需要身份验证,传入对应的用户名和密码即可。把时间戳令牌嵌入原签名
将TSA返回的令牌放到原签名字典的Timestamp字段里,保存PDF完成操作。
关键代码片段:
// 加载已签名PDF PDDocument doc = PDDocument.load(new File("your-signed.pdf")); PDSignature existingSig = doc.getLastSignature(); // 初始化TSA客户端 TSAClient tsaClient = new TSAClientBouncyCastle( "https://your-trusted-tsa-url", "tsa-username", // 不需要验证则传null "tsa-password", // 不需要验证则传null MessageDigest.getInstance("SHA-256") ); // 获取原签名的待哈希数据并计算哈希 byte[] signedContent = existingSig.getSignedContent(doc); byte[] contentHash = MessageDigest.getInstance("SHA-256").digest(signedContent); // 请求TSA时间戳令牌 byte[] tsToken = tsaClient.getTimeStampToken(contentHash); // 将时间戳嵌入签名字典 existingSig.getCOSObject().setItem(COSName.TIMESTAMP, new COSStream(doc.getDocument(), new ByteArrayInputStream(tsToken))); // 保存修改后的PDF doc.save("signed-with-trusted-ts.pdf"); doc.close();
签名字典的更新要求
嵌入时间戳后,签名字典(Sig字典)必须做这些修改:
- 新增
Timestamp字段:值为TSA返回的时间戳令牌(以COSStream形式存储),这是PDF规范中关联可信时间戳到签名的标准字段。 - 可选更新
M字段:原M是你用系统时钟生成的签名时间,你可以把它改成TSA返回的时间戳时间,保证时间一致性;不过规范里M是签名者本地时间,时间戳是第三方权威时间,也可以保留原M,验证时优先识别Timestamp里的时间。 SubFilter字段:如果原签名是adbe.pkcs7.detached,这个字段不需要改动——时间戳令牌本身是符合RFC 3161的PKCS#7结构,会被PDF阅读器自动识别。
DSS/DTS字典的更新规则
如果你的PDF用到了文档安全存储(DSS)或文档时间戳存储(DTS),需要对应调整:
DSS字典(用于长期验证)
VRI条目:把TSA的证书链加入VRI下的Cert数组,确保验证时间戳时能找到可信的TSA证书锚点。TimeStamp条目:如果需要把这个时间戳作为独立的验证条目,可以添加到TimeStamp数组中;如果只是关联到原签名,也可以跳过这一步。
DTS字典(用于独立文档时间戳)
你现在是给现有签名附加时间戳,不是加独立的文档时间戳,所以通常不需要修改DTS字典——只有当你要给整个文档加独立于签名的时间戳时才用到DTS。
注意要点
- TS服务器必须是可信的,其证书要在你的信任链里,否则时间戳不会被认可。
- 哈希算法必须和原签名保持一致,比如原签名用SHA-256,请求TSA时也得用SHA-256,否则时间戳和原签名匹配不上。
- 绝对不能修改原签名的
Contents字段,否则原签名会直接失效——时间戳是附加信息,不是替换原签名内容。
内容的提问来源于stack exchange,提问作者Qazazazaz
相关产品推荐
相关产品推荐

