c# - 我的签名结构干扰了使用 itext7 嵌入签名
问题描述
我在我的应用程序中使用 itext7 版本 7.1.5。我的场景如下: - 获取文档的哈希 - 从外部签名服务器签署哈希并获取 Pkcs7 - 使用 itext7 将签名的 pkcs7 嵌入到 PDF 文档中
嵌入 adobe 后无法验证我在文档中的签名。当我检查签名结构时,签名结构是错误的。
这是逐步的代码:
1:文档散列:
string hashAlgorithm = "SHA256";
Stream documentStream = new MemoryStream(_latestDocumentBytes);
PdfSigner _pdfSigner = new PdfSigner(pdfReader, outputStream, new StampingProperties());
_pdfSigner.SetFieldName("Signature1");
ImageData imageData = ImageDataFactory.Create(imageBytes);
PdfSignatureAppearance sigAppearance = _pdfSigner.GetSignatureAppearance();
sigAppearance.SetContact("ContactInfo");
sigAppearance.SetLocation("Location");
sigAppearance.SetPageNumber(1);
sigAppearance.SetReason("SigningReason");
sigAppearance.SetSignatureGraphic(imageData);
sigAppearance.SetRenderingMode(sigAppearance.RenderingMode.GRAPHIC)
sigAppearance.SetSignatureCreator("Malik");
PdfSignature signature = new PdfSignature(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
signature.SetContact(sigAppearance.GetContact());
signature.SetDate(new PdfDate(DateTime.Now));
signature.SetLocation(sigAppearance.GetLocation());
signature.SetReason(sigAppearance.GetReason());
signature.SetSignatureCreator(sigAppearance.GetSignatureCreator());
signature.MakeIndirect(_pdfDocument);
documentHash = DigestAlgorithms.Digest(documentStream, DigestAlgorithms.SHA256);
SHA256 sha256 = new SHA256Managed();
byte[] documentHash = sha256.ComputeHash(documentHash);
2:从文档服务器获取签名哈希
3:将签名嵌入PDF
Stream readerStream = new MemoryStream(_latestDocumentBytes);
PdfPKCS7 pdfPKCS7 = new PdfPKCS7(pdfSignatureBytes, PdfName.Adbe_pkcs7_detached);
PdfSignatureAppearance signatureAppearance = _pdfSigner.GetSignatureAppearance();
signatureAppearance.SetCertificate(pdfPKCS7.GetSigningCertificate());
signature.SetContents(pdfSignatureBytes);
IExternalSignatureContainer externalSignatureContainer = new ExternalBlankSignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
_pdfSigner.SignExternalContainer(externalSignatureContainer, 8192);
_latestDocumentBytes = ((MemoryStream)outputStream).ToArray();
我的签名结构差异如下:
正确的签名结构:
<</Type/Sig/Reason(I have approved ad signed the document)/Contents ><[CONTENT]>/Prop_Build<</App<</Name/Malik>>>>/ByteRange [0 10857 522859 2584 >] >/SubFilter/adbe.pkcs7.detached/Filter/Adobe.PPKLite/M(D:20190719103520+00'00>>')/ContactInfo(923399999999)/Name(John Clark)/Location(Pakistan)>>
不正确(Itext 实现):
<</ByteRange [0 157 16543 260086 ] [Large Space] >/ContactInfo(email@email.com)/Contents ><[CONTENT]>/Filter/Adobe.PPKLite/Location(Pakistan)/M(D:20190719154813+05'00'>)/Prop_Build<</App<</Name/Malik>>>>/Reason(Test Signing >Reason)/SubFilter/adbe.pkcs7.detached/Type/Sig>>
在 Adobe 中打开签名的 PDF 时,在验证签名时显示错误,错误在解码时遇到
原始文件在这里: https ://www.dropbox.com/s/ajscg8j74opuwxe/SigFieldDoc%20-%20Original.pdf?dl=0
签名文件在这里: https ://www.dropbox.com/s/h72u360rl5iy6fq/SigFieldDoc%20-%20AfterSign.pdf?dl=0
在这方面的任何帮助将不胜感激。
解决方案
要使用远程服务器使用 iText 7 签署 PDF 以创建实际的加密签名,您可以简单地实现一个 iText 签名接口,该接口在其Sign
方法中针对给定数据执行远程服务的调用。
此外,您在评论中澄清说,您的
外部签名服务器返回 PKCS7 签名签名数据。
因此,要使用的接口是一个IExternalSignatureContainer
(而不是IExternalSignature
如果服务器仅返回原始签名字节则将使用的接口)。
我不知道您如何与服务器通信;特别是我不知道您使用的 API 是要求您提供完整的签名数据还是仅提供其哈希值;在前一种情况下,是否将它们作为流或数组提供。因此,我Sign
为每种情况在方法中添加了提示:
public class ExternalServiceSignatureContainer : IExternalSignatureContainer
{
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
}
public byte[] Sign(Stream data)
{
// Call your external signing service to create a CMS signature container
// for the data in the InputStream
// Depending on your API to access that service you may either be able to
// directly call it with the stream
// return YOUR_SIGNING_API_CALL_FOR_STREAM(data);
// (or a byte[] generated from the stream contents)
// return YOUR_SIGNING_API_CALL_FOR_ARRAY(StreamUtil.InputStreamToArray(data));
// as parameter, or you may first have to hash the data yourself
// (e.g. as follows) and send your hash to the service.
// byte[] hash = DigestAlgorithms.Digest(data, DigestAlgorithms.SHA256);
// return YOUR_SIGNING_API_CALL_FOR_HASH(hash)
// dummy
return new byte[0];
}
}
然后,您可以按如下方式使用此类:
PdfReader pdfReader = new PdfReader(DOCUMENT_TO_SIGN);
PdfSigner pdfSigner = new PdfSigner(pdfReader, RESULT_STREAM, new StampingProperties().UseAppendMode());
pdfSigner.SetFieldName("Signature1");
ImageData imageData = ImageDataFactory.Create(SIGNATURE_IMAGE);
PdfSignatureAppearance sigAppearance = pdfSigner.GetSignatureAppearance();
sigAppearance.SetContact("ContactInfo");
sigAppearance.SetLocation("Location");
sigAppearance.SetPageNumber(1);
sigAppearance.SetPageRect(new Rectangle(100, 500, imageData.GetWidth() / 2, imageData.GetHeight() / 2));
sigAppearance.SetReason("SigningReason");
sigAppearance.SetSignatureGraphic(imageData);
sigAppearance.SetRenderingMode(RenderingMode.GRAPHIC);
sigAppearance.SetSignatureCreator("Malik");
pdfSigner.GetDocument().GetCatalog().SetModified();
int estimatedSize = 12000;
pdfSigner.SignExternalContainer(new ExternalServiceSignatureContainer(), estimatedSize);
一些备注:
我添加了
sigAppearance.SetPageRect(new Rectangle(100, 500, imageData.GetWidth() / 2, imageData.GetHeight() / 2));
因为我也用了不同的文档来测试。由于该其他文档最初不包含名为Signature1的签名字段,因此我需要设置矩形以获得可见签名。如果您的文档已经有这样的签名字段,则可以删除该行,但无论如何都会被忽略。
线
pdfSigner.GetDocument().GetCatalog().SetModified();
由于 iText 中的一个小错误,需要添加:当签署现有字段(不是新字段)时,AcroForm字典(特别是如果直接包含在您的文档中的目录中)不会标记为已修改,因此, 在增量更新中不更新。这可以防止设置文档中的SigFlags,从而导致 Adobe Reader 不显示顶部签名栏。
您可能想要更改估算值
int estimatedSize = 12000;
签名容器大小,特别是如果您检索的签名容器总是小得多或有时大于 12000 字节。
推荐阅读
- angularjs - 下划线 indexOf() 在 AngularJS {{ }} 插值中不起作用
- java - java.util.ConcurrentModificationException - 抛出此异常的简单循环
- php - 更新 Woocommerce 中数量变化的购物车项目自定义数据
- angular - 打字稿:如何在对象更改时更新选择
- postgresql - 在archlinux中找不到“createlang”命令
- java - Spring Boot Security 在无效凭据后无法登录
- mysql - mysql 中的 COUNT 和 GROUP_CONCAT
- css - 如何在 Symfony 4 中为表单元素添加样式?
- c# - C# 查找和替换长字符串中的价格
- ruby-on-rails - Customise Mailer::DeliverJob