签名后不允许更改的PDF设置

时间:2016-04-20 10:00:05

标签: pdf itextsharp itext sign

所以我遇到了一个有趣的问题。所以我知道你在签名时在itext / itextsharp中有一个PdfSignatureAppearance上的设置,你可以设置CertificationLevel,并且按预期工作,但最近我遇到了我正在签署文件的问题,其中第一个签名是文件用PdfSignatureAppearance.CERTIFIED_FORM_FILLING签名,因此允许我使用(PdfSignatureAppearance.NOT_CERTIFIED)作为批准签名向后添加/签名签名。

所以我问这个问题的原因是我遇到了一个问题,我在文档中有一个包含现有签名字段的文档,我使用PdfSignatureAppearance.CERTIFIED_FORM_FILLING签署了第一个签名,然后用PdfSignatureAppearance.NOT_CERTIFIED签署了签名,但是当我签署第二个签名字段时,第一个签名字段变为无效。

我使用的这个文档是使用FoxitPro创建的,但如果我在Adobe DC Pro中做同样的事情,它会按预期工作。

任何建议都将受到赞赏。

以下是我用于签署pdf文档的代码,它只是签名者类,因此其他对象和对此类的引用将会丢失。

        public byte[] Sign(SignRequest request)
    {
        request.Document = SaveDocumentChanges(new SaveRequest
        {
            Document = request.Document,
            FormFields = request.FormFields,
            SigningBlocks = request.SigningBlocks
        }, request.Information);

        return SignDocument(request.Certificate, request.Information, request.SigningBlocks, request.SignatureImages, request.Document, request.IsFinalSignature);
    }

    private byte[] AddFormFields(List<FormField> formFields, byte[] document)
    {
        for (int i = 0; i < formFields.Count; i++)
        {
            using (MemoryStream outputStream = new MemoryStream())
            {
                using (PdfReader reader = new PdfReader(document))
                {
                    using (PdfStamper stamper = CreatePdfStamper(reader, outputStream, false))
                    {
                        if (!DoesFormFieldExist(reader, formFields[i].Name))
                        {
                            CreateFormField(reader, stamper, formFields[i]);
                        }
                        else
                        {
                            UpdateFormField(stamper, formFields[i]);
                        }
                    }
                }

                document = outputStream.ToArray();
            }
        }

        return document;
    }

    private byte[] AddMetaData(SigningInformation information, byte[] document)
    {
        if (information.CustomProperties != null && information.CustomProperties.Any())
        {
            using (MemoryStream outputStream = new MemoryStream())
            {
                using (PdfReader reader = new PdfReader(document))
                {
                    using (PdfStamper stamper = CreatePdfStamper(reader, outputStream, false))
                    {
                        Dictionary<string, string> currentProperties = reader.Info;
                        foreach (string key in information.CustomProperties.Keys)
                        {
                            AddMetaDataAddDictionaryValue(currentProperties, key, information.CustomProperties[key]);
                        }

                        AddMetaDataAddDictionaryValue(currentProperties, "Producer", "Signisure");
                        AddMetaDataAddDictionaryValue(currentProperties, "Creator", "Signisure");
                        AddMetaDataAddDictionaryValue(currentProperties, "Author", "Signisure");
                        stamper.MoreInfo = currentProperties;
                    }
                }

                return outputStream.ToArray();
            }
        }

        return document;
    }

    private void AddMetaDataAddDictionaryValue(Dictionary<string, string> dictionary, string key, string value)
    {
        if (dictionary.ContainsKey(key))
        {
            dictionary[key] = value;
        }
        else
        {
            dictionary.Add(key, value);
        }
    }

    private void AddMetaDataAddDictionaryValue(PdfDictionary dictionary, PdfName key, PdfObject value)
    {
        if (!dictionary.Keys.Contains(key))
        {
            dictionary.Put(key, value);
        }
    }

    private byte[] AddSignatureFields(List<SigningBlock> signingBlocks, byte[] document)
    {
        for (int i = 0; i < signingBlocks.Count; i++)
        {
            using (MemoryStream outputStream = new MemoryStream())
            {
                using (PdfReader reader = new PdfReader(document))
                {
                    using (PdfStamper stamper = CreatePdfStamper(reader, outputStream, false))
                    {
                        if (!DoesSignatureFieldExist(reader, signingBlocks[i].Name))
                        {
                            CreateSignatureField(stamper, signingBlocks[i]);
                        }
                    }
                }

                document = outputStream.ToArray();
            }
        }

        return document;
    }

    private void CreateFormField(PdfReader reader, PdfStamper stamper, FormField formField)
    {
        TextField field = new TextField(stamper.Writer, new Rectangle(formField.X, formField.Y, formField.X + formField.Width, formField.Y + formField.Height), formField.Name);
        field.Text = formField.Value;
        field.Font = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, BaseFont.EMBEDDED);
        field.FontSize = 12;
        field.Options = TextField.READ_ONLY;

        stamper.AddAnnotation(field.GetTextField(), formField.Page);
    }

    private PdfSignatureAppearance CreatePdfAppearance(PdfStamper stamper, SigningInformation information, string location, bool certify)
    {
        PdfSignatureAppearance appearance = stamper.SignatureAppearance;
        appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
        CreatePdfAppearanceCertifyDocument(appearance, certify);

        if (information != null)
        {
            appearance.Location = location != String.Empty ? String.Format("{0} ({1})", location, information.IPAddress) : information.IPAddress;
            appearance.Reason = information.SignatoryReason;
            appearance.Contact = String.Format("{0} ({1})", information.Signatory, information.SignatoryEmail);
        }

        return appearance;
    }

    private void CreatePdfAppearanceCertifyDocument(PdfSignatureAppearance appearance, bool certify)
    {
        if (certify)
        {
            appearance.CertificationLevel = PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS;
        }
        else
        {
            appearance.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
        }
    }

    private PdfStamper CreatePdfStamper(PdfReader reader, MemoryStream outputStream, bool isSignature)
    {
        if (CreatePdfStamperIsPDFADocument(reader))
        {
            if (isSignature)
            {
                return PdfAStamper.CreateSignature(reader, outputStream, _pdfVersion, null, true, PdfAConformanceLevel.PDF_A_1A);
            }
            else
            {
                return new PdfAStamper(reader, outputStream, _pdfVersion, true, PdfAConformanceLevel.PDF_A_1A);
            }
        }
        else
        {
            if (isSignature)
            {
                return PdfStamper.CreateSignature(reader, outputStream, _pdfVersion, null, true);
            }
            else
            {
                return new PdfStamper(reader, outputStream, _pdfVersion, true);
            }
        }
    }

    private bool CreatePdfStamperIsPDFADocument(PdfReader reader)
    {
        if (reader.Metadata != null && reader.Metadata.Length > 0)
        {
            IXmpMeta xmpMeta = XmpMetaParser.Parse(reader.Metadata, null);
            IXmpProperty pdfaidConformance = xmpMeta.GetProperty(XmpConst.NS_PDFA_ID, "pdfaid:conformance");
            IXmpProperty pdfaidPart = xmpMeta.GetProperty(XmpConst.NS_PDFA_ID, "pdfaid:part");

            if (pdfaidConformance == null || pdfaidPart == null)
            {
                return false;
            }

            return true;
        }

        return false;
    }

    private void CreateSignatureField(PdfStamper stamper, SigningBlock signingBlock)
    {
        if (signingBlock == null)
        {
            return;
        }

        PdfFormField signatureField = PdfFormField.CreateSignature(stamper.Writer);
        signatureField.SetWidget(new Rectangle(signingBlock.X, signingBlock.Y, signingBlock.X + signingBlock.Width, signingBlock.Y + signingBlock.Height), null);
        signatureField.Flags = PdfAnnotation.FLAGS_PRINT;
        signatureField.FieldName = signingBlock.Name;
        signatureField.Page = signingBlock.Page;

        signatureField.Put(PdfName.CONTENTS, new PdfString(String.Empty));
        CreateSignatureFieldAddLockProperties(signatureField, signingBlock, stamper);

        stamper.AddAnnotation(signatureField, signingBlock.Page);
    }

    private void CreateSignatureFieldAddLockProperties(PdfFormField signatureField, SigningBlock signingBlock, PdfStamper stamper)
    {
        if (signingBlock.LinkedFormFields != null && signingBlock.LinkedFormFields.Count > 0)
        {
            PdfSigLockDictionary lockDictionary = new PdfSigLockDictionary(PdfSigLockDictionary.LockAction.INCLUDE, signingBlock.LinkedFormFields.ToArray());
            signatureField.Put(PdfName.LOCK, stamper.Writer.AddToBody(lockDictionary).IndirectReference);
        }
    }

    private bool DoesFormFieldExist(PdfReader reader, string formFieldName)
    {
        if (String.IsNullOrWhiteSpace(formFieldName))
        {
            return false;
        }

        return reader.AcroFields.Fields.Where(vp => vp.Key == formFieldName).Any();
    }

    private bool DoesSignatureFieldExist(PdfReader reader, string signatureFieldName)
    {
        if (String.IsNullOrWhiteSpace(signatureFieldName))
        {
            return false;
        }

        return reader.AcroFields.DoesSignatureFieldExist(signatureFieldName);
    }

    private AcroFields.FieldPosition GetAcroFieldByName(PdfStamper stamper, string signatureBlockName)
    {
        return stamper.AcroFields.GetFieldPositions(signatureBlockName).FirstOrDefault();
    }

    private List<string> GetAllSignatureFieldNames(PdfReader reader)
    {
        List<string> signatureFields = new List<string>();
        signatureFields.AddRange(reader.AcroFields.GetBlankSignatureNames());
        signatureFields.AddRange(reader.AcroFields.GetSignatureNames());

        return signatureFields;
    }

    private void GetDocumentFormFieldsBuildFormFields(List<FormField> formFields, PdfReader reader, PdfStamper stamper)
    {
        List<string> signatureFields = GetAllSignatureFieldNames(reader);
        foreach (KeyValuePair<string, AcroFields.Item> field in reader.AcroFields.Fields)
        {
            string fieldName = field.Key.ToString();
            if (!signatureFields.Where(signatureFieldName => signatureFieldName == fieldName).Any())
            {
                string fieldValue = reader.AcroFields.GetField(field.Key.ToString());
                AcroFields.FieldPosition formFieldPosition = GetAcroFieldByName(stamper, fieldName);

                formFields.Add(GetDocumentFormFieldsBuildFormFieldsCreateField(formFieldPosition, fieldName, fieldValue));
            }
        }
    }

    private FormField GetDocumentFormFieldsBuildFormFieldsCreateField(AcroFields.FieldPosition formFieldPosition, string fieldName, string fieldValue)
    {
        return new FormField
        {
            Height = (int)formFieldPosition.position.Height,
            Name = fieldName,
            Page = formFieldPosition.page,
            Width = (int)formFieldPosition.position.Width,
            X = (int)formFieldPosition.position.Left,
            Y = (int)formFieldPosition.position.Top,
            Value = fieldValue
        };
    }

    private void GetDocumentSignatureBlocksBuildSignatureBlocks(List<SigningBlock> signatureBlocks, List<string> signatureBlockNames, PdfReader reader, PdfStamper stamper, bool isSigned)
    {
        foreach (string signatureBlockName in signatureBlockNames)
        {
            AcroFields.FieldPosition signatureFieldPosition = GetAcroFieldByName(stamper, signatureBlockName);
            signatureBlocks.Add(GetDocumentSignatureBlocksBuildSignatureBlocksCreateBlock(signatureFieldPosition, signatureBlockName, isSigned));
        }
    }

    private SigningBlock GetDocumentSignatureBlocksBuildSignatureBlocksCreateBlock(AcroFields.FieldPosition signatureFieldPosition, string fieldName, bool isSigned)
    {
        return new SigningBlock
        {
            Height = (int)signatureFieldPosition.position.Height,
            Name = fieldName,
            Page = signatureFieldPosition.page,
            Width = (int)signatureFieldPosition.position.Width,
            X = (int)signatureFieldPosition.position.Left,
            Y = (int)signatureFieldPosition.position.Top,
            IsSigned = isSigned
        };
    }

    private string GetFormFieldValueForName(PdfStamper stamper, string formFieldName)
    {
        AcroFields formFields = stamper.AcroFields;
        return formFields.GetField(formFieldName);
    }

    private byte[] GetSignatureImage(List<MemberItemSignature> signatureImages, string signingBlockName)
    {
        MemberItemSignature signature = signingBlockName.Contains("Initial") ? signatureImages.Where(image => image.Use == SignatureUses.Initial).FirstOrDefault() : signatureImages.Where(image => image.Use == SignatureUses.Signature).FirstOrDefault();
        if (signature != null)
        {
            return signature.Image;
        }

        return null;
    }

    private byte[] SaveDocumentChanges(SaveRequest request, SigningInformation information)
    {
        request.Document = AddMetaData(information, request.Document);
        request.Document = AddFormFields(request.FormFields, request.Document);
        request.Document = AddSignatureFields(request.SigningBlocks, request.Document);

        return request.Document;
    }

    private byte[] SignDocument(Certificate certificate, SigningInformation information, List<SigningBlock> signingBlocks, List<MemberItemSignature> signatureImages, byte[] document, bool isFinalSignature)
    {
        for (int i = 0; i < signingBlocks.Count; i++)
        {
            document = SignDocumentSignSignatureField(certificate, information, signingBlocks[i], signatureImages, document, true);
        }

        if (isFinalSignature)
        {
            return SignDocumentLTVVerification(certificate, document);
        }

        return document;
    }

    private byte[] SignDocumentLTVVerification(Certificate certificate, byte[] document)
    {
        using (MemoryStream outputStream = new MemoryStream())
        {
            using (PdfReader reader = new PdfReader(document))
            {
                using (PdfStamper stamper = PdfStamper.CreateSignature(reader, outputStream, '\0', null, true))
                {
                    SignDocumentSigningBlockAddLTVVerification(stamper, certificate);
                }
            }

            return outputStream.ToArray();
        }
    }

    private void SignDocumentSigningBlock(SigningComponents components, SigningInformation information, SigningBlock block, PdfSignatureAppearance appearance, PdfStamper stamper, byte[] signatureImage)
    {
        appearance.SetVisibleSignature(block.Name);
        SignDocumentSigningBlockWithImage(signatureImage, appearance);
        SignDocumentSigningBlockWithText(appearance, information, appearance.SignDate);

        if (components.Certificate != null)
        {
            using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)components.Certificate.PrivateKey)
            {
                PrivateKeySignature privateKeySignature = SignDocumentSigningBlockBuildDigestSigningMethod(information, rsa) as PrivateKeySignature;
                SignatureHelper.Sign(appearance, privateKeySignature, components.CertificateChain, new List<ICrlClient> { components.CrlClient }, components.OcspClient, components.TimeStampingAuthority, Int32.Parse(_settingManager["DocumentSigningEstimatedDigestSize"]), CryptoStandard.CMS, SignDocumentSigningBlockCreateMetaData(information));
            }
        }
        else
        {
            HSMExternalSignature hsmExternalSignature = SignDocumentSigningBlockBuildDigestSigningMethod(information, null) as HSMExternalSignature;
            SignatureHelper.Sign(appearance, hsmExternalSignature, components.TimeStampingAuthority, Int32.Parse(_settingManager["DocumentSigningEstimatedDigestSize"]), CryptoStandard.CMS, SignDocumentSigningBlockCreateMetaData(information));
        }
    }

    private void SignDocumentSigningBlockAddLTVVerification(PdfStamper stamper, Certificate certificate)
    {
        SigningComponents components = new SigningComponents(_settingManager, certificate);
        LtvVerification ltvVerification = stamper.LtvVerification;
        List<string> signatureFieldNames = stamper.AcroFields.GetSignatureNames();

        PdfPKCS7 pkcs7 = stamper.AcroFields.VerifySignature(signatureFieldNames.Last());
        if (pkcs7.IsTsp)
        {
            bool validationAddedSuccessfully = ltvVerification.AddVerification(signatureFieldNames.Last(), components.OcspClient, components.CrlClient, LtvVerification.CertificateOption.SIGNING_CERTIFICATE, LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.YES);
        }
        else
        {
            foreach (string name in stamper.AcroFields.GetSignatureNames())
            {
                bool validationAddedSuccessfully = ltvVerification.AddVerification(name, components.OcspClient, components.CrlClient, LtvVerification.CertificateOption.WHOLE_CHAIN, LtvVerification.Level.OCSP_CRL, LtvVerification.CertificateInclusion.YES);
            }
        }
        ltvVerification.Merge();

        PdfSignatureAppearance appearance = stamper.SignatureAppearance;
        LtvTimestamp.Timestamp(appearance, components.TimeStampingAuthority, null);
    }

    private IExternalSignature SignDocumentSigningBlockBuildDigestSigningMethod(SigningInformation information, RSACryptoServiceProvider rsaCryptoProvider)
    {
        if (information.CertificateUse == CertificateUse.SignisureCertificate || rsaCryptoProvider == null)
        {
            return new HSMExternalSignature(_hsmService, _settingManager["DocumentSigningEncryptionHashAlgorithm"]);
        }
        else
        {
            return new PrivateKeySignature(DotNetUtilities.GetRsaKeyPair(rsaCryptoProvider).Private, _settingManager["DocumentSigningEncryptionHashAlgorithm"]);
        }
    }

    private PdfDictionary SignDocumentSigningBlockCreateMetaData(SigningInformation information)
    {
        PdfDictionary signatureDictionary = new PdfDictionary();
        if (!String.IsNullOrWhiteSpace(information.IdentificationInformation))
        {
            AddMetaDataAddDictionaryValue(signatureDictionary, new PdfName(".Signisure.IdentificationInformation"), new PdfString(information.IdentificationInformation));
        }
        if (!String.IsNullOrWhiteSpace(information.JuristicEntity))
        {
            AddMetaDataAddDictionaryValue(signatureDictionary, new PdfName(".Signisure.JuristicEntity"), new PdfString(information.JuristicEntity));
        }
        if (!String.IsNullOrWhiteSpace(information.Capacity))
        {
            AddMetaDataAddDictionaryValue(signatureDictionary, new PdfName(".Signisure.Capacity"), new PdfString(information.Capacity));
        }

        return signatureDictionary;
    }

    private void SignDocumentSigningBlockWithImage(byte[] signatureImage, PdfSignatureAppearance appearance)
    {
        if (signatureImage != null && signatureImage.Length > 0)
        {
            Image signatureImageInstance = Image.GetInstance(ImageHelper.FlattenImage(signatureImage));

            appearance.Image = signatureImageInstance;
            appearance.SignatureGraphic = signatureImageInstance;
        }
    }

    private void SignDocumentSigningBlockWithText(PdfSignatureAppearance appearance, SigningInformation information, DateTime timestampDateTime)
    {
        BaseFont verdana = BaseFont.CreateFont(AssemblyDirectory + "\\Content\\Fonts\\Verdana\\Verdana.ttf", BaseFont.CP1252, BaseFont.EMBEDDED);
        BaseFont helvetica = BaseFont.CreateFont(AssemblyDirectory + "\\Content\\Fonts\\Helvetica\\Helvetica.ttf", BaseFont.CP1252, BaseFont.EMBEDDED);
        BaseFont comicSans = BaseFont.CreateFont(AssemblyDirectory + "\\Content\\Fonts\\ComicSans\\ComicSans.ttf", BaseFont.CP1252, BaseFont.EMBEDDED);

        appearance.Layer2Text = SignDocumentSigningBlockWithTextBuildText(appearance, information, timestampDateTime);
        appearance.Layer2Font = new Font(verdana);
    }

    private string SignDocumentSigningBlockWithTextBuildText(PdfSignatureAppearance appearance, SigningInformation information, DateTime timestampDateTime)
    {
        return String.Format("Signee: {0}\nSign date: {1}\nLocation: {2}\nReason: {3}", information.Signatory, timestampDateTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss zzz"), appearance.Location, appearance.Reason);
    }

    private byte[] SignDocumentSignSignatureField(Certificate certificate, SigningInformation information, SigningBlock signingBlock, List<MemberItemSignature> signatureImages, byte[] document, bool isVisible)
    {
        SigningComponents components = new SigningComponents(_settingManager, certificate);
        using (MemoryStream outputStream = new MemoryStream())
        {
            using (PdfReader reader = new PdfReader(document))
            {
                using (PdfStamper stamper = CreatePdfStamper(reader, outputStream, true))
                {
                    PdfSignatureAppearance appearance = CreatePdfAppearance(stamper, information, SignDocumentSignSignatureFieldBuildLocation(stamper, signingBlock), false);
                    SignDocumentSigningBlock(components, information, signingBlock, appearance, stamper, GetSignatureImage(signatureImages, signingBlock.Name));
                }
            }

            return outputStream.ToArray();
        }
    }

    private string SignDocumentSignSignatureFieldBuildLocation(PdfStamper stamper, SigningBlock signingBlock)
    {
        StringBuilder builder = new StringBuilder();
        for (int index = 0; index < signingBlock.LinkedFormFields.Count; index++)
        {
            builder.Append(GetFormFieldValueForName(stamper, signingBlock.LinkedFormFields[index]));
            if (index + 1 < signingBlock.LinkedFormFields.Count)
            {
                builder.Append(", ");
            }
        }

        return builder.ToString();
    }

    private void UpdateFormField(PdfStamper stamper, FormField formField)
    {
        AcroFields formFields = stamper.AcroFields;
        if (formField.Value != null && GetFormFieldValueForName(stamper, formField.Name) != formField.Value)
        {
            formFields.SetField(formField.Name, formField.Value);
            formFields.SetFieldProperty(formField.Name, "setfflags", PdfFormField.FF_READ_ONLY, null);
        }
    }
}

}

