使用iText 7在2个步骤中对PDF文件进行数字签名,结果是损坏的文件。

huangapple go评论108阅读模式
英文:

Digitally sign a pdf file in 2 steps with iText 7, result is corrupted file

问题

我会为您翻译代码部分:

首先,我会翻译计算哈希和签名的部分:

  1. public static byte[] ComputeHash(string source, string temp, X509Certificate[] chains, string reason, string location, int qtySigns, int pageNumber)
  2. {
  3. IList<ICrlClient> crlList = new List<ICrlClient>();
  4. crlList.Add(new CrlClientOnline(chains));
  5. using (var reader = new PdfReader(source))
  6. {
  7. using (var os = new FileStream(temp, FileMode.OpenOrCreate, FileAccess.Write))
  8. {
  9. var signer = new PdfSigner(reader, os, new StampingProperties().UseAppendMode());
  10. var signatureAppearance = signer.GetSignatureAppearance();
  11. signatureAppearance
  12. .SetReason(reason)
  13. .SetLocation(location)
  14. .SetPageNumber(pageNumber)
  15. .SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION)
  16. .SetCertificate(chains[0])
  17. .SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53));
  18. var container = new EmptySignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
  19. signer.SignExternalContainer(container, 8192);
  20. byte[] hash = container.Hash;
  21. return hash;
  22. }
  23. }
  24. }

以下是签名容器部分的翻译:

  1. public class EmptySignatureContainer : IExternalSignatureContainer
  2. {
  3. private PdfDictionary _sigDic;
  4. public byte[] Hash;
  5. public byte[] Sign(Stream data)
  6. {
  7. string hashAlgorithm = "SHA256";
  8. try
  9. {
  10. this.Hash = DigestAlgorithms.Digest(data, hashAlgorithm);
  11. }
  12. catch (IOException e)
  13. {
  14. throw new GeneralSecurityException("EmptySignatureContainer signing exception", e);
  15. }
  16. return new byte[0];
  17. }
  18. public void ModifySigningDictionary(PdfDictionary signDic)
  19. {
  20. signDic.PutAll(_sigDic);
  21. }
  22. public EmptySignatureContainer(PdfName filter, PdfName subFilter)
  23. {
  24. _sigDic = new PdfDictionary();
  25. _sigDic.Put(PdfName.Filter, filter);
  26. _sigDic.Put(PdfName.SubFilter, subFilter);
  27. }
  28. }

接下来是计算哈希的部分:

  1. var hash = ComputeHash(inputFile, tempFile, chain, "sign-reason", "", 1, 1);
  2. PdfPKCS7 sgn = new PdfPKCS7(null, chain, "SHA256", false);
  3. var authenAttrBytes = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
  4. var computedHash = SHA256Managed.Create().ComputeHash(authenAttrBytes);

最后,是签名和添加签名的部分:

  1. Sign(tempFile, outputFile, chain, authenAttrBytes, signedHash);

签名方法的翻译如下:

  1. public static void Sign(string tempFile, string targetFile, X509Certificate[] chains, byte[] hash, byte[] signedHash)
  2. {
  3. using (PdfReader reader = new PdfReader(tempFile))
  4. {
  5. using (FileStream outStream = System.IO.File.OpenWrite(targetFile))
  6. {
  7. var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
  8. var signer = new PdfSigner(reader, outStream, new StampingProperties());
  9. signer.SignExternalContainer(signedContainer, 8192);
  10. }
  11. }
  12. }

