当前位置: 首页 > 知识库问答 >
问题:

如何使用iText IExternalSignatureContainer提前创建pdf哈希

唐啸
2023-03-14

我正在使用iText7对pdf文档应用签名。我还使用自己的IExternalSignatureContainer实现将证书集成到PKCS7 CMS中,因为签名服务只返回PKCS1签名。

签名过程是异步的(用户必须进行身份验证),我希望执行以下操作:

  • 准备文档(PDF阅读器)
  • 将文档的哈希值返回给用户
  • 扔掉文档(PDF阅读器)
  • 让用户进行身份验证(与iText签名过程没有直接关系)并创建签名(PKCS1)
  • 如果用户已通过身份验证,请重新准备文档并应用签名

这样做的原因是,我不需要把准备好的文档保存在内存中,也不需要批量签名。

我的问题是,创建的哈希值总是不同的。(即使我通过pdfSigner.SetSignDate将日期/时间设置为相同的值)或每个PdfReader/pdfSigner实例。

            //Create the hash of of the pdf document 
            //Part of my IExternalSignatureContainer Sign method
            //Called from iText pdfSigner.SignExternalContainer
            //The produced hash is always different
            byte[] hash = DigestAlgorithms.Digest(pdfStream, DigestAlgorithms.GetMessageDigest(hashAlgorithm));

问题:有没有办法

  • 在PdfReader的一个实例上“提前”生成pdf文档的哈希值

附件是该过程的完整示例(包括实际上需要由不同服务完成的签名创建)

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using iText.Kernel.Pdf;
using iText.Signatures;
using Org.BouncyCastle.X509;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

namespace SignExternalTestManuel
{
    class Program
    {
        const string filePath = @"c:\temp\pdfsign\";
        public static string pdfToSign = Path.Combine(filePath, @"test.pdf");
        public static string destinationFile = Path.Combine(filePath, "test_signed.pdf");
        public static string LocalUserCertificatePublicKey = Path.Combine(filePath, "BITSignTestManuel5Base64.cer");
        public static string LocalCaCertificatePublicKey = Path.Combine(filePath, "BITRoot5Base64.cer");
        public static string privateKeyFile = Path.Combine(filePath, "BITSignTestManuel5.pfx");
        public static string privateKeyPassword = "test";

        public static void Main(String[] args)
        {
            PdfReader reader = new PdfReader(pdfToSign);
            using (FileStream os = new FileStream(destinationFile, FileMode.OpenOrCreate))
            {
                
                StampingProperties stampingProperties = new StampingProperties();
                stampingProperties.UseAppendMode();
                PdfSigner pdfSigner = new PdfSigner(reader, os, stampingProperties);
                pdfSigner.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);

                IExternalSignatureContainer external = new GsSignatureContainer(
                    PdfName.Adobe_PPKLite,
                    PdfName.Adbe_pkcs7_detached);

                pdfSigner.SetSignDate(new DateTime(2021, 2, 22, 10, 0, 0));

                pdfSigner.SetFieldName("MySignatureField");
                pdfSigner.SignExternalContainer(external, 32000);
            }
        }
    }


    public class GsSignatureContainer : IExternalSignatureContainer
    {
        private PdfDictionary sigDic;


        public GsSignatureContainer(PdfName filter, PdfName subFilter)
        {
            sigDic = new PdfDictionary();
            sigDic.Put(PdfName.Filter, filter);
            sigDic.Put(PdfName.SubFilter, subFilter);
        }

        /// <summary>
        /// Implementation based on https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7#HowtouseaDigitalSigningService(DSS)suchasGlobalSign,withiText7-Examplecode
        /// </summary>
        /// <param name="pdfStream"></param>
        /// <returns></returns>
        public byte[] Sign(Stream pdfStream)
        {
            //Create the certificate chaing since the signature is just a PKCS1, the certificates must be added to the signature
            X509Certificate[] chain = null;


            string cert = System.IO.File.ReadAllText(Program.LocalUserCertificatePublicKey);
            string ca = System.IO.File.ReadAllText(Program.LocalCaCertificatePublicKey);
            chain = CreateChain(cert, ca);

            X509CrlParser p = new X509CrlParser();

            String hashAlgorithm = DigestAlgorithms.SHA256;
            PdfPKCS7 pkcs7Signature = new PdfPKCS7(null, chain, hashAlgorithm, false);

            //Create the hash of of the pdf document 
            //Part of my IExternalSignatureContainer Sign method
            //Called from iText pdfSigner.SignExternalContainer
            //The produced hash is always different
            byte[] hash = DigestAlgorithms.Digest(pdfStream, DigestAlgorithms.GetMessageDigest(hashAlgorithm));

            byte[] signature = null;

            //Create the hash based on the document hash which is suitable for pdf siging with SHA256 and a X509Certificate
            byte[] sh = pkcs7Signature.GetAuthenticatedAttributeBytes(hash, null, null, PdfSigner.CryptoStandard.CMS);
            //Create the signature via own certificate
            signature = CreateSignature(sh, Program.privateKeyFile, Program.privateKeyPassword);
            pkcs7Signature.SetExternalDigest(signature, null, "RSA");
            return pkcs7Signature.GetEncodedPKCS7(hash, null, null, null, PdfSigner.CryptoStandard.CMS);
        }

        public void ModifySigningDictionary(PdfDictionary signDic)
        {
            signDic.PutAll(sigDic);
        }

        private static X509Certificate[] CreateChain(String cert, String ca)
        {
            //Note: The root certificate could be omitted and it would still work
            X509Certificate[] chainy = new X509Certificate[2];
            X509CertificateParser parser = new X509CertificateParser();
            chainy[0] = new X509Certificate(parser.ReadCertificate(Encoding.UTF8.GetBytes(cert))
                .CertificateStructure);
            chainy[1] = new X509Certificate(parser.ReadCertificate(Encoding.UTF8.GetBytes(ca))
                .CertificateStructure);
            return chainy;
        }

        #region "Create signature, will be done by an actual service"
        private byte[] CreateSignature(byte[] hash, string privateKeyFile, string privateKeyPassword)
        {
            //Sign data directly with a X509Certificate
            X509Certificate2 rootCertificateWithPrivateKey = new X509Certificate2();
            byte[] rawData = System.IO.File.ReadAllBytes(privateKeyFile);
            rootCertificateWithPrivateKey.Import(rawData, privateKeyPassword, X509KeyStorageFlags.Exportable);

            using (var key = rootCertificateWithPrivateKey.GetRSAPrivateKey())
            {
                return key.SignData(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
            }
        }
        #endregion


    }
}

