首页 > 解决方案 > 我的签名结构干扰了使用 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();

我的签名结构差异如下:

在 Adob​​e 中打开签名的 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

在这方面的任何帮助将不胜感激。

标签: c#pdfitext7

解决方案


要使用远程服务器使用 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,从而导致 Adob​​e Reader 不显示顶部签名栏。

您可能想要更改估算值

int estimatedSize = 12000;

签名容器大小,特别是如果您检索的签名容器总是小得多或有时大于 12000 字节。


推荐阅读