最后是签名容器的翻译:

  1. public class SignedSignatureContainer : IExternalSignatureContainer
  2. {
  3. public byte[] Hash { get; set; }
  4. public byte[] SignedHash { get; set; }
  5. public X509Certificate[] CertChains { get; set; }
  6. public SignedSignatureContainer(byte[] hash, byte[] signedHash, X509Certificate[] certCertChains)
  7. {
  8. this.Hash = hash;
  9. this.SignedHash = signedHash;
  10. this.CertChains = certCertChains;
  11. }
  12. public byte[] Sign(Stream data)
  13. {
  14. var sgn = new PdfPKCS7(null, CertChains, "SHA256", false);
  15. sgn.SetExternalDigest(this.SignedHash, null, "RSA");
  16. return sgn.GetEncodedPKCS7(this.Hash, CryptoStandard.CMS, null, null, null );
  17. }
  18. public void ModifySigningDictionary(PdfDictionary signDic)
  19. {
  20. signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
  21. signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
  22. }
  23. }

希望这有助于您理解代码。如果您有其他问题,请随时提出。

英文:

I'm trying to digitally sign a PDF file in two steps: compute hash, sign hash, and then merge the signed hash to the output file. The library that I'm using is iText 7.

However, the result is a corrupted file and cannot be opened.

What is wrong?

First I'm computing the hash and sign the input file with an empty signature container:

  1. public static byte[] ComputeHash(string source, string temp, X509Certificate[] chains, string reason, string location, int qtySigns, int pageNumber)
  2. {
  3. IList&lt;ICrlClient&gt; crlList = new List&lt;ICrlClient&gt;();
  4. crlList.Add(new CrlClientOnline(chains));
  5. using (var reader = new PdfReader(source))
  6. {
  7. using (var os = new FileStream(temp, FileMode.OpenOrCreate, FileAccess.Write))
  8. {
  9. var signer = new PdfSigner(reader, os, new StampingProperties().UseAppendMode());
  10. var signatureAppearance = signer.GetSignatureAppearance();
  11. signatureAppearance
  12. .SetReason(reason)
  13. .SetLocation(location)
  14. .SetPageNumber(pageNumber)
  15. .SetRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION)
  16. .SetCertificate(chains[0])
  17. .SetPageRect(new iText.Kernel.Geom.Rectangle(36, 20, 144, 53));
  18. var container = new EmptySignatureContainer(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
  19. signer.SignExternalContainer(container, 8192);
  20. byte[] hash = container.Hash;
  21. return hash;
  22. }
  23. }
  24. }

Here is the container:

  1. public class EmptySignatureContainer : IExternalSignatureContainer
  2. {
  3. private PdfDictionary _sigDic;
  4. public byte[] Hash;
  5. public byte[] Sign(Stream data)
  6. {
  7. string hashAlgorithm = &quot;SHA256&quot;;
  8. try
  9. {
  10. this.Hash = DigestAlgorithms.Digest(data, hashAlgorithm);
  11. }
  12. catch (IOException e)
  13. {
  14. throw new GeneralSecurityException(&quot;EmptySignatureContainer signing exception&quot;, e);
  15. }
  16. return new byte[0];
  17. }
  18. public void ModifySigningDictionary(PdfDictionary signDic)
  19. {
  20. signDic.PutAll(_sigDic);
  21. }
  22. public EmptySignatureContainer(PdfName filter, PdfName subFilter)
  23. {
  24. _sigDic = new PdfDictionary();
  25. _sigDic.Put(PdfName.Filter, filter);
  26. _sigDic.Put(PdfName.SubFilter, subFilter);
  27. }
  28. }

