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

使用C在签名中添加LTV会使pdf无效#

陶涵育
2023-03-14

将LTV添加到数字签名后,它显示文档已更改。

参考后:LTV认证签名后,PDF显示“文档已更改”

我对代码进行了更改,它可以很好地用于除此文档以外的所有文档:https://www.sendspace.com/file/3ulwn7-显示无效签名。

我们还使用来自global sign的文档签名服务。

以下代码用于添加LTV:

public void AddLtv(string src, string dest, IOcspClient ocsp, ICrlClient crl, ITSAClient tsa)
{
    using (PdfReader r = new PdfReader(src))
    {
        using (FileStream fos =new FileStream(dest,FileMode.CreateNew))
        {
            PdfStamper stp = new PdfStamper(r, fos, '\0', true);
            LtvVerification v = stp.LtvVerification;
            AcroFields fields = stp.AcroFields;
            List<String> names = fields.GetSignatureNames();
            String sigName = names[names.Count - 1];
            PdfPKCS7 pkcs7 = fields.VerifySignature(sigName);
            if (pkcs7.IsTsp)
            {
                v.AddVerification(sigName, ocsp, crl,
                        LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
                        LtvVerification.Level.OCSP_CRL,
                        LtvVerification.CertificateInclusion.NO);
            }
            else
            {
                foreach (var name in names)
                {
                    v.AddVerification(name, ocsp, crl,
                            LtvVerification.CertificateOption.WHOLE_CHAIN,
                            LtvVerification.Level.OCSP_CRL,
                            LtvVerification.CertificateInclusion.NO);
                }
            }
            stp.Close();
        }
    }
}

编辑:我认为我在代码中操作pdf的方式导致了阅读/编写pdf的问题。不知何故,我无法获得任何pdf验证程序,该验证程序可以识别@mkl告诉我的交叉pdf引用的问题。但是,如果我如何操作pdf有任何问题,我将分享我的代码,如下所示。我们将不胜感激。

添加新签名时,此pdf的旧签名无效。如果我添加LTV,那么即使是单个签名也无效。

未签名的pdf URL:https://www.sendspace.com/file/n0ckem

无LTV URL的已签名pdf:https://www.sendspace.com/file/t1gwp9

带LTV单签名的签名pdf:https://www.sendspace.com/file/ba8leq

签署pdf与LTV两个标志:https://www.sendspace.com/file/6b53z1

下面是创建空容器和添加签名的代码:

