1

Adobe Acrobat has the option to lock the PDF document after signing it. This changes the document permissions so that Acrobat does not offer signing the document again or modifying annotations or forms. This seems like a reasonable action to do after a document has been signed for review by multiple entities and finally for release by someone responsible.

iTextSharp can also sign documents, and it can also add further signatures to an already signed document. iTextSharp basically also can set the document's permissions, but somehow I can't get it to set the same permissions as Acrobat X Pro does. I set the following permissions:

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

I do not set the following permissions (which should be all else):

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

Anyway, after saving a document like that, I see the following permissions in Acrobat X Pro:

  • Printing: Allowed (OK)
  • Modifying document: Not allowed (OK)
  • Assembly: Not allowed (OK)
  • Copy contents: Allowed (OK)
  • Screen reader: Allowed (OK)
  • Remove pages: Allowed (NOT OK)
  • Annotate: Allowed (NOT OK)
  • Fill in forms: Allowed (NOT OK)
  • Sign: Allowed (NOT OK)
  • Create templates: Allowed (unknown)

I'm not using encryption because that is only possible before putting the first signature. Also, I don't really want to use it because a) it's basically useless without a viewer password and b) Acrobat doesn't do it either when locking a document after signing.

My code is based on the iSafePDF project (open-source on Codeplex) which is using PdfStamper in some way on an existing document.

My iTextSharp version is 5.2.1. The latest version has incompatible API changes that I didn't resolve yet.

So how can I achieve the same result as with Acrobat?

(I'm aware that my application will still be able to sign the document because it doesn't care for the existing permissions. But at least other Acrobat users should see the "intended permissions" correctly.)

Update:

I have further investigated the issue and it seems to come from here: The permissions can only be set through the PdfStamper.SetEncryption method, as its 4th parameter. But calling this method together with appending a signature leads to the following DocumentException: "Append mode does not support changing the encryption status." I haven't seen a method that sets permissions but not encryption. Is this the problem? Does iTextSharp simply not support what's actually possible?

ygoe
  • 18,655
  • 23
  • 113
  • 210
  • Have you read [Digital Signatures for PDF documents](http://itextpdf.com/book/digitalsignatures), *A White Paper by Bruno Lowagie (iText Software)*, especially section 2.5.5 *Locking fields and documents after signing*? The corresponding c#/iTextSharp samples can be found on the iTextSharp SVN repository. – mkl Nov 15 '13 at 21:25
  • No, not yet. But it looks good enough so that I assume it will provide an answer to my question. Meanwhile, I figured out how to use certification signatures and compared it to Acrobat's behaviour. But locking is another use case. (Adobe's help translated into German is so wrong that it contradicts itself and cannot be understood. That also doesn't help in learning the matter.) – ygoe Nov 15 '13 at 22:48
  • Could it be that this chapter is only about creating signature fields that can be signed in Acrobat, which in turn will add the lock after signing? That's not what I want to do. iText will be adding the signature directly, not just a field for it. So iText is the one who needs to add the lock as well. – ygoe Nov 16 '13 at 09:42
  • Ok, so essentially your code has to sign like Adobe Reader does. That is possible but requires some own coding. I'll try and look into that later. – mkl Nov 16 '13 at 10:11
  • From beginning to read that document, I know that I need to upgrade iText anyway, and much has changed concerning digital signatures. So I may have to rewrite it anyway. It'll take some time to update it. – ygoe Nov 16 '13 at 13:23
  • To implement advanced signing related features, you should indeed build on at least 5.3.x, better on the current version. – mkl Nov 16 '13 at 13:48
  • I've tried to quickly backport the relevant code and found that it's not working. And it can't. The example in the book only explains how to apply a lock on an empty signature field with the PdfWriter. I only have PdfReader and PdfStamper, no writer, and I also have no empty signature field but a full signature already. So the book doesn't help me. Any ideas how to do it? – ygoe Nov 16 '13 at 22:36
  • You *have PdfReader and PdfStamper, no writer* - that should be no problem, signature fields can be created in a very similar manner. You *also have no empty signature field but a full signature* - do you by *full signature* mean a signature field already signed? Then it is too late to lock as part of this signature. Or do you have a signature field with all signing preparations done, ready for a `preclose` call? That might be an interesting case. As is iText is does locking automatically if a signature field has the appropriate lock dictionary. If you have code (sscce) and sample documents... – mkl Nov 25 '13 at 11:27
  • Sorry, now I don't understand anymore what you mean. At no time, I want a signature field that has no actual signature. This is not part of my app/workflow design. My input is a PDF with no fields (or a PDF that is already signed), and my output is a PDF with all existing signatures plus one. And Acrobat can do all that, including locking after signing, also after multiple signatures. I want to do the same with iText. – ygoe Nov 25 '13 at 14:53
  • Ok, you add a new signature in a new signature field to an existing PDF which may or may not be signed. And you want to lock it to forbid additional changes. Is that correct? – mkl Nov 25 '13 at 15:21
  • I implemented it using Java/iText, see my answer below. – mkl Nov 27 '13 at 10:28

1 Answers1

2

If your use case only required signing unsigned PDFs, locking would be easy: You simply would have set CertificationLevel = CERTIFIED_NO_CHANGES_ALLOWED for your PdfSignatureAppearance object. But as your use case is to

add a new signature in a new signature field to an existing PDF which may or may not be signed,

the solution is somewhat more difficult: Instead of the DocMDP transform method (used for certification) the FieldMDP transform method has to be used. For details read ISO 32000-1, especially section 12.8.

I tried to do this in one step but, unfortunately, iText in its current state (version 5.4.4) only properly supports lock dictionaries in already existing fields.

@Bruno It should not be too difficult to add a lock dictionary support for fields created on the run while signing, too.

Thus, here a two-step solution first adding an empty signature field with locking information and then signing this field. I did it in Java (I'm more at home there) but this should not be too difficult to port to 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);

As you see, there is nothing special to do in the second step when signing the prepared empty signature field, iText applies the lock under the hood.

This feature, though, has only been available since iText 5.3.2, and I have not checked when it was completely ported to iTextSharp.

For a test run (using self-signed test certificates, thus the warnings) I got:

The input file signed once:

enter image description here

The output file signed twice and locked:

enter image description here

mkl
  • 90,588
  • 15
  • 125
  • 265