基于PDFBox跨服务器生成PDF数字签名,Adobe验证失败求助
我对数字签名概念不太熟悉,目前遇到以下问题:
我有两台服务器:
- Server A:接收上传的PDF文档,通过
MessageDigest接口使用SHA-256算法生成文档哈希值,将该哈希值传给Server B生成数字签名,之后使用PDFBox将Server B返回的签名加盖到PDF文档上。
详细流程:上传文档传入Server A的PDFBox代码,添加PDSignature,重写SignatureInterface的sign()方法,对方法传入的内容参数进行SHA-256哈希,将哈希值传给Server B生成签名,再将签名以byte[]形式返回至sign()方法,由PDFBox完成签名加盖。 - Server B:通过
BouncyCastleFipsProvider,使用SHA256WithRSA算法生成数字签名。
预期签名能在Adobe中被验证为有效,但打开生成的文档时,Adobe提示“文档已被更改或修改”。
要求流程必须为:Server A生成哈希值,Server B生成签名,Server A将签名加盖到PDF文档上。补充说明:曾尝试直接将PDFBox的内容字节传给Server B,生成的签名加盖后验证有效,但需求严格要求仅传递哈希值而非内容字节。
核心错误原因
你的处理方式存在关键错误:对PDFBox的sign()方法传入的待签内容做了重复哈希。
PDFBox的sign()方法传入的内容,已经是符合PDF签名规范的待签数据。而SHA256WithRSA算法本身包含「对输入数据做SHA-256哈希→用RSA私钥签名」的完整流程。你在Server A先对sign()的内容做一次SHA-256,再传给Server B用SHA256WithRSA签名,相当于对数据做了两次SHA-256哈希,导致最终签名的哈希值与Adobe验证时计算的哈希值不匹配,因此提示文档被修改。
修正方案(符合仅传哈希的需求)
1. Server A端处理
在sign()方法中,直接获取传入的待签内容字节数组,对其做一次SHA-256哈希(得到32字节的哈希值),然后将该哈希值传给Server B。
关键代码示例:
MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] contentHash = digest.digest(content); // content为sign()方法传入的待签字节数组 // 将contentHash传给Server B
2. Server B端处理
不要使用SHA256WithRSA算法,而是直接用RSA私钥对传入的哈希值做裸签名(仅执行RSA加密,不预先做哈希),同时必须使用符合PDF规范的填充方式(推荐PKCS1Padding)。
关键代码示例(BouncyCastle FIPS环境):
// 假设已初始化RSA私钥对象RSAPrivateKey privateKey Signature signature = Signature.getInstance("NONEwithRSA", "BCFIPS"); signature.initSign(privateKey); signature.update(contentHash); byte[] signedHash = signature.sign(); // 将signedHash返回给Server A
3. Server A端签名封装
将Server B返回的签名字节数组返回给sign()方法,同时确保创建PDSignature时正确设置签名算法属性:
PDSignature signature = new PDSignature(); signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); signature.setSignDate(Calendar.getInstance()); // 设置签名算法为SHA256WithRSA,确保Adobe验证时使用正确的逻辑 signature.setName("SHA256WithRSA");
方案可行性说明
PDF签名规范中,SHA256WithRSA的本质逻辑是:待签数据→SHA256哈希→RSA私钥加密(PKCS#1 v1.5填充)。修正后的流程完全匹配这一逻辑:Server A负责生成待签数据的SHA256哈希,Server B负责对哈希做RSA加密签名,最终Adobe验证时会重新计算待签数据的SHA256哈希,与解密签名得到的哈希值比对,匹配则验证通过。
内容的提问来源于stack exchange,提问作者Kim