1 个答案:

答案 0 :(得分:2)

简答:

如果您使用级别为PdfSignatureAppearance.CERTIFIED_FORM_FILLING的证书签名对文档进行签名,则可以根据需要添加任意数量的额外批准签名,而不会破坏原始(或之前)签名。

更长的回答:

如果你写的话,没有人会相信你:

  

我首先尝试使用PdfSignatureAppearance.CERTIFIED_FORM_FILLING签署文档,这应该允许我添加更多签名字段,编辑表单字段或签署其他签名字段,但它会使我后续的签名签名无效。

你在这里说的是错的。

但是,也许您没有正确签署PDF。请允许我重复我的简短回答:

如果您使用级别为PdfSignatureAppearance.CERTIFIED_FORM_FILLING认证签名签署文档,则可以根据需要添加任意数量的额外批准签名,而不会破坏原始文件(或之前)签名。

我强调两个概念。 PDF可以包含最多一个证书签名(也称作者签名),并且此签名应该是文档中的第一个签名。 PDF可以有多个批准签名(也称为收件人签名)。

也许您正在使用认证签名签署文档(我认为这是真的,因为您谈到了认证级别)。也许您正在尝试添加第二个认证签名。显然,这会破坏PDF中的签名,因为PDF规范只允许单个证书签名。

可能通过添加批准签名而不是认证签名来解决问题。