使用iText7重签PDF时签名无效的技术求助
PDF签名无效问题:更新签名外观后重算哈希导致的验证失败
问题场景
先生成待签哈希发送至签名服务器,获取签名者证书信息后,更新已创建的签名外观对象的Layer2文本,再重新计算待签哈希并返回给签名服务器,但最终生成的签名无法通过验证。
问题原因
当你第一次调用pdfSigner.SignExternalContainer()时,iText已经基于当前签名外观的状态生成了待签名的PDF结构——包括签名外观的所有内容(比如Layer2文本),这个完整的结构会被纳入待签哈希的计算范围。后续修改Layer2文本后,PDF的实际内容已经发生变化,但你只是复用了之前的PdfSigner对象重新计算哈希,此时的哈希对应的还是第一次的PDF结构,和最终要签名的PDF内容不匹配,自然导致签名无效。
解决方案
方案1:先获取证书再设置签名外观(最优)
如果签名服务器允许先获取证书信息,推荐采用这个流程,避免重复操作:
// 1. 先从签名服务器获取签名者证书(实际替换为你的服务器调用逻辑) X509Certificate2 signerCert = GetSignerCertificateFromServer(); // 从证书中提取需要显示的信息,比如签名者姓名 string signerDisplayName = signerCert.Subject.Split(',').FirstOrDefault(s => s.StartsWith("CN="))?.Replace("CN=", "") ?? "Unknown Signer"; // 2. 初始化PDF签名流程,设置正确的签名外观 Stream readerStream = new MemoryStream(documentBytes); PdfReader pdfReader = new PdfReader(readerStream); PdfSigner pdfSigner = new PdfSigner(pdfReader, outputStream, new StampingProperties()); pdfSigner.SetFieldName("Signature1"); PdfSignatureAppearance signatureAppearance = pdfSigner.GetSignatureAppearance(); // 使用证书信息设置Layer2文本 signatureAppearance.SetLayer2Text($"Signed by: {signerDisplayName}"); signatureAppearance.SetLocation("test"); signatureAppearance.SetContact("test"); signatureAppearance.SetPageNumber(1); signatureAppearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION); // 3. 生成待签哈希并获取签名 PreSigning external = new PreSigning(PdfName.Adobe_PPKLite, PdfName.ETSI_CAdES_DETACHED, "SHA256"); pdfSigner.SignExternalContainer(external, estimatedSize); byte[] docHash = external.getHash(); // 发送哈希到签名服务器获取最终签名 byte[] signatureBytes = GetSignatureFromServer(docHash, signerCert); // 4. 完成签名 external.SetSignature(signatureBytes);
方案2:必须先哈希再拿证书(重新初始化签名流程)
如果签名服务器强制要求先提交哈希才能返回证书,那么需要在获取证书后,重新初始化整个PdfSigner流程,确保修改后的签名外观被纳入最终哈希计算:
// 第一步:生成临时哈希用于获取证书(此哈希不用于最终签名) Stream tempReader = new MemoryStream(documentBytes); PdfReader tempPdfReader = new PdfReader(tempReader); PdfSigner tempSigner = new PdfSigner(tempPdfReader, new MemoryStream(), new StampingProperties()); tempSigner.SetFieldName("Signature1"); PreSigning tempExternal = new PreSigning(PdfName.Adobe_PPKLite, PdfName.ETSI_CAdES_DETACHED, "SHA256"); tempSigner.SignExternalContainer(tempExternal, estimatedSize); byte[] tempHash = tempExternal.getHash(); // 提交临时哈希到服务器,获取签名者证书 X509Certificate2 signerCert = GetSignerCertificateFromServer(tempHash); string signerDisplayName = signerCert.Subject.Split(',').FirstOrDefault(s => s.StartsWith("CN="))?.Replace("CN=", "") ?? "Unknown Signer"; // 第二步:重新初始化签名流程,设置正确的Layer2文本 Stream finalReader = new MemoryStream(documentBytes); PdfReader finalPdfReader = new PdfReader(finalReader); PdfSigner finalSigner = new PdfSigner(finalPdfReader, outputStream, new StampingProperties()); finalSigner.SetFieldName("Signature1"); PdfSignatureAppearance finalAppearance = finalSigner.GetSignatureAppearance(); finalAppearance.SetLayer2Text($"Signed by: {signerDisplayName}"); finalAppearance.SetLocation("test"); finalAppearance.SetContact("test"); finalAppearance.SetPageNumber(1); finalAppearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION); // 生成最终待签哈希 PreSigning finalExternal = new PreSigning(PdfName.Adobe_PPKLite, PdfName.ETSI_CAdES_DETACHED, "SHA256"); finalSigner.SignExternalContainer(finalExternal, estimatedSize); byte[] finalDocHash = finalExternal.getHash(); // 获取最终签名并完成流程 byte[] finalSignature = GetSignatureFromServer(finalDocHash, signerCert); finalExternal.SetSignature(finalSignature);
关键说明
- 签名外观属于PDF内容的一部分,任何对外观的修改都会改变PDF的哈希值。
- PdfSigner在调用
SignExternalContainer()后,内部状态已固化,无法通过修改外观后重新计算出正确的哈希,必须重新初始化对象。
内容的提问来源于stack exchange,提问作者Muddassir Awan
相关产品推荐
相关产品推荐

