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

iText 7 pdf签名在手动创建时无效

郎翔
2023-03-14

我想使用iText 7对pdf文档进行数字签名。签名由仅返回PKCS1签名的外部服务创建。然后我必须创建和应用PKCS7。

iText中有一个很好的关于此场景的文档:https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7

我创建了一个通过本地证书签署pdf文档的示例应用程序。此示例应用程序可以从中克隆https://github.com/suntsu42/PdfSignSamplePkcs1.在这个示例应用程序中,有两种创建PKCS7的不同方法。一次手动,一次通过IExternalSignature(PrivateKeySignature)实现。

对于这两种情况,必须签名的pdf摘要以相同的方式创建。唯一的区别是PKCS7的创建方式。

github上的项目(https://github.com/suntsu42/PdfSignSamplePkcs1)是完整的和独立的。在resources文件夹中有一个私钥文件(pfx),用于创建签名和根证书。为了运行该示例,只需更改resourcePath变量的值以适应本地系统就足够了。

可以通过更改createSignatureViaPlainPkcs1的值来切换签名创建

using iText.Kernel.Pdf;
using iText.Signatures;
using System;
using System.IO;

namespace PdfSignSamplePkcs1
{
    class Program
    {
        static void Main(string[] args)
        {
            // TODO >> Change this path based on your local system
            var resourcePath = @"c:\project\github\PdfSignSamplePkcs1\Resources\";
            var pdfToSignPath = Path.Combine(resourcePath, "test.pdf");
            var signedPdfPath = Path.Combine(resourcePath, "signedPdf.pdf");
            var privateKey = Path.Combine(resourcePath, "SignTest.pfx"); // not critical, self signed certificate
            var privateKeyPassword = "test";

            // ############
            // Change value in order to create the PKCS7
            // either manually or via Itext
            // ############
            bool createSignatureViaPlainPkcs1 = false;

            //delete signed file if it exists
            if (System.IO.File.Exists(signedPdfPath))
                System.IO.File.Delete(signedPdfPath);
            var pdfToSign = System.IO.File.ReadAllBytes(pdfToSignPath);
            byte[] pdfDigest = null;


            //#1 Prepare pdf for signing
            var SignatureAttributeName = $"SignatureAttributeName_{DateTime.Now:yyyyMMddTHHmmss}";
            byte[] preparedToSignPdf = null;
            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, 32000);

                        //get digest to be signed
                        pdfDigest = external.PdfDigest;
                        preparedToSignPdf = baos.ToArray();
                    }
                }
            }

            //#2 Create PKCS7
            SignService ss = new SignService(pdfDigest, privateKey, privateKeyPassword);
            byte[] signatureAsPkcs7 = null;
            if (createSignatureViaPlainPkcs1)
                signatureAsPkcs7 = ss.CreatePKCS7ViaPkcs1(); // >> Creates invalid pdf signature
            else
                signatureAsPkcs7 = ss.CreatePKCS7(); // Creates valid pdf signature

            //#3 apply cms(PKCS7) to prepared pdf
            ReadySignatureSigner extSigContainer = new ReadySignatureSigner(signatureAsPkcs7);
            using (MemoryStream preparedPdfStream = new MemoryStream(preparedToSignPdf))
            {
                using (var pdfReader = new PdfReader(preparedPdfStream))
                {
                    using (PdfDocument docToSign = new PdfDocument(pdfReader))
                    {
                        using (MemoryStream outStream = new MemoryStream())
                        {
                            PdfSigner.SignDeferred(docToSign, SignatureAttributeName, outStream, extSigContainer);
                            System.IO.File.WriteAllBytes(signedPdfPath, outStream.ToArray());
                        }
                    }
                }
            }
        }
    }


}

在本示例中,首先使用本地证书创建PKCS1签名。然后,通过SetExternalDigest将创建的PKCS1签名应用于PdfPKCS7容器

