如果您的用例仅需要签署未签名的 PDF,锁定将很容易:您只需设置CertificationLevel = CERTIFIED_NO_CHANGES_ALLOWED
为您PdfSignatureAppearance
目的。但正如你的用例是
在新签名字段中将新签名添加到可能已签名或未签名的现有 PDF,
解决方案有点困难:而不是DocMDP变换方法(用于认证)FieldMDP变换方法必须使用。欲了解详细信息,请阅读ISO 32000-1,特别是第 12.8 节。
我尝试一步完成此操作,但不幸的是,当前状态(版本 5.4.4)的 iText 仅正确支持已存在字段中的锁定字典。
@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](https://i.stack.imgur.com/DwNtr.png)
输出文件签名两次并锁定:
![enter image description here](https://i.stack.imgur.com/JbiPP.png)