签名后如何锁定PDF文档?

时间:2013-11-15 18:38:56

标签: pdf permissions itextsharp signing

Adob​​e Acrobat可以选择在签名后锁定PDF文档。这会更改文档权限,以便Acrobat不再提供对文档的签名或修改注释或表单。在签署文件供多个实体审核并最终由负责人发布后,这似乎是一项合理的行动。

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

  • PdfWriter.ALLOW_COPY
  • PdfWriter.ALLOW_DEGRADED_PRINTING
  • PdfWriter.ALLOW_PRINTING
  • PdfWriter.ALLOW_SCREENREADERS

我没有设置以下权限(应该是其他所有权限):

  • PdfWriter.ALLOW_ASSEMBLY
  • PdfWriter.ALLOW_FILL_IN
  • PdfWriter.ALLOW_MODIFY_ANNOTATIONS
  • PdfWriter.ALLOW_MODIFY_CONTENTS

无论如何,保存这样的文档后,我在Acrobat X Pro中看到以下权限:

  • 打印:允许(确定)
  • 修改文档:不允许(确定)
  • 装配:不允许(确定)
  • 复制内容:允许(确定)
  • 屏幕阅读器:允许(OK)
  • 删除页面:允许(不正常)
  • 注释:允许(不正常)
  • 填写表格:允许(不行)
  • 签名:允许(不行)
  • 创建模板:允许(未知)

我没有使用加密,因为这只能在放置第一个签名之前进行。另外,我真的不想使用它,因为a)没有查看器密码,它基本上没用; b)签名后锁定文档时,Acrobat不会这样做。

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

我的iTextSharp版本是5.2.1。最新版本的API更改不兼容,我还没有解决。

那么我怎样才能获得与Acrobat相同的结果?

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

更新

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

1 个答案:

答案 0 :(得分:1)

如果您的用例仅需要签署未签名的PDF,则锁定很简单:您只需为CertificationLevel = CERTIFIED_NO_CHANGES_ALLOWED对象设置PdfSignatureAppearance即可。但正如你的用例是

  

在新签名字段中为现有PDF添加新签名,该签名可能已签名,也可能未签名,

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

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

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

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

// 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);

如您所见,在签署准备好的空签名字段时,第二步没有什么特别的事情要做,iText在引擎盖下应用锁。

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

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

输入文件签名一次:

enter image description here

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

enter image description here