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

多重签名

萧丁雨
2023-03-14

我尝试多次使用itext 5.5.13.1模拟不同用户的签名对文档进行签名,PdfStamper位于AppendMode上。如果文档没有签名,则证书级别为CERTIFIED_NO_CHANGES_ALLOWED或CERTIFIED_FORM_FILLING_AND_ANNOTATIONS,否则我不会为PdfSignatureAppearnce设置此参数。第二次签名后,第一次签名无效,因为文档已更改。有什么办法解决这个问题吗?这是我的代码:

        public void Sign(string Thumbprint, string document, string logoPath) {

        X509Certificate2 certificate = FindCertificate(Thumbprint);

        PdfReader reader = new PdfReader(document);

        //Append mode
        PdfStamper st = PdfStamper.CreateSignature(reader, new FileStream(SignedDocumentPath(document), FileMode.Create, FileAccess.Write), '\0', null, true);


        int signatureWidth = 250;
        int signatureHeight = 100;       
        int NewXPos = 0;
        int NewYPos = 0;

        SetStampCoordinates(reader, st, ref NewXPos, ref NewYPos, signatureWidth, signatureHeight);            

        PdfSignatureAppearance sap = st.SignatureAppearance;

        if (reader.AcroFields.GetSignatureNames().Count == 0)
        {
            SetSignatureFieldOptions(certificate, sap, reader, "1", 1, NewXPos, NewYPos, signatureWidth, signatureHeight);
        }
        else {
            SetSignatureFieldOptions(certificate, sap, reader, "2", NewXPos, NewYPos, signatureWidth, signatureHeight);
        }

        Image image = Image.GetInstance(logoPath);
        image.ScaleAbsolute(50, 50);

        Font font1 = SetFont("TIMES.TTF", BaseColor.BLUE, 10, 0);
        Font font2 = SetFont("TIMES.TTF", BaseColor.BLUE, 8, 0);

        PdfTemplate layer = sap.GetLayer(2);
        Chunk chunk1 = new Chunk($"\r\nДОКУМЕНТ ПОДПИСАН\r\nЭЛЕКТРОННОЙ ПОДПИСЬЮ\r\n", font1);
        Chunk chunk2 = new Chunk($"Сертификат {certificate.Thumbprint}\r\n" +
                             $"Владелец {certificate.GetNameInfo(X509NameType.SimpleName, false)}\r\n" +
                             $"Действителен с {Convert.ToDateTime(certificate.GetEffectiveDateString()).Date.ToShortDateString()} " +
                             $"по {Convert.ToDateTime(certificate.GetExpirationDateString()).Date.ToShortDateString()}\r\n", font2);

        PdfTemplate layer0 = sap.GetLayer(0);
        image.SetAbsolutePosition(5, 50);
        layer0.AddImage(image);

        Paragraph para1 = SetParagraphOptions(chunk1, 1, 50, 0, 2, 1.1f);
        Paragraph para2 = SetParagraphOptions(chunk2, 0, 5, 15, 0.5f, 1.1f);

        ColumnText ct = new ColumnText(layer);
        ct.SetSimpleColumn(3f, 3f, layer.BoundingBox.Width - 3f, layer.BoundingBox.Height);
        ct.AddElement(para1);
        ct.AddElement(para2);
        ct.Go();

        layer.SetLineWidth(3);
        layer.SetRGBColorStroke(0, 0, 255);
        layer.Rectangle(0, 0, layer.BoundingBox.Right, layer.BoundingBox.Top);
        layer.Stroke();

        EncryptDocument(certificate, sap);
    }

    public X509Certificate2 FindCertificate(string Thumbprint) {
        X509Store store = new X509Store("My", StoreLocation.CurrentUser);
        store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
        X509Certificate2Collection found = store.Certificates.Find(
            X509FindType.FindByThumbprint, Thumbprint, true);

        X509Certificate2 certificate = found[0];

        if (certificate.PrivateKey is Gost3410_2012_256CryptoServiceProvider cert_key)
        {
            var cspParameters = new CspParameters
            {
                KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName,
                ProviderType = cert_key.CspKeyContainerInfo.ProviderType,
                ProviderName = cert_key.CspKeyContainerInfo.ProviderName,
                Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
               ? (CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore)
               : (CspProviderFlags.UseExistingKey),
                KeyPassword = new SecureString()
            };

            certificate = new X509Certificate2(certificate.RawData)
            {
                PrivateKey = new Gost3410_2012_256CryptoServiceProvider(cspParameters)
            };
        }

        return certificate;
    }

    public Font SetFont(string TTFFontName, BaseColor color, float size, int style) {
        string ttf = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), TTFFontName);
        BaseFont baseFont = BaseFont.CreateFont(ttf, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

        Font font = new Font(baseFont, size, style);
        font.Color = color;

        return font;
    }

    public Paragraph SetParagraphOptions(Chunk chunk, int ParagraphAligment, float marginLeft, float marginTop, float fixedLeading, float multipledLeading) {
        Paragraph paragraph = new Paragraph();
        paragraph.Alignment = ParagraphAligment;
        paragraph.IndentationLeft = marginLeft;
        paragraph.SpacingBefore = marginTop;
        paragraph.SetLeading(fixedLeading, multipledLeading);
        paragraph.Add(chunk);

        return paragraph;
    }

    public string SignedDocumentPath(string document) {
        string filename = String.Concat(Path.GetFileNameWithoutExtension(document), "_signed.pdf");
        string path = Path.Combine(Path.GetDirectoryName(document), filename);

        return path;
    }

    public void SetSignatureFieldOptions(X509Certificate2 certificate, PdfSignatureAppearance sap, PdfReader reader, string field, int level, int XPos, int YPos, int width, int height)
    {
        X509CertificateParser parser = new X509CertificateParser();

        try
        {
            Rectangle rectangle = new Rectangle(XPos, YPos, XPos + width, YPos + height);
            sap.SetVisibleSignature(rectangle, reader.NumberOfPages, field);
            sap.Certificate = parser.ReadCertificate(certificate.RawData);
            sap.Reason = "I agree";
            sap.Location = "Location";
            sap.Acro6Layers = true;
            sap.SignDate = DateTime.Now;
            sap.CertificationLevel = level;
        }

        catch (Exception ex)
        {
            if (ex.Message == $"The field {certificate.Thumbprint} already exists.")
                throw new Exception("Вы уже подписали данный документ");
        }
    }

    public void SetSignatureFieldOptions(X509Certificate2 certificate, PdfSignatureAppearance sap, PdfReader reader,string field, int XPos, int YPos, int width, int height) {
        X509CertificateParser parser = new X509CertificateParser();

        try
        {
            Rectangle rectangle = new Rectangle(XPos, YPos, XPos + width, YPos + height);
            sap.SetVisibleSignature(rectangle, reader.NumberOfPages, field);
            sap.Certificate = parser.ReadCertificate(certificate.RawData);
            sap.Reason = "I agree";
            sap.Location = "Location";
            sap.Acro6Layers = true;
            sap.SignDate = DateTime.Now;
        }

        catch (Exception ex) {
            if (ex.Message == $"The field {certificate.Thumbprint} already exists.")
                throw new Exception("Вы уже подписали данный документ");
        }
    }

    public void EncryptDocument(X509Certificate2 certificate, PdfSignatureAppearance sap) {

        PdfName filterName;
        if (certificate.PrivateKey is Gost3410CryptoServiceProvider)
            filterName = new PdfName("CryptoPro#20PDF");
        else
            filterName = PdfName.ADOBE_PPKLITE;

        PdfSignature dic = new PdfSignature(filterName, PdfName.ADBE_PKCS7_DETACHED);
        dic.Date = new PdfDate(sap.SignDate);
        dic.Name = certificate.GetNameInfo(X509NameType.SimpleName, false);
        if (sap.Reason != null)
            dic.Reason = sap.Reason;
        if (sap.Location != null)
            dic.Location = sap.Location;
        sap.CryptoDictionary = dic;

        int intCSize = 4000;
        Dictionary<PdfName, int> hashtable = new Dictionary<PdfName, int>();
        hashtable[PdfName.CONTENTS] = intCSize * 2 + 2;
        sap.PreClose(hashtable);
        Stream s = sap.GetRangeStream();
        MemoryStream ss = new MemoryStream();
        int read = 0;
        byte[] buff = new byte[8192];
        while ((read = s.Read(buff, 0, 8192)) > 0)
        {
            ss.Write(buff, 0, read);
        }

        ContentInfo contentInfo = new ContentInfo(ss.ToArray());
        SignedCms signedCms = new SignedCms(contentInfo, true);
        CmsSigner cmsSigner = new CmsSigner(certificate);
        signedCms.ComputeSignature(cmsSigner, false);
        byte[] pk = signedCms.Encode();

        byte[] outc = new byte[intCSize];
        PdfDictionary dic2 = new PdfDictionary();
        Array.Copy(pk, 0, outc, 0, pk.Length);
        dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
        sap.Close(dic2);
    }

