使用IText SignDeferred签署文件时如何保留PDF-A [英] Howto keep PDF-A when signing a document using IText SignDeferred

查看:56
本文介绍了使用IText SignDeferred签署文件时如何保留PDF-A的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我确实使用IText通过延迟签名(SignDeferred)将签名应用于pdf文档. 该过程包含以下步骤:

I do apply a signature to a pdf document via delayed signing(SignDeferred) using IText. The process contains the following steps:

  • 准备pdf文件以便进行后续处理
    • 为pdf文档中的签名保留空间
    • Prepare the pdf document for siging
      • Reserve space for the signature in the pdf document
      • 使用自签名证书

      整个过程都有效,最后我得到了一个pdf文件,该文件中设置了签名并且有效.

      The whole process works and i end with a pdf document where the signature is set and is valid.

      原始pdf是 PDF-A1a ,但生成的pdf不再是有效的PDF-A1a. 我知道有一个有关IText PDF-A支持的文档(

      The original pdf is a PDF-A1a but the resulting pdf is not a valid PDF-A1a anymore. I am aware that there is a documentation about IText PDF-A support (https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-jump-start-tutorial-for-java/chapter-7-creating-pdf-ua-and-pdf-a-documents), but this seems to not apply since i don't change the content of the document.

      我的问题: 如何使用延迟签名应用签名并将PDF-A1a保留在生成的文档中?

      My question: How can i apply a signature using deferred signing and keep the PDF-A1a in the resulting document?

      注意:如果我确实直接应用了签名(没有SignDeferred),则生成的pdf仍然是PDF-A1a,但是我必须使用SignDeferred 注意:我确实使用 https://www.pdfen.com/pdf-a-validator用于检查pdf-A

      Note: If i do apply a signature directly (Without SignDeferred), the resulting pdf is still a PDF-A1a, but i do have to use SignDeferred Note: I do use https://www.pdfen.com/pdf-a-validator for checking pdf-A

      • 用于签名的组件:
        • itext.sign 7.1.5.0
        • itext.kernel 7.1.5.0
        • Component used for signing:
          • itext.sign 7.1.5.0
          • itext.kernel 7.1.5.0
          • BouncyCastle.Crypto 1.8.1.0

          以下是一个完整的代码示例示例,其中所有必需的内容都在一个文件中. 它只需要引用itext和BouncyCastle以及自签名证书的路径

          The following is a complete code sample sample with everything required in one file. It only requires the references to itext and BouncyCastle and the path to a self signed certificate

          using iText.Kernel.Pdf;
          using iText.Signatures;
          using Org.BouncyCastle.Pkcs;
          using Org.BouncyCastle.Security;
          using System;
          using System.Collections.Generic;
          using System.IO;
          
          namespace DeferredSigningTestConsole
          {
              class Program
              {
                  static string SignatureAttributeName = "DeferredSignature";
                  static string CertificatePath = @"C:\temp\PDFA\PdfATestCert.2pfx.pfx";
                  static string CertificatePassword = "test";
          
                  static void Main(string[] args)
                  {
                      var signedPdf = SignPdf(System.IO.File.ReadAllBytes(@"C:\temp\PDFA\PDF_A1a.pdf"));
                      System.IO.File.WriteAllBytes(@"C:\temp\PDFA\signed.pdf", signedPdf);
                  }
          
                  public static byte[] SignPdf(byte[] pdfToSign)
                  {
                      byte[] hash = null;
                      byte[] tmpPdf = null;
                      //Step #1 >> prepare pdf for signing (Allocate space for the signature and calculate hash)
                      using (MemoryStream input = new MemoryStream(pdfToSign))
                      {
                          using (var reader = new PdfReader(input))
                          {
                              StampingProperties sp = new StampingProperties();
                              sp.UseAppendMode();
                              using (MemoryStream baos = new MemoryStream())
                              {
                                  var signer = new PdfSigner(reader, baos, sp);
                                  signer.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);
          
                                  signer.SetFieldName(SignatureAttributeName);
                                  DigestCalcBlankSigner external = new DigestCalcBlankSigner(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
          
                                  signer.SignExternalContainer(external, 121743);
                                  hash = external.GetDocBytesHash();
                                  tmpPdf = baos.ToArray();
                              }
                          }
          
                          //Step #2 >> Create the signature based on the document hash
                          byte[] signature = GetSignatureFromHash(hash);
          
                          //Step #3 >> Apply the signature to the document
                          ReadySignatureSigner extSigContainer = new ReadySignatureSigner(signature);
                          using (MemoryStream preparedPdfStream = new MemoryStream(tmpPdf))
                          {
                              using (var pdfReader = new PdfReader(preparedPdfStream))
                              {
                                  using (PdfDocument docToSign = new PdfDocument(pdfReader))
                                  {
                                      using (MemoryStream outStream = new MemoryStream())
                                      {
                                          PdfSigner.SignDeferred(docToSign, SignatureAttributeName, outStream, extSigContainer);
                                          return outStream.ToArray();
                                      }
                                  }
                              }
                          }
          
                      }
                  }
          
                  public static byte[] GetSignatureFromHash(byte[] hash)
                  {
                      FileStream fs = new FileStream(CertificatePath, FileMode.Open);
                      Pkcs12Store store = new Pkcs12Store(fs, CertificatePassword.ToCharArray());
                      String alias = "";
                      foreach (string al in store.Aliases)
                          if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
                          {
                              alias = al;
                              break;
                          }
                      AsymmetricKeyEntry pk = store.GetKey(alias);
                      X509CertificateEntry[] chain = store.GetCertificateChain(alias);
          
                      List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
                      foreach (X509CertificateEntry en in chain)
                      {
                          c.Add(en.Certificate);
                      }
                      PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA256");
                      String hashAlgorithm = signature.GetHashAlgorithm();
                      PdfPKCS7 sgn = new PdfPKCS7(null, c.ToArray(), hashAlgorithm, false);
                      DateTime signingTime = DateTime.Now;
                      byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, null, null, PdfSigner.CryptoStandard.CMS);
                      byte[] extSignature = signature.Sign(sh);
                      sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
                      return sgn.GetEncodedPKCS7(hash, null, null, null, PdfSigner.CryptoStandard.CMS);
          
                  }
              }
          
              internal class DigestCalcBlankSigner : IExternalSignatureContainer
              {
                  private readonly PdfName _filter;
          
                  private readonly PdfName _subFilter;
          
                  private byte[] _docBytesHash;
          
                  internal DigestCalcBlankSigner(PdfName filter, PdfName subFilter)
                  {
                      _filter = filter;
                      _subFilter = subFilter;
                  }
          
                  internal virtual byte[] GetDocBytesHash()
                  {
                      return _docBytesHash;
                  }
          
                  public virtual byte[] Sign(Stream docBytes)
                  {
          
                      _docBytesHash = CalcDocBytesHash(docBytes);
                      //If we retun the signature bytes, GetAuthenticatedAttributeBytes will throw an exception
                      //Not clear how this should be done
                      return new byte[0];
                  }
          
                  public virtual void ModifySigningDictionary(PdfDictionary signDic)
                  {
                      signDic.Put(PdfName.Filter, _filter);
                      signDic.Put(PdfName.SubFilter, _subFilter);
                  }
          
                  internal static byte[] CalcDocBytesHash(Stream docBytes)
                  {
                      byte[] docBytesHash = null;
                      docBytesHash = DigestAlgorithms.Digest(docBytes, DigestUtilities.GetDigest(DigestAlgorithms.SHA256));
                      return docBytesHash;
                  }
              }
          
          
              internal class ReadySignatureSigner : IExternalSignatureContainer
              {
                  private byte[] cmsSignatureContents;
          
                  internal ReadySignatureSigner(byte[] cmsSignatureContents)
                  {
                      this.cmsSignatureContents = cmsSignatureContents;
                  }
          
                  public virtual byte[] Sign(Stream docBytes)
                  {
                      return cmsSignatureContents;
                  }
          
                  public virtual void ModifySigningDictionary(PdfDictionary signDic)
                  {
                  }
              }
          }
          
          

          推荐答案

          似乎签名的pdf不再是有效的PDF-A1a的原因是签名的估计大小. 我已经使用了大约120kb的值作为签名.

          It seems the reason why the signed pdf is no longer a valid PDF-A1a is the estimated size for the signature. I have used a value of about 120kb for the signature.

          //doesn't work
          signer.SignExternalContainer(external, 121743);
          
          //does work
          signer.SignExternalContainer(external, 65000);
          

          这的概念记录在电子书"PDF文件的数字签名"中.来自iText.

          The concept of this is documented in the ebook "Digital Signatures for PDF documents" from iText.

          为了获得有效的pdf-A1a,最大大小似乎限制为65kb.

          It seems in order to get a valid pdf-A1a the maximum size is limited to 65kb.

          现在,当我添加视觉表示(签名图像)时,我将不得不测试这种方法是否有效,因为这就是我选择如此大的估计尺寸的原因.

          I now will have to test whether this works when i add a visual representation (signature image) since this was the reason i did choose such a large estimated size.

          我进行了更多测试,现在可以生成带有签名的有效pdf-A文档: pdf现在是有效的pdf-A,其估计大小已更改:

          I did some more testing and i am now able to produce valid pdf-A document with signatures: The pdf are now valid pdf-A with the changed estimated size:

          • 有效,估计大小为32'000/65'000
            • A1a
            • A1b
            • Valid with estimate size 32'000/65'000
              • A1a
              • A1b
              • A2a
              • A2b
              • A2u
              • A3a
              • A3b
              • A3u

              添加视觉表示(图像)时,pdf-A1a和pdf-A1b不再有效.

              When adding a visual representation (image), pdf-A1a and pdf-A1b are no longer valid.

              存在透明的软膜.从PDF 1.4开始,支持透明性.一些基于PDF的ISO标准禁止使用透明度.

              A transparent soft mask is present. Beginning with PDF 1.4 transparency is supported. Some PDF-based ISO standards prohibit the use of transparency.

              但这是我现在想找出的另一个问题.

              But this is another problem which i now try to figure out.

              这篇关于使用IText SignDeferred签署文件时如何保留PDF-A的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