After that, I get the authenticated attribute bytes and compute its hash:

  1. var hash = ComputeHash(inputFile, tempFile, chain, &quot;sign-reason&quot;, &quot;&quot;, 1, 1);
  2. PdfPKCS7 sgn = new PdfPKCS7(null, chain, &quot;SHA256&quot;, false);
  3. var authenAttrBytes = sgn.GetAuthenticatedAttributeBytes(hash, PdfSigner.CryptoStandard.CMS, null, null);
  4. var computedHash = SHA256Managed.Create().ComputeHash(authenAttrBytes);`

The computedHash then will be sent to the hsm server to sign.
The signed hash then will be added back to the temporary file as following code:

Sign(tempFile, outputFile, chain, authenAttrBytes, signedHash);

And here is the Sign method:

  1. public static void Sign(string tempFile, string targetFile, X509Certificate[] chains, byte[] hash, byte[] signedHash)
  2. {
  3. using (PdfReader reader = new PdfReader(tempFile))
  4. {
  5. using (FileStream outStream = System.IO.File.OpenWrite(targetFile))
  6. {
  7. var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
  8. var signer = new PdfSigner(reader, outStream, new StampingProperties());
  9. signer.SignExternalContainer(signedContainer, 8192);
  10. }
  11. }
  12. }

The Signed container as following:

  1. public class SignedSignatureContainer : IExternalSignatureContainer
  2. {
  3. public byte[] Hash { get; set; }
  4. public byte[] SignedHash { get; set; }
  5. public X509Certificate[] CertChains { get; set; }
  6. public SignedSignatureContainer(byte[] hash, byte[] signedHash, X509Certificate[] certCertChains)
  7. {
  8. this.Hash = hash;
  9. this.SignedHash = signedHash;
  10. this.CertChains = certCertChains;
  11. }
  12. public byte[] Sign(Stream data)
  13. {
  14. var sgn = new PdfPKCS7(null, CertChains, &quot;SHA256&quot;, false);
  15. sgn.SetExternalDigest(this.SignedHash, null, &quot;RSA&quot;);
  16. return sgn.GetEncodedPKCS7(this.Hash, CryptoStandard.CMS, null, null, null );
  17. }
  18. public void ModifySigningDictionary(PdfDictionary signDic)
  19. {
  20. signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
  21. signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached);
  22. }
  23. }

答案1

得分: 0

在你的Sign方法中,你使用了PdfSigner方法的SignExternalContainer

  1. var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
  2. var signer = new PdfSigner(reader, outStream, new StampingProperties());
  3. signer.SignExternalContainer(signedContainer, 8192);

但这不会填充原先准备好的签名!相反,它会创建另一个签名字段,并使用原始签名的签名来填充它。

请改用静态的PdfSigner方法SignDeferred

  1. /// <summary>Signs a PDF where space was already reserved.</summary>
  2. /// <param name="document">the original PDF</param>
  3. /// <param name="fieldName">the field to sign. It must be the last field</param>
  4. /// <param name="outs">the output PDF</param>
  5. /// <param name="externalSignatureContainer">
  6. /// the signature container doing the actual signing. Only the
  7. /// method ExternalSignatureContainer.sign is used
  8. /// </param>
  9. public static void SignDeferred(PdfDocument document, String fieldName, Stream outs,
  10. IExternalSignatureContainer externalSignatureContainer)
英文:

In your Sign method you use the PdfSigner method SignExternalContainer:

  1. var signedContainer = new SignedSignatureContainer(hash, signedHash, chains);
  2. var signer = new PdfSigner(reader, outStream, new StampingProperties());
  3. signer.SignExternalContainer(signedContainer, 8192);

But that does not fill in the originally prepared signature! Instead this creates another signature field and fills it with a signature for the original one.

Use the static PdfSigner method SignDeferred instead:

  1. /// &lt;summary&gt;Signs a PDF where space was already reserved.&lt;/summary&gt;
  2. /// &lt;param name=&quot;document&quot;&gt;the original PDF&lt;/param&gt;
  3. /// &lt;param name=&quot;fieldName&quot;&gt;the field to sign. It must be the last field&lt;/param&gt;
  4. /// &lt;param name=&quot;outs&quot;&gt;the output PDF&lt;/param&gt;
  5. /// &lt;param name=&quot;externalSignatureContainer&quot;&gt;
  6. /// the signature container doing the actual signing. Only the
  7. /// method ExternalSignatureContainer.sign is used
  8. /// &lt;/param&gt;
  9. public static void SignDeferred(PdfDocument document, String fieldName, Stream outs,
  10. IExternalSignatureContainer externalSignatureContainer)

huangapple
  • 本文由 发表于 2023年4月19日 15:04:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76051603.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定