如何使用itextsharp.net将相同的数字签名放置在PDF中的多个位置 [英] How to place the Same Digital signatures to Multiple places in PDF using itextsharp.net

查看:165
本文介绍了如何使用itextsharp.net将相同的数字签名放置在PDF中的多个位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用iTextSharp Dll实现了数字签名,用单个签名对PDF文件进行签名,创建空签名字段,并使用签名哈希工作正常更新签名字段。现在,我想在pdf的每一页中放置相同的数字签名。这是我的客户要求。

I have implemented Digital Signature using iTextSharp Dll to sign PDF files with a single signature creating empty signature fields and update the signature field with signed hash working fine. Now, I want to place the same digital signature in every page of pdf. It's my client requirement.

我使用以下代码:

public class MyExternalSignatureContainer : IExternalSignatureContainer
{
    private readonly byte[] signedBytes;

    public MyExternalSignatureContainer(byte[] signedBytes)
    {
        this.signedBytes = signedBytes;
    }

    public byte[] Sign(Stream data)
    {
        return signedBytes;
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
    }
}

以下代码中使用的代码

PdfReader reader = new PdfReader(unsignedPdf);
FileStream os = File.OpenWrite(tempPdf);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;

appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(36, 748, 144, 780), 1, null);
for (int i = 1; i < 8; i++)
{
    var signatureField = PdfFormField.CreateSignature(stamper.Writer);
    var signatureRect = new Rectangle(200, 200, 100, 100);
    signatureField.Put(PdfName.T, new PdfString("ClientSignature_"+i.ToString()));
    PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
    signatureField.Put(PdfName.V, PRef);
    signatureField.Put(PdfName.F, new PdfNumber("132"));
    signatureField.SetWidget(signatureRect, null);
    signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);

    PdfDictionary xobject1 = new PdfDictionary();
    PdfDictionary xobject2 = new PdfDictionary();
    xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
    xobject2.Put(PdfName.AP, xobject1);
    signatureField.Put(PdfName.AP, xobject1);
    signatureField.SetPage();
    PdfDictionary xobject3 = new PdfDictionary();
    PdfDictionary xobject4 = new PdfDictionary();
    xobject4.Put(PdfName.FRM, appearance.GetAppearance().IndirectReference);
    xobject3.Put(PdfName.XOBJECT, xobject4);
    signatureField.Put(PdfName.DR, xobject3);

    stamper.AddAnnotation(signatureField, i);
}

IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
stamper.Close();

byte[] SignedHash =  DoEsign(SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
os.close();
reader.close();

reader = new PdfReader(tempPdf))
os = File.OpenWrite(signedPdf)

IExternalSignatureContainer external1 = new MyExternalSignatureContainer(SignedHash);
MakeSignature.SignDeferred(reader, signatureFieldName, os, external1);
os.close();
reader.close();

请建议我完成任务

推荐答案

要为所有签名字段提供包装新创建的签名容器的相同单个值,它们必须全部引用与值相同的间接对象。不幸的是,iText只为创建了签名值的间接对象,应用程序代码有机会添加其附加字段,而这些字段又需要对该签名值对象的引用。因此,应用程序代码必须预测间接对象将具有的对象编号。

To give all signature fields the same single value wrapping the newly created signature container, they must all reference the same indirect object as value. Unfortunately iText creates the indirect object for the signature value only after the application code had the chance to add its additional fields which in turn require a reference to that signature value object. Thus, the application code has to anticipate the object number that indirect object will have.

这个对象编号的预期或预测是非常微妙的,它取决于完全相同用例,也可能由于iTextSharp库中的微小更改而变得不正确

This anticipation or prediction of the object number is very delicate, it depends on the exact same use case and can also become incorrect as the result of minor changes in the iTextSharp library

为了使这更容易,应用程序代码应添加带有签名值引用的签名字段尽可能晚,所以在iText创建值间接对象之前,尽可能少创建其他新的间接对象。

To make this easier, the application code should add those signature fields with their signature value references as late as possible, so there are as few other new indirect objects created as possible until iText creates the value indirect object.

事实证明, ModifySigningDictionary IExternalSignatureContainer 的方法是一个很好的位置。

As it turns out, the ModifySigningDictionary method of an IExternalSignatureContainer is a good position for that.

As当一个人在那里添加一个代码时,会弹出另一个问题:无法在外部设置 PdfIndirectReference 实例中的预期对象编号。解决此问题的一种方法是使用 PdfLiteral 模仿此类引用。 (好吧,也许人们也可以使用反射。)

As soon as one adds one's code there, another issue pops up: There is no means to set the anticipated object number in a PdfIndirectReference instance externally. One way to get around this is to mimic such a reference using a PdfLiteral. (Well, probably one could also use reflection for this.)

此外,事实证明,在构建之前,最好创建一个外观流以供所有人使用其他签名字段。 PdfLiteral 模仿 PdfIndirectReference ,因为这简化了iText将用于实际值对象的对象编号的计算。

