iText 7手动生成PKCS7签名无效,对接返回PKCS1的外部签名服务如何实现
问题解答
1. PKCS1签名嵌入PDF后无效的原因
PDF数字签名遵循PKCS#7/CMS规范,要求签名的对象不是直接的PDF文档原始哈希,而是DER编码的签名认证属性(AuthenticatedAttributes,也叫SignedAttributes)结构。你当前直接对PDF文档哈希做PKCS1签名,缺少了规范要求的SignedAttributes封装,PDF验签时会因为签名对象不匹配判定签名无效。
iText自带的PrivateKeySignature实现内部会自动生成符合规范的SignedAttributes结构,对该结构哈希后再做签名,因此生成的签名合法有效。
2. 基于外部返回的PKCS1、Timestamp、OCSP、CRL构造合法PKCS7的步骤
你无需避开iText的GetAuthenticatedAttributeBytes方法,Timestamp、OCSP、CRL属于PKCS7的未签名属性,不参与签名计算,不会和该方法的调用冲突,具体流程如下:
- 第一步:完成PDF签名预处理,计算得到待签名PDF原文的哈希值,该步骤和你之前对接
PrivateKeySignature的逻辑完全一致 - 第二步:初始化
PdfPKCS7容器,传入你的证书链、哈希算法、签名子过滤器等参数,调用GetAuthenticatedAttributeBytes方法,传入PDF原文哈希,得到待签名的认证属性字节流 - 第三步:对该字节流计算哈希,将哈希值传给外部签名服务,获取对应的PKCS1格式签名值
- 第四步:调用
SetExternalDigest方法,将外部返回的PKCS1签名值、签名算法标识传入PdfPKCS7容器 - 第五步:将外部返回的Timestamp写入PKCS7的未签名属性区,OCSP、CRL可以写入PKCS7未签名属性,也可以直接写入PDF的DSS字典实现LTV功能
- 第六步:调用iText的签名闭合接口,将完整的PKCS7容器写入PDF签名字段即可
3. 该场景下实际需要签名的内容
实际需要提交给外部签名服务做签名的内容,是严格按照ASN.1 DER规范编码的完整SignedAttributes结构字节流,该结构至少要包含两个强制属性:
contentType:固定OID为1.2.840.113549.1.7.1,标识内容类型为数据messageDigest:值为你预处理得到的PDF原文哈希值
如果添加了签名时间、自定义签名属性等,也需要包含在该结构内,且结构编码顺序不能改动,否则会导致签名失效。
代码示例(Java)
// 预处理得到PDF原文哈希 byte[] pdfHash = calculatePdfDigest(...); // 初始化PKCS7容器 PdfPKCS7 pkcs7 = new PdfPKCS7(null, certificateChain, "SHA256", PdfName.Adobe_PPKLite.getValue(), PdfName.Adbe_pkcs7_detached.getValue(), false); // 获取待签名的认证属性字节,直接传给外部服务做哈希签名即可 byte[] toSignBytes = pkcs7.getAuthenticatedAttributeBytes(pdfHash, CryptoStandard.CMS, null, null); // 调用外部签名服务拿到PKCS1签名值signBytes ... // 写入签名值 pkcs7.setExternalDigest(signBytes, null, "RSA"); // 写入时间戳、OCSP、CRL ... // 生成最终PKCS7容器写入PDF byte[] pkcs7Content = pkcs7.getEncodedPKCS7();
内容的提问来源于stack exchange,提问作者Manuel
相关产品推荐
相关产品推荐

