如何在签名后锁定PDF文档?

如何在签名后锁定PDF文档?,pdf,permissions,itextsharp,signing,Pdf,Permissions,Itextsharp,Signing,Adobe Acrobat可以在签署PDF文档后锁定该文档。这将更改文档权限,以便Acrobat不再提供对文档的再次签名或修改批注或表单的功能。这似乎是一个合理的行动,在一个文件被多个实体签署审查,并最终由负责人发布之后 iTextSharp还可以签署文档,还可以向已签署的文档添加更多签名。iTextSharp基本上也可以设置文档的权限,但不知何故,我无法让它设置与Acrobat X Pro相同的权限。我设置了以下权限: PdfWriter.ALLOW_COPY PdfWriter.ALLOW

Adobe Acrobat可以在签署PDF文档后锁定该文档。这将更改文档权限,以便Acrobat不再提供对文档的再次签名或修改批注或表单的功能。这似乎是一个合理的行动,在一个文件被多个实体签署审查,并最终由负责人发布之后

iTextSharp还可以签署文档,还可以向已签署的文档添加更多签名。iTextSharp基本上也可以设置文档的权限,但不知何故,我无法让它设置与Acrobat X Pro相同的权限。我设置了以下权限:

  • PdfWriter.ALLOW_COPY
  • PdfWriter.ALLOW_降级_打印
  • PdfWriter.ALLOW_打印
  • PdfWriter.ALLOW_屏幕阅读器
我没有设置以下权限(应为所有其他权限):

  • PdfWriter.ALLOW_程序集
  • PdfWriter.ALLOW_FILL_
  • PdfWriter.ALLOW_MODIFY_注释
  • PdfWriter.ALLOW_MODIFY_内容
无论如何,保存这样的文档后,我在Acrobat X Pro中看到以下权限:

  • 打印:允许(OK)
  • 修改单据:不允许(确定)
  • 组装:不允许(正常)
  • 复制内容:允许(确定)
  • 屏幕阅读器:允许(OK)
  • 删除页面:允许(不确定)
  • 注释:允许(不确定)
  • 填写表格:允许(不允许)
  • 标志:允许(不正常)
  • 创建模板:允许(未知)
我没有使用加密,因为只有在放入第一个签名之前才可能使用加密。此外,我并不真的想使用它,因为a)没有查看器密码基本上是无用的,b)Acrobat在签名后锁定文档时也不会这样做

我的代码基于iSafePDF项目(Codeplex上的开源),该项目以某种方式在现有文档上使用PdfStamper

我的iTextSharp版本是5.2.1。最新版本有我尚未解决的不兼容API更改

那么,我如何才能达到与Acrobat相同的效果呢

(我知道我的应用程序仍然能够对文档进行签名,因为它不关心现有权限。但至少其他Acrobat用户应该正确地看到“预期权限”。)

更新:


我进一步研究了这个问题,它似乎来自这里:权限只能通过PdfStamper.SetEncryption方法设置,作为其第四个参数。但是调用此方法并附加签名会导致以下DocumentException:“附加模式不支持更改加密状态。”我没有看到设置权限但不设置加密的方法。这就是问题所在吗?iTextSharp是否不支持实际可能的功能?

如果您的用例只需要对未签名的PDF进行签名,那么锁定将很容易:您只需为
pdfsignaturepearance
对象设置
CertificationLevel=certificated\u NO\u CHANGES\u ALLOWED
。但是你的用例是

将新签名字段中的新签名添加到现有PDF中,该PDF可能已签名,也可能未签名

解决方案有些困难:必须使用FieldMDP转换方法,而不是DocMDP转换方法(用于认证)。有关详细信息,请阅读,尤其是第12.8节

我试图一步完成这项工作,但不幸的是,iText在其当前状态(版本5.4.4)下只正确地支持现有字段中的锁字典

@Bruno为签名时在运行时创建的字段添加锁字典支持应该不会太困难

因此,这里有一个两步解决方案,首先添加一个带有锁定信息的空签名字段,然后对该字段进行签名。我是用Java实现的(我更熟悉Java),但移植到C#应该不会太难

正如您所看到的,在第二步对准备好的空签名字段进行签名时,没有什么特别的事情要做,iText会在引擎盖下应用锁

不过,这项功能仅在iText 5.3.2之后才可用,我还没有检查它何时完全移植到iTextSharp

对于测试运行(使用自签名测试证书,因此出现警告),我得到:

输入文件签名一次:

输出文件已签名两次并锁定:


您是否阅读过Bruno Lowagie(iText软件)的白皮书,特别是第2.5.5节签名后锁定字段和文档?相应的c#/iTextSharp示例可以在iTextSharp SVN存储库中找到。不,还没有。但它看起来足够好,所以我认为它将为我的问题提供一个答案。同时,我发现了如何使用认证签名,并将其与Acrobat的行为进行了比较。但是锁定是另一个用例。(Adobe的帮助翻译成德语是错误的,它自相矛盾,无法理解。这也无助于了解这件事。)难道这一章只是关于创建可以在Acrobat中签名的签名字段,而Acrobat又会在签名后添加锁吗?那不是我想做的。iText将直接添加签名,而不仅仅是一个字段。所以iText也是需要添加锁的人。好的,所以本质上你的代码必须像AdobeReader那样签名。这是可能的,但需要一些自己的编码。从开始阅读该文档开始,我就知道我无论如何都需要升级iText,关于数字签名,我已经做了很多改变。所以我可能不得不重写它。更新它需要一些时间。布鲁诺是谁?请注意,@回复在回答中不起作用。这是众所周知的,但如果读者是布鲁诺,它仍然是向读者发出信号的一种方式。我在这里向布鲁诺·洛瓦吉讲话。
// STEP 1 --- prepare a signature field with locking information
//
// Creating the reader and the stamper for adding the field
PdfReader reader = new PdfReader(SRC);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, baos, (char)0, true);

// adding the empty signature field
PdfFormField field = PdfFormField.createSignature(stamper.getWriter());
field.setFieldName("Signature");
field.put(PdfName.LOCK, stamper.getWriter().addToBody(new PdfSigLockDictionary(LockPermissions.NO_CHANGES_ALLOWED)).getIndirectReference());
field.setFlags(PdfAnnotation.FLAGS_PRINT);
field.setPage(1);
field.setWidget(new Rectangle(150, 250, 300, 401), PdfAnnotation.HIGHLIGHT_INVERT);
stamper.addAnnotation(field, 1);

// finishing the intermediate PDF
stamper.close();
reader.close();

// STEP 2 --- sign the prepared signature field, nothing special
//
// Creating the reader and the stamper for signing
reader = new PdfReader(baos.toByteArray());
FileOutputStream os = new FileOutputStream("target/test-outputs/test_signed-with-lock-field-2step.pdf");
stamper = PdfStamper.createSignature(reader, os, '\0', null, true);

// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason("reason");
appearance.setLocation("location");
appearance.setVisibleSignature("Signature");

// Creating the signature
ExternalDigest digest = new BouncyCastleDigest();
ExternalSignature signature = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, "BC");
MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, CryptoStandard.CMS);