共有1个答案

白云
2023-03-14

屏幕截图中最重要的部分

是签名面板上签名之间的文本“1页已修改”。这告诉我们,除了添加和填充签名字段之外,您还可以进行其他更改。通过检查文件本身,可以快速识别更改:

>

  • 在原来的sample.pdf

    只有一个内容流。

    使用一个签名sample_signed.pdf

    有三个内容流,原始内容流被新内容流包围。

    在带有两个签名的sample_signed_signed.pdf中

    有五个内容流,前三个被两个新内容流所包围。

    因此,在每次签名过程中,您都要更改页面内容。正如您在本答案中所读到的,始终不允许更改已签名文档的页面内容。添加的流的内容很琐碎,这甚至于事无补,前面添加的每个流都包含:

    q
    

    每个添加到末尾的流都包含

    Q
    q
    Q
    

    即,只发生一些保存和恢复图形状态的情况。

    上述更改是PdfStamper方法完成的典型准备步骤,将原始内容包装在q…q中(保存

    我在您发布的代码中找不到这样的调用,但在您的代码中缺少 SetStamp 坐标方法。对于该方法中的 PdfStamper 参数,您是否可能将“获取内容”称为“获取内容”?

  •  类似资料:
    • 由比特币的签名机制可知,如果丢失了私钥,没有任何办法可以花费对应地址的资金。 这样就使得因为丢失私钥导致资金丢失的风险会很高。为了避免一个私钥的丢失导致地址的资金丢失,比特币引入了多重签名机制,可以实现分散风险的功能。 具体来说,就是假设N个人分别持有N个私钥,只要其中M个人同意签名就可以动用某个“联合地址”的资金。 多重签名地址实际上是一个Script Hash,以2-3类型的多重签名为例,它的

    • 前言 随着区块链等相关技术的创新和突破,很多有形或无形资产都将实现去中心化,数字资产将无处不在。比如我们这里分享的 亿书 就是要把数字出版物版权进行保护,实现去中心化,解决业界多年来版权保护不力的难题。 无论数字资产,还是数字出版版权,都是有明确所有权的,当前实现数字资产所属的技术手段就是本篇要介绍的签名。而多重签名是对签名的扩展使用,给数字资产转移提供了安全保障和技术手段。本篇,从基本概念入手,

    • PDF创建步骤: 通过添加空签名字段名称创建pdf:suhasb@gmail.com和nikhil.courser@gmail.com,使用原始的hello.pdf输出文件名hello_tag.pdf运行程序>tagpdfsignaturefields.java 从hello_tag.pdf文件中提取签名字段suhasb@gmail.com进行首次签名,输出文件名为hello_signd.pdf

    • 问题内容: 我有三个类:,和。 继承自和(按此顺序)。的构造特征和不同。如何调用两个父类的方法? 我在代码中的努力: 产生错误: 我发现此资源用不同的参数集解释了多重继承,但他们建议对所有参数使用和。我觉得这很丑陋,因为我无法从子类的构造函数调用中看到将哪些参数传递给父类。 问题答案: 千万 不能* 使用,除非你知道自己在做什么。第一个参数告诉它寻找下一个要使用的方法时要 跳过的 类。例如,将查看

    • 我试图学习WP小部件创建。在教程网站上,这是发布- 标题 我对这部分感到困惑-for="消息" 这意味着什么“message”只是一个类似的类或ID,将在CSS中设置样式?还是我没有得到正确的信息?

    • 我正在使用Tika来自动检测被推入DMS的文档的内容类型。几乎所有的工作都很好,除了电子邮件。 我引用了核心库和tika解析器来检测pdf和msword文档。我是不是还漏掉了什么?