以这种方式创建的pdf是无效的。

        public byte[] CreatePKCS7ViaPkcs1()
        {
            //Load the certificate used for signing
            signCertificatePrivateKey = LoadCertificateFromFile();

            // create sha256 message digest
            // This is from https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7
            // Not sure if this is required, but the created signature is invalid either way
            using (SHA256 sha256 = SHA256.Create())
            {
                Digest = sha256.ComputeHash(Digest);
            }

            //Create pkcs1 signature
            byte[] signature = null;
            using (var key = signCertificatePrivateKey.GetRSAPrivateKey())
            {
                signature = key.SignData(Digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
            }

            Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(signCertificatePrivateKey);
            PdfPKCS7 sgn = new PdfPKCS7(null, new[] { cert }, "SHA256", false);
            sgn.SetExternalDigest(signature, null, "RSA");

            //Return the complete PKCS7 CMS 
            return sgn.GetEncodedPKCS7(Digest, PdfSigner.CryptoStandard.CMS, null, null, null);
        }

在此示例中,PKCS7是使用iText私有密钥签名创建的。签名是用与其他示例相同的摘要和私钥创建的。

此处创建的pdf是有效的。但是由于这种方法不允许使用外部服务来创建签名,所以我不能使用它。

        public byte[] CreatePKCS7ViaPkcs1()
        {
            //Load the certificate used for signing
            signCertificatePrivateKey = LoadCertificateFromFile();

            // create sha256 message digest
            // This is from https://kb.itextpdf.com/home/it7kb/examples/how-to-use-a-digital-signing-service-dss-such-as-globalsign-with-itext-7
            // Not sure if this is required, but the created signature is invalid either way
            using (SHA256 sha256 = SHA256.Create())
            {
                Digest = sha256.ComputeHash(Digest);
            }

            //Create pkcs1 signature using RSA
            byte[] signature = null;
            using (var key = signCertificatePrivateKey.GetRSAPrivateKey())
            {
                signature = key.SignData(Digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
            }

            Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(signCertificatePrivateKey);
            PdfPKCS7 sgn = new PdfPKCS7(null, new[] { cert }, "SHA256", false);
            sgn.SetExternalDigest(signature, null, "RSA");

            //Return the complete PKCS7 CMS 
            return sgn.GetEncodedPKCS7(Digest, PdfSigner.CryptoStandard.CMS, null, null, null);
        }

我认为问题的原因是我没有使用GetAuthenticatedAttributeBytes来获得要签名的哈希。但我不能用这种方法。时间戳、ocsp和CLR作为服务调用的一部分返回。由于GetAuthenticatedAttributeBytes的参数必须与应用签名时相同,我想我无法使用此功能。

通过RSA创建的签名在生成的pdf中无效的原因是什么?编辑:更具体地说:当签名服务返回PKCS1、时间戳、Ocsp和CRL时,我如何创建有效的pkcs7容器。这种情况下到底要签什么?

共有1个答案

沈永贞
2023-03-14

有一个错误相当明显:

CreatePKCS7中,您对包含文档摘要(Digest)的签名容器的待签名属性进行签名:

var sh = sgn.GetAuthenticatedAttributeBytes(Digest, PdfSigner.CryptoStandard.CMS, null, null);
byte[] extSignature = signature.Sign(sh);

CreatePKCS7ViaPkcs1中,您对文档摘要(digest)本身进行签名:

//Create pkcs1 signature using RSA
byte[] signature = null;
using (var key = signCertificatePrivateKey.GetRSAPrivateKey())
{
    signature = key.SignData(Digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}

在这两种情况下,您都可以通过将返回的签名注入PdfPKCS7

sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());

sgn.SetExternalDigest(signature, null, "RSA");

分别。

第一个变量有效,这是一个指示器,告诉您,SetExternalDigest希望将待签名属性的签名(外部签名摘要)作为第一个参数,而不是直接将文档摘要的签名(外部签名摘要)作为第一个参数。

因此,在CreatePKCS7ViaPkcs1中,您只需签署错误的字节!

您可以通过(就像在CreatePKCS7中一样)在接收待签名属性之前创建PdfPKCS7实例(使用GetAuthenticatedAttributeBytes),来修复CreatePKCS7ViaPkcs1。您可能需要也可能不需要在签署之前对本协议的结果进行散列-我不太熟悉。NET加密API。

 类似资料:
  • 我试图使用以下工作流程创建PAdES签名: PDF准备签名,哈希在浏览器中计算 哈希发送到后端 后端形成分离的CAdES签名 分离的CAdES被发送回组装PAdES签名的浏览器 我们有一个PDF签名的工作示例,其工作原理如下: 准备好PDF,并在浏览器中计算哈希值 这很好用。 然而,现在我们在后端使用DSS库,而不是BouncyCastle,因为我们正在尝试创建PAdES签名。因此,DSS lib

  • 是否可以使用存储在Azure密钥库中的X509证书创建RSA-SHA1签名?“不可抵赖证明书” 不幸的是,我不能将哈希算法更改为SHA256或更安全的东西,我真的需要将证书作为密钥存储在Azure Key Vault中。 null

  • 数字签名允许我们验证签名的作者,日期和时间,验证消息内容。 它还包括用于其他功能的身份验证功能。 数字签名的优点 在本节中,我们将了解要求使用数字签名的不同原因。 将数字签名实施到通信有几个原因 - 身份验证 (Authentication) 数字签名有助于验证消息来源。 例如,如果银行的分支机构向中央办公室发送消息,请求更改帐户余额。 如果中央办公室无法验证从授权来源发送的消息,则执行此类请求可

  • 问题内容: 我使用一些手动创建的训练数据来训练IBK分类器,如下所示: 然后我建立分类器: 我想用未标记的类创建一个新实例并对该实例进行分类,但我没有运气尝试了以下方法。 我刚收到以下错误 看起来我在创建新实例时做错了什么。如何准确创建一个未标记的实例? 提前致谢 问题答案: 问题在于此行: 当您尝试分类时,Weka会引发异常,因为没有与之关联的Instances对象(即,数据集)-因此,它对类属

  • 我在react native上做了一个项目。一切都很好,每当我创建调试apk时,它也总是有效的。但现在我必须创建一个签名版apk,将其部署到google play商店。为此,我使用命令。我为构建添加了密钥库细节。gradle as<code>…android{…签名配置{release{storeFile(‘your_key_file_name.keystore’)storePassword‘yo