private async Task<string> SignPdf(string ocspResponse, string cert, string unsignedPdf, DocumentShapeModel annotations, string caCertraw, int pageHeight, UserProfileModel user)
{
    var trustedSignedpdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString(), "trustedSignedpdf.pdf");
    if (!Directory.Exists(Path.GetDirectoryName(trustedSignedpdf)))
    {
        Directory.CreateDirectory(trustedSignedpdf);
    }

    var tempPdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString());
    if (!Directory.Exists(tempPdf))
    {
        Directory.CreateDirectory(tempPdf);
    }
    tempPdf = Path.Combine(tempPdf, "tempSignedpdfglobal.pdf");
    string finalsignedPdf = trustedSignedpdf;
    var ocsp = new OcspClientBouncyCastle();
    byte[] oc2 = Convert.FromBase64String(oc1);
    OcspResp ocspResp = new OcspResp(oc2);
    BasicOcspResp basicResp = (BasicOcspResp)ocspResp.GetResponseObject();
    byte[] oc = basicResp.GetEncoded();

    bool check = false;
    string hexencodedDigest = null;
    PdfPKCS7 sgn = null;
    byte[] hash = null;
    Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[2];
    var cer = new Org.BouncyCastle.X509.X509CertificateParser()
        .ReadCertificate((new X509Certificate2(cert)).GetRawCertData());
    chain[0] = cer;
    var caCert = new Org.BouncyCastle.X509.X509CertificateParser()
        .ReadCertificate((new X509Certificate2(caCertraw)).GetRawCertData());
    chain[1] = caCert;
    while (!check)
    {
        PdfReader.unethicalreading = true;
        //create empty signature
        using (PdfReader reader = new PdfReader(unsignedPdf))
        {
            using (FileStream os = File.OpenWrite(tempPdf))
            {
                PdfStamper pdfStamper = PdfStamper.CreateSignature(reader, os, '\0', null, true);
                PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;

                // Sets Signature Appearance

                signatureAppearance.Certificate = chain[0];
                signatureAppearance.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
                signatureAppearance.Reason = "E Signed by " + user.FirstName + " " + user.LastName + " (" + user.Email + ").";

                signatureAppearance.Acro6Layers = false;
                signatureAppearance.Layer4Text = PdfSignatureAppearance.questionMark;

                float shapeH = annotations.IsResponsive == true ? annotations.h : ((annotations.h * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 
                float shapeX = annotations.IsResponsive == true ? annotations.x : ((annotations.x * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 
                float shapeY = annotations.IsResponsive == true ? annotations.y : ((annotations.y * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 
                float shapeW = annotations.IsResponsive == true ? annotations.w : ((annotations.w * 72 / 150) / (float)Convert.ToDouble(annotations.ratio)); 

                double yaxis = (float)Convert.ToDouble(pageHeight) - (shapeH + shapeY);

                // Sets Layer2 text and acro6layers

                signatureAppearance.Layer2Text = " "; //Left blank so that it do not overwrite Esignature.
                signatureAppearance.SetVisibleSignature(new iTextSharp.text.Rectangle((int)(shapeX), (int)yaxis, (int)(shapeX) + (int)shapeW, (int)yaxis + (int)shapeH), annotations.p, annotations.Id.ToString());

                IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);

                MakeSignature.SignExternalContainer(signatureAppearance, external, 8192);

                Stream data = signatureAppearance.GetRangeStream();
                string hashAlgorithm = "SHA256";

                sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
                hash = DigestAlgorithms.Digest(data, hashAlgorithm);
                byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, oc, null, CryptoStandard.CADES);

                //create sha256 message digest
                using (SHA256.Create())
                {
                    sh = SHA256.Create().ComputeHash(sh);
                }

                //create hex encoded sha256 message digest
                hexencodedDigest = new BigInteger(1, sh).ToString(16);
                hexencodedDigest = hexencodedDigest.ToUpper();
                if (hexencodedDigest.Length == 64)
                {
                    check = true;
                }
            }
        }
    }

    var identityGetResult = await IdentityGet(_appConfiguration.TrustedSignSettings.Url + "/identity", AccessToken, IdentityJson, hexencodedDigest);

    //decode hex
    byte[] dsg = FromHex(identityGetResult);

    //include signature on PDF
    sgn.SetExternalDigest(dsg, null, "RSA");

    //create TimeStamp Client
    ITSAClient tsc = new DssClient(AccessToken, _env, _appConfiguration.TrustedSignSettings.Url);

    //byte[] ocspResponse = ocsp.GetEncoded(chain[0],chain[chain.Length -1], CertificateUtil.GetCRLURL(chain[0]));
    //Collection<byte[]> crlBytes = CertificateUtil.fetchCrlBytes(x509certificate, chain);

    byte[] encodedpkcs7 = sgn.GetEncodedPKCS7(hash, tsc, oc, null, CryptoStandard.CADES);
    //adds PKCS7 format Signature on empty signature container
    CreateSignature(tempPdf, finalsignedPdf, annotations.Id.ToString(), encodedpkcs7);

    var finaltrustedSignedpdf = Path.Combine(_env.WebRootPath, "TempPath", annotations.userId, annotations.Id.ToString());
    if (!Directory.Exists(finaltrustedSignedpdf))
    {
        Directory.CreateDirectory(finaltrustedSignedpdf);
    }
    finaltrustedSignedpdf = Path.Combine(finaltrustedSignedpdf, "FinaltrustedSignedpdf.pdf");

    //adds LTV to signed document
    AddLtv(finalsignedPdf, finaltrustedSignedpdf, ocsp, new CrlClientOnline(), tsc);
    return finaltrustedSignedpdf;
}

用于创建签名

public void CreateSignature(string src, string dest, string fieldname, byte[] sig)
{
    using (PdfReader reader = new PdfReader(src))
    {
        using (FileStream os = File.OpenWrite(dest))
        {
            IExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
            MakeSignature.SignDeferred(reader, fieldname, os, external);
        }
    }
}

共有1个答案

吕承福
2023-03-14

原始PDF的交叉引用表中存在错误。众所周知,Adobe签名验证对此类错误很敏感(请参阅此答案和此答案),在某些情况下,它会将此类文件的签名显示为无效。

您应该要求该文档的源提供没有该错误的版本

文档的第一个未签名修订版的交叉引用表如下所示:

xref
0 55
0000000000 65535 f
0000000018 00000 n
0000000164 00000 n
0000000216 00000 n
0000000554 00000 n
0000003363 00000 n
0000003529 00000 n
0000003764 00000 n
0000003815 00000 n
0000003866 00000 n
0000004038 00000 n
0000004279 00000 n
0000004439 00000 n
0000004662 00000 n
0000004792 00000 n
0000004818 00000 n
0000004991 00000 n
0000005061 00000 n
0000005297 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000005466 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000006188 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000006236 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
0000000000 00000 n
54 18
0000006284 00000 n
0000006350 00000 n
0000006648 00000 n
0000171077 00000 n
0000171435 00000 n
0000171726 00000 n
0000171973 00000 n
0000323100 00000 n
0000323123 00000 n
0000324290 00000 n
0000324333 00000 n
0000324715 00000 n
0000326153 00000 n
0000328056 00000 n
0000328093 00000 n
0000328132 00000 n
0000328214 00000 n
0000328377 00000 n

如您所见,它由两个子部分组成,第一个子部分用于从0开始的55个对象,第二个子部分用于从54开始的18个对象。

这是无效的,原因有两个:

>

对于从未进行增量更新的文件,交叉引用部分应仅包含一个子部分,其对象编号从0开始。

(ISO 32000-1和ISO 32000-2,在这两种情况下,第7.5.4节“交叉参考表”)

此外,该交叉引用表具有相同对象的两个条目,第一小节的最后一个条目和第二小节的第一个条目都与对象54相关。这也是禁止的:

一个给定的目标编号不得在一个单独的部分中有一个以上的小节。

(ibidem)

根据相应代码的详细信息,在使用某些PDF处理器(例如Adobe Acrobat Reader)处理PDF时,这可能会或可能不会导致任意问题。

在编辑中,您共享了许多文件。特别是你分享的

>

  • VeriFinger_SDK_Brochure_2017-12-27.pdf

    未签名的pdf URL:https://www.sendspace.com/file/n0ckem

    验证人\u SDK\u手册\u签名\u witoutltv。Pdf格式

    无LTV URL的已签名pdf:https://www.sendspace.com/file/t1gwp9

    FinaltrustedSignedpdf。pdf格式

    带LTV单签名的签名pdf:https://www.sendspace.com/file/ba8leq

    FinaltrustedSignedpdf。pdf(名称相同,但与之前不同)

    签署pdf与LTV两个标志:https://www.sendspace.com/file/6b53z1

    根据您的代码,您可以在追加模式下应用所有更改。因此,后三个文件都必须由第一个文件VeriFinger_SDK_Brochure_2017-12-27.pdf加上一些附加数据组成。但事实并非如此,后三个文件实际上都比第一个短。因此,我必须假设第一个文件首先以某种方式处理,然后签名。

    现在查看“原始文件”的交叉引用表VeriFinger_SDK_Brochure_2017-12-27.pdf(只需在文本查看器中打开它并滚动到它的末尾),我们可以看到它是一个整体,只是一个小节。它包含许多标记为免费但没有间隙的条目。

    但是,通过查看后三个文件的第一次修订版的交叉引用表,我们可以看到每个文件都分为多个小节。显然,标记为“自由”的条目已从表中删除,从而形成了一个包含许多子部分的表。可能这是一种优化尝试,但结果是PDF文件受损。

    因此,无论您在签名之前对文件应用何种PDF处理器,该处理器都会损坏PDF。

    将原始文件的文档信息与其他三个文件中的初始修订进行比较,在签名之前处理该文件的PDF处理器似乎是正确的。的PDF。净19.1,因为生产者价值已更改为净19.1。

    事实上,这似乎是一个已知的Aspose问题,例如,请参阅PDF/A-1转换在Aspose免费支持论坛上创建无效的XRef表线程,该论坛始于2016年8月。

    已将其归档为PDFNET-41272,并在Aspose中标记为固定。的Pdf。NET 17.2.0,但正如同一个月论坛帖子上所报道的那样,它根本没有真正固定下来。

    显然Aspose还没有修复这个错误,并且仍在努力解决它。

  •  类似资料:
    • 我需要将一个使用iText5进行PDF签名验证/创建的Java程序移植到iText7。 旧代码显然不能按原样工作,因为iText的大部分内容都经过了重组。 我找到的所有关于如何做到这一点的例子和教程都是针对iText5的。(非常好的)白皮书也是如此。它们依赖于通过方法返回的列表,在该方法上执行所有与签名相关的操作。 在iText7中,不再具有该方法。 有人知道iText7的例子/文档吗?

    • 我正在编写一个小型库,旨在成为一个高级(简单易用)库,用于对使用WeasyPrint库生成的pdf进行数字签名(https://github.com/Kozea/WeasyPrint). 我已经让它为自签名证书工作,现在我正在从GlobalSignDSS API(https://www.globalsign.com/en/resources/apis/api-documentation/digit

    • 我想知道是否有一个替代包括完整的CRL同时仍然支持LTV?包含完整的CRL似乎有点矫枉过正,而似乎唯一需要的“东西”是包含一个可验证的证明,证明链中的证书在签名时没有被撤销。我认为使用OCSP可能会提供这样的功能,但是简单地删除CRL并包含一个OcspClientBouncyCastle实例并不能起到作用。授予SignDetached的OCSP是否用于检查证书是否在签名时被吊销? 一个相关的问题涉

    • 出身背景 我使用iTextSharp已经有一段时间了。我已经创建了一个带有两个可签名的PdfFormFields的pdf文档。如果我打开pdf文档,我可以手动对每个字段进行手动签名。我希望通过iTextSharp完成这件事。 我目前正在从X509Store检索证书。直到现在,我都能弄明白。 问题 有人能告诉我如何使用X509Certificate2签署一个已经存在的签名字段吗。 工具书类 以下参考

    • 据我所知有两种方法 添加DSS字典 在签名时在签名中嵌入CRL或OCSP响应 DSS方法似乎是可行的,Adobe将签名识别为启用了LTV。第二种方法更适合我们的应用程序,所以我仍在努力让它工作。我在向签名添加OCSP响应时遇到了问题,所以我只尝试添加证书和CRL。如果我错了,请纠正我,但据我所知,CRL或OCSP响应都应该添加到签名中。不需要两者兼而有之吗?我收集签名证书及其根证书,还有TSA证书