Furthermore it turns out that one best creates the appearance streams to use by all one's additional signature fields before building that PdfLiteral mimicking a PdfIndirectReference as this simplifies the calculation of the object number iText will use for the actual value object.

考虑到这一点,这里是一个概念验证。这个概念证明使用 IExternalSignature 实例进行实际签名。这不是必要的前提条件,也可以使用 IExternalSignatureContainer 而只需进行一些更改,即使是 ExternalBlankSignatureContainer 也可以在问题中稍后使用 MakeSignature.SignDeferred 完成签名。

With this in mind, here a proof-of concept. This proof of concept makes use of an IExternalSignature instance for actually signing. This is not a necessary precondition, one can also use an IExternalSignatureContainer instead with only a few changes, even an ExternalBlankSignatureContainer as in the question to later finalize the signature using MakeSignature.SignDeferred.

所以给定密码参数 cp (私钥材料,例如 pk.Key ,用于 Org.BouncyCastle.Pkcs.AsymmetricKeyEntry pk )和证书链 chain ,人们会使用

So given cipher parameters cp (private key material, e.g. pk.Key for an Org.BouncyCastle.Pkcs.AsymmetricKeyEntry pk) and a certificate chain chain, one would use

PdfReader reader = new PdfReader(SRC);
FileStream os = new FileStream(DEST, FileMode.Create, FileAccess.Write);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;

appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(10, 10, 100, 100), reader.NumberOfPages, null);

IExternalSignature externalSignature = new PrivateKeySignature(cp, "SHA-256");
AllPagesSignatureContainer allPagesContainer = new AllPagesSignatureContainer(appearance, externalSignature, chain);
MakeSignature.SignExternalContainer(appearance, allPagesContainer, 8192);

此外部签名容器类

public class AllPagesSignatureContainer : IExternalSignatureContainer
{
    public AllPagesSignatureContainer(PdfSignatureAppearance appearance, IExternalSignature externalSignature, ICollection<X509Certificate> chain)
    {
        this.appearance = appearance;
        this.chain = chain;
        this.externalSignature = externalSignature;
    }

    public void ModifySigningDictionary(PdfDictionary signDic)
    {
        signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKMS);
        signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);

        PdfStamper stamper = appearance.Stamper;
        PdfReader reader = stamper.Reader;
        PdfDictionary xobject1 = new PdfDictionary();
        PdfDictionary xobject2 = new PdfDictionary();
        xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
        xobject2.Put(PdfName.AP, xobject1);

        PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
        PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");

        for (int i = 1; i < reader.NumberOfPages; i++)
        {
            var signatureField = PdfFormField.CreateSignature(stamper.Writer);

            signatureField.Put(PdfName.T, new PdfString("ClientSignature_" + i.ToString()));
            signatureField.Put(PdfName.V, PRefLiteral);
            signatureField.Put(PdfName.F, new PdfNumber("132"));
            signatureField.SetWidget(appearance.Rect, null);
            signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);

            signatureField.Put(PdfName.AP, xobject1);
            signatureField.SetPage();
            Console.WriteLine(signatureField);

            stamper.AddAnnotation(signatureField, i);
        }
    }

    public byte[] Sign(Stream data)
    {
        String hashAlgorithm = externalSignature.GetHashAlgorithm();
        PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
        IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
        byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
        byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
        byte[] extSignature = externalSignature.Sign(sh);
        sgn.SetExternalDigest(extSignature, null, externalSignature.GetEncryptionAlgorithm());
        return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
    }

    PdfSignatureAppearance appearance;
    ICollection<X509Certificate> chain;
    IExternalSignature externalSignature;
}

行中签名值的预测间接对象编号

The predicted indirect object number of the signature value in the line

PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");

严格依赖于用例每页只有一个签名字段。对于不同的用例,预测的估计会有所不同。

strictly depends upon the use case being "exactly one signature field per page". For different use cases the estimate the prediction would differ.

我再次强调这一点,因为例如此问题的OP在尝试在单个页面上放置多个签名时没有考虑到这一点

I stress this here once again because e.g. the OP of this question did not take this into account when trying "to place multiple signatures on single page".

注意:虽然此程序创建的内容不违反字母 PDF规范(仅禁止从多个页面引用相同字段对象的情况,无论是通过相同的还是通过不同的小部件),它显然违反了 intent ,其精神。因此,作为规范的勘误表文件的一部分,此程序也可能被禁止。

Beware: While this procedure creates something which does not violate the letter of the PDF specifications (which only forbid the cases where the same field object is referenced from multiple pages, be it via the same or via distinct widgets), it clearly does violate its intent, its spirit. Thus, this procedure might also become forbidden as part of a Corrigenda document for the specification.

这篇关于如何使用itextsharp.net将相同的数字签名放置在PDF中的多个位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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