共有1个答案

邵麒
2023-03-14

问题:有没有办法

  • 在PdfReader的一个实例上“提前”生成pdf文档的哈希值
  • 创建签名
  • 在PdfReader的不同实例上应用签名

iText目前不支持此用例,尤其是在每个过程中

  • 将生成不同的PDF ID,
  • 使用不同的修改时间,并且
  • 在AES加密PDF的情况下,用于加密的随机数是不同的

在每一次传递中,可以修补ITEXT使用相同的值,但是在修补库之前,您应该考虑是否可以调整架构以使补丁不必要。

例如,在您的情况下,如果您不能保留原始的PdfSigner实例,另一种方法可以是让原始的PdfSigner在散列后用虚拟签名字节(例如new byte[0])存储其结果文件)。然后,在检索签名容器后,您可以使用PdfSigner.signDeferred将其注入到不同服务中的存储文件中,只要两个服务都可以访问共享存储(或者第一个服务至少可以将文件转发到第二服务的存储)。

 类似资料:
  • 我正在迁移一些代码(最初使用iText)来使用PdfBox进行PDF合并。除了创建PDF包或文件夹,一切都很好。我不得不承认,直到现在我才意识到它的存在。 这是我的代码片段(使用iText): 我需要这个,但与PdfBox。 我正在研究两者的 API 和文档,但找不到解决方案。任何帮助都会很棒。 附言。如果我给人留下印象,我需要在iText中解决方案,我需要它在PdfBox中,因为迁移是从iTex

  • 我用iText创建了一个pdf,我想打开它,但是当我这样做时,Adobe阅读器说我“打开文档时出错。此文件已打开或由其他应用程序使用“。我该如何解决? 这是我的代码(抱歉休斯顿println异常;)):

  • 问题内容: 目前,我正在用Java创建XML文件,并通过使用XSL / XSLT进行转换将其显示在JSP页面中。现在,我需要获取该XML文件并在PDF中显示相同的信息。有什么办法可以通过使用某种XSL文件来做到这一点? 我已经看过iText Java-PDF库,但是找不到任何将其用于XML和样式表的方法。 任何帮助将不胜感激。提前致谢! 问题答案: 您可以使用XSL格式对象。这里有一些很好的文章,

  • 问题内容: 我想创建一个(用键签名)的哈希 如何使用Node.js Crypto创建该哈希? 问题答案: 加密文档:http : //nodejs.org/api/crypto.html

  • null 有人能解释一下,当我使用PdfReader阅读模板后,我如何制作模板的副本吗?有没有办法把表格写到模板副本上,而不是一个新文档上? 为了将来的参考,我做了以下工作:

  • 问题内容: 我正在尝试为Laravel创建哈希密码。现在有人告诉我使用Laravel哈希帮助器,但我似乎找不到它,或者我的方向错误。 如何创建laravel哈希密码?在哪 编辑:我知道代码是什么,但我不知道在哪里以及如何使用它,因此它给了我哈希密码。如果得到哈希密码,则可以手动将其插入数据库 问题答案: 使用Bcrypt散列密码: 这将创建一个哈希密码。您可以在控制器中甚至在模型中使用它,例如,如