ctdt_eng
ctdt_eng
The main rationale for PDF used to be viewing and printing documents in a reliable way.
The technology was conceived with the goal “to provide a collection of utilities,
applications, and system software so that a corporation can effectively capture documents
from any application, send electronic versions of these documents anywhere, and view
and print these documents on any machines.” (Warnock, 1991)
Why we need PDF
This mission was set forth in the Camelot paper, and it was accomplished with the first
publication of the Portable Document Format Reference (Adobe, 1993) and the
availability of the first PDF software products created by Adobe. PDF became renowned
as the format that could be trusted to ensure a consistent output, be it on screen or in
print. In the years that followed, an abundance of new tools from Adobe as well as from
third party software vendors emerged, and the PDF specification was —and still is—
very much alive. Plenty of functionality has been added to the PDF format over the years.
Because of this, PDF has become the preferred document format of choice in many
professional sectors and industries. In this paper we’ll focus on one specific aspect of
PDF files that makes the choice for PDF over any other document format a no-brainer:
digital signatures.
Why we need digital signatures
Imagine a document that has legal value. Such a document may contain important
information about rights and obligations, in which case you need to ensure its
authenticity. You don’t want people to deny the commitments they’ve written down.
Furthermore, this document probably has to be mailed to, viewed and stored by different
parties. On different places in the workflow, at different moments in time, the document
can be altered, be it voluntary, for instance to add an extra signature, involuntary, for
example due to a transmission error, or deliberately, if somebody wants to create a
forgery from the original document. For centuries, we’ve tried to solve this problem by
putting a so-called ‘wet ink signature’ on paper. Nowadays, we can use digital signatures
to ensure:
the integrity of the document— we want assurance that the document hasn’t been
changed somewhere in the workflow,
the authenticity of the document— we want assurance that the author of the document
is who we think it is (and not somebody else),
non-repudiation— we want assurance that the author can’t deny his or her authorship.
In this paper, we’ll focus on documents in the portable document format (PDF).
2.2 The “Hello World” of digital signing using iText
Forget everything that was written in the first and second edition of “iText in Action”
(Lowagie, 2011); forget all the code you wrote before upgrading to iText 5.3.0. Signing a
PDF using iText has been completely redesigned. You’ll soon find out that the changes
are for the better.
2.2.1 A simple example adding a visible signature to a document Let’s start with the
“Hello World” app of signing. How can we use iText to sign a file and get a result as is
shown in figure 2.3?
With code sample 2.1, we can create a very simple digital signature using iText. Code
sample 2.1: the “Hello World” of signing with iText
public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String
digestAlgorithm, String provider, CryptoStandard subfilter, String reason, String
location) throws GeneralSecurityException, IOException, DocumentException { //
Creating the reader and the stamper PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest); PdfStamper stamper =
PdfStamper.createSignature(reader, os, '\0'); // Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason); appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig"); // Creating
the signature ExternalDigest digest = new BouncyCastleDigest(); ExternalSignature
signature = new PrivateKeySignature(pk, digestAlgorithm, provider);
MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0,
subfilter); }
Please be aware that this is far from the ‘definite code sample’, but it’s a start. Let’s
examine the example step by step.
First we create a PdfReader and a PdfStamper object. This is what you’ll always do in
iText when you want to manipulate an existing PDF document. The main difference is
that you have to use the createSignature() method instead of the PdfStamper constructor.
NOTE: If you need to sign a PDF/A file, you should use the createSignature() method
available in the PdfAStamper stamper class. This class can be found in a separate
itextpdfa.jar starting with iText 5.3.4. Secondly, we define the appearance using the
PdfSignatureAppearance class. We’ll go into more detail later on in this chapter, but for
now we only set a reason for signing and a location. We also define a rectangle where the
signature will be added (lower-left coordinate [36,748]; upperright coordinate [144,780]),
a page number (page 1), and a name for the signature field (“sig”).
Furthermore, we need an implementation of the ExternalDigest interface to create a
digest and of the ExternalSignature interface to create the signature, a process that
involves hashing as well as encryption. We can use Bouncy Castle as security provider
for the digest by choosing an instance of the BouncyCastleDigest class. If you want
another provider, use the ProviderDigest class. iText has only one implementation for the
signing process: PrivateKeySignature; we’ll use another implementation in chapter 4
when we sign a document using a smart card. The constructor of the PrivateKeySignature
class needs a PrivateKey instance, a digest algorithm and a provider. For instance: we
won’t use Bouncy Castle as provider when we apply a signature using PKCS#11 in
chapter 4.
NOTE: Unless otherwise specified, iText uses the same digest algorithm for making the
hash of the PDF bytes as defined for creating the signature. The encryption method
(“RSA”, “DSA” or “ECDSA”) and the key size will be obtained from the private key
object. If we use the key store we created in section 1.3.1, iText will use 2048-bit RSA
encryption because those were the parameters we passed to the keytool utility. We’re
using the signDetached() method, which means we’re creating a detached signature, and
we can choose between adbe.pkcs7.detached and ETSI.CAdES.detached. This is done
with an enum named CrypoStandard in MakeSignature: use either CMS or CADES.
WARNING: For the moment, we’re passing plenty of null objects and one 0 value to the
signDetached() method. It’s important to understand that we’re creating a signed PDF
that only meets the minimum requirements of a digital signature. You’ll need to replace
at least two of those null values with actual objects if you want to create a signature that
conforms to the best practices in signing. This will be discussed in chapter 3.
Code sample 2.2 shows how we can create a variety of signed Hello World files. First we
create a PrivateKey instance and an array of Certificate objects based on the keystore we
created in section 1.3.1. Then we invoke the method from code sample 2.1 using different
digest algorithms, choosing between CMS (PKSC#7 as described in ISO-32000-1 and
PAdES 2) and CAdES (as described in PAdES 3).
Code sample 2.2: signing a PDF using different algorithms and sub filters
public static final String KEYSTORE = "src/main/resources/ks"; public static final char[]
PASSWORD = "password".toCharArray(); public static final String SRC =
"src/main/resources/hello.pdf"; public static final String DEST =
"results/chapter2/hello_signed%s.pdf"; public static void main(String[] args) throws
GeneralSecurityException, IOException, DocumentException { BouncyCastleProvider
provider = new BouncyCastleProvider(); Security.addProvider(provider); KeyStore ks =
KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(new
FileInputStream(KEYSTORE), PASSWORD); String alias =
(String)ks.aliases().nextElement(); PrivateKey pk = (PrivateKey) ks.getKey(alias,
PASSWORD); Certificate[] chain = ks.getCertificateChain(alias); SignHelloWorld app =
new E04_SignHelloWorld(); app.sign(SRC, String.format(DEST, 1), chain, pk,
DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CMS, "Test 1",
"Ghent"); app.sign(SRC, String.format(DEST, 2), chain, pk, DigestAlgorithms.SHA512,
provider.getName(), CryptoStandard.CMS, "Test 2", "Ghent"); app.sign(SRC,
String.format(DEST, 3), chain, pk, DigestAlgorithms.SHA256, provider.getName(),
CryptoStandard.CADES, "Test 3", "Ghent"); app.sign(SRC, String.format(DEST, 4),
chain, pk, DigestAlgorithms.RIPEMD160, provider.getName(), CryptoStandard.CADES,
"Test 4", "Ghent"); }
Figure 2.4 shows the different icons that indicate whether or not a signature is valid. As
you can see, these icons have changed over the years depending on the version of Adobe
Acrobat or Reader you’re using. Figure 2.4: Different icons used to recognize if a
signature was validated A red cross always means that your signature is broken: the
content has been altered or corrupted, or one of the certificates isn’t valid, and so on. In
any case, you shouldn’t trust the signature.
WARNING: support for CAdES is very new. Don’t expect versions older than
Acrobat/Reader X to be able to validate CAdES signatures! Acrobat 9 only supports
signatures as described in the specification for PDF 1.7, and CAdES is new in PDF 2.0.
In figure 2.3, we get a yellow triangle with a message “At least one signature has
problems”. A yellow triangle (or a question mark in older versions of Adobe Reader)
means that the signature can’t be validated because some information is missing. In our
case, Adobe Reader says: “The signature validity is unknown.” There’s no problem with
the integrity because we see that the “Document has not been modified since this
signature was applied.” So what’s missing?
2.2.2 Manage trusted identities
There are different ways to add a certificate to the list of trusted identities. One way is to
look at the signature panel and to open the Certificate Viewer by clicking on Certificate
Details under Certificate details. See figure 2.5. Figure 2.5: Certificate details viewer: add
to trusted identities There’s a tab named Trust saying “This is a self-signed certificate.
The selected certificate path is valid.” However: “The certificate is not trusted.” In the
trust settings, there’s a button with caption “Add to trusted identities”. If you click this
button, you get another dialog, as shown in figure 2.6.
Figure 2.6: Import Contact Settings dialog You could decide to use this certificate as a
trusted root. Let’s try this and see what happens. Go to Edit > Protection > Manage
Trusted Identities… as shown in figure 2.7:
Figure 2.7: Import Contact Settings dialog
You’ll find Bruno Specimen listed if you display the Certificates as is done in figure 2.8
Figure 2.8: the Manage Trusted Identities dialog From now on, you’ll see a green check
mark when opening the signed document. See figure 2.9.
Figure 2.9: Signed document and the Signature is valid.
This is one way you can add the certificate to the trusted identities, but it defies the
purpose of a digital signature. If you accept the validity of the signer’s identity anyway,
why go through the hassle of signing? The initial idea we thought of when we discussed
authentication, was that Bruno Specimen would send you his public certificate, and that
you would use it to identify him. So let’s remove the certificate from that list of trusted
identities, and try anew using a different approach.
2.2.3 Adding a certificate to the Contacts list in Adobe Reader Bruno Specimen can
extract his public certificate from his key store using the command shown in code sample
2.3.
Code sample 2.3: exporting a certificate from a key store $ keytool -export -alias demo -
file bruno.crt -keystore ks -storepass password Certificate stored in file The result is a file
name that can be imported into Adobe Reader by clicking “Add Contacts” (see figure
2.8). Just browse for the file bruno.crt and click “Import” (see figure 2.10). The file will
show up in the list of Trusted Identities, but don’t forget to edit the trust settings! You
need to explicitly trust the certificate (as is shown in figure 2.6) before you’ll be able to
validate Bruno Specimen’s signature.
Figure 2.10: Import a Certificate into the Trusted Identities list But wait! What if you
don’t really know Bruno Specimen? What if somebody else sends you a public key
pretending he’s Bruno Specimen? How can you know that the person who is sending you
his public key is the person he or she says he or she is?
There are different ways and mechanisms that allow you to obtain a green check mark
without managing trusted identities manually. In chapter 3, we’ll discuss certificate
authorities, and in section 3.4, we’ll discover different options to get a green check mark
requiring less (or even no) manual intervention. First let’s sign our Hello World file once
more, using a different method to create a PdfStamper object. 2.2.4 Signing large PDF
files When you use the createSignature() method as shown in code sample 2.1, iText will
create a copy of the document you’re about to sign in memory, leaving the bytes that are
reserved for the signature blank. iText needs this copy so that it can provide the bytes that
need to be hashed and signed. This can be problematic for files with a large file size: you
risk OutOfMemoryExceptions. Take a look at code sample 2.4 if you want to store the
copy on disk instead of keeping it in memory:
Code sample 2.4: Signing a document using a temporary file
public void sign(String src, String tmp, String dest, Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, CryptoStandard subfilter, String reason, String
location) throws GeneralSecurityException, IOException, DocumentException { //
Creating the reader and the stamper PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest); PdfStamper stamper =
PdfStamper.createSignature(reader, os, '\0', new File(tmp)); // Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason); appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig"); // Creating
the signature ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm,
provider); ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0,
subfilter); }
There’s only one difference with code sample 2.1, we added a File object as an extra
parameter to the createSignature() method. The tmp variable in this code sample can be a
path to a specific file or to a directory. In case a directory is chosen, iText will create a
file with a unique name in that directory.
NOTE: if you use the createSignature() method with a temporary file, you can use an
OutputStream that is null, in that case, the temporary file will serve as the actual
destination file. This is good practice if your goal is to store a signed file on your file
system. If the OutputStream is not null, iText will always try to delete the temporary file
after the signing is done.
Make sure you use a path with sufficient writing permissions, and make sure you don’t
try to overwrite existing files if you’re working in a multithreaded environment. As we’re
working with very simple PDF files in these examples, we’ll continue using the method
that tells iText to keep the bytes in memory.
We’ve already seen an example of a PDF with an invisible signature (see figure 1.3) and
we’ve already seen PDF documents with a visible signature (see for instance figure 2.3).
In code samples 10 and 13, we created a visible signature using a Rectangle object and
absolute coordinates for the lowerleft corner and the upper-right corner. If you define a
rectangle of which either the width, or the height (or both) are zero, you’re creating an
invisible signature. When creating a document of which you know it will have to be
signed, you can choose the coordinates of the signature rectangle in advance.
2.3 Creating and signing signature fields
It’s not always simple to determine the coordinates where the signature should be placed.
That’s why you may want to create a placeholder if you need a visible signature. Let’s
start by creating a PDF document with an empty signature field using Adobe Acroba
2.3.2 Creating a signature field programmatically using iText
If you know how to create AcroForm fields using iText, you know how to create an
empty signature field as shown in figure 2.11. If you don’t know anything about
AcroForm fields, take a look at code sample 2.6.
Code sample 2.6: Creating a signature field
public void createPdf(String filename) throws IOException, DocumentException {
// step 1: Create a Document Document document = new Document();
// step 2: Create a PdfWriter PdfWriter writer = PdfWriter.getInstance( document, new
FileOutputStream(filename));
// step 3: Open the Document document.open();
// step 4: Add content document.add(new Paragraph("Hello World!")); // create a
signature form field PdfFormField field = PdfFormField.createSignature(writer);
field.setFieldName(SIGNAME); // set the widget properties field.setPage();
field.setWidget( new Rectangle(72, 732, 144, 780),
PdfAnnotation.HIGHLIGHT_INVERT);
field.setFlags(PdfAnnotation.FLAGS_PRINT); // add it as an annotation
writer.addAnnotation(field); // maybe you want to define an appearance PdfAppearance
tp = PdfAppearance.createAppearance(writer, 72, 48);
tp.setColorStroke(BaseColor.BLUE); tp.setColorFill(BaseColor.LIGHT_GRAY);
tp.rectangle(0.5f, 0.5f, 71.5f, 47.5f); tp.fillStroke(); tp.setColorFill(BaseColor.BLUE);
ColumnText.showTextAligned(tp, Element.ALIGN_CENTER, new Phrase("SIGN
HERE"), 36, 24, 25); field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL,
tp);
// step 5: Close the Document
document.close();
}
When using iText, a PDF file is created from scratch in five steps: create a Document
object, create a PdfWriter, open the Document, add content, and close the Document.
We’re interested in the fourth step: adding content. NOTE: iText has different
convenience classes for text fields (TextField), push buttons (PushbuttonField), radio
fields and checkboxes (RadioCheckField). For signatures, we use the generic class
PdfFormField and we create the signature field using the convenience method
createSignature(). We choose a name, and we set some other field properties if necessary.
In a PDF form based on AcroForm technology, each field corresponds with zero, one or
more widget annotations. These annotations define the visual appearance of the field on
the document. In this case, we use setPage() to indicate the signature field has to be added
to the current page. We use setWidget() to define the position on the page as well as the
behavior when somebody clicks on the field widget. There are four different types of
behavior:
HIGHLIGHT_NONE— no highlighting.
HIGHLIGHT_INVERT— inverts the content of the annotation square.
HIGHLIGHT_OUTLINE— inverts the annotation border.
HIGHLIGHT_PUSH— displays the annotation as if it were being pushed below the
surface of the page
If a field corresponds with a single widget annotation (as is the case here), the field
properties and the annotation properties are usually merged into a single dictionary
object. We can add the field and its visual representation to a document by adding the
PdfFormField object to the PdfWriter using the addAnnotation() method.
NOTE: Is it possible to have one signature correspond with more than one widget? I’m
sorry, but that’s not a good question. See the spec about digital signature appearances by
Adobe: “The location of a signature within a document can have a bearing on its legal
meaning. For this reason, signatures never refer to more than one annotation. If more than
one location is associated with a signature the meaning may become ambiguous.”
In code sample 2.6, we create an appearance for the empty signature using the
PdfAppearance class. This class extends the PdfTemplate class used in iText to create
small patches of reusable content. It’s not to be mistaken with the
PdfSignatureAppearance class. With PdfAppearance, you define what the field looks like
before it’s signed, whereas PdfSignatureAppearance defines what the field looks like
after signing. NOTE: Creating an appearance for an empty signature field is optional: if
you omit this code, a valid signature field will be added, but the end user might not really
notice it. He’ll only see a small orange ribbon added by Adobe Reader marking the
location of the field. The original, unsigned document and the resulting, signed document
for code sample 2.6 are shown next to each other in figure 2.12.
Figure 2.12: iText created document with empty signature field and the same document
signed
If you want to add a signature field to specific page of an existing document, you can
invoke the addAnnotation() method on a PdfStamper object passing the PdfFormField
instance and a page number as parameters. 2.3.3 Adding an empty signature field to an
existing document using iText In code sample 2.7, we pass the page number as a
parameter for the addAnnotation() method in PdfStamper. We don’t need to define the
page number on the level of the widget annotation.
Code sample 2.7: adding a signature field to an existing PDF
PdfReader reader = new PdfReader(src); PdfStamper stamper = new PdfStamper(reader,
new FileOutputStream(dest)); // create a signature form field PdfFormField field =
PdfFormField.createSignature(stamper.getWriter()); field.setFieldName(SIGNAME); //
set the widget properties field.setWidget(new Rectangle(72, 732, 144, 780),
PdfAnnotation.HIGHLIGHT_OUTLINE);
field.setFlags(PdfAnnotation.FLAGS_PRINT); // add the annotation
stamper.addAnnotation(field, 1); // close the stamper stamper.close();
We can now reuse the method from code sample 2.5 to sign a document to which we’ve
added a signature field using iText. Code sample 2.8 shouldn’t have any secrets for you
anymore.
Code sample 2.8: Signing a signature field created with iText CreateEmptyField
appCreate = new CreateEmptyField(); appCreate.createPdf(UNSIGNED);
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider); KeyStore ks =
KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(new
FileInputStream(KEYSTORE), PASSWORD); String alias =
(String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD); Certificate[] chain =
ks.getCertificateChain(alias); SignEmptyField appSign = new SignEmptyField();
appSign.sign(UNSIGNED, SIGNAME, DEST, chain, pk, DigestAlgorithms.SHA256,
provider.getName(), CryptoStandard.CMS, "Test", "Ghent");
The signature appearance as shown in the signed PDF of figure 2.12 is what a signature
created by iText looks like by default. It contains the following information:
Who has signed the document? — iText extracts this information from the certificate.
When was the document signed? — If you provide a connection to a timestamp server,
iText will use the date provided by the TSA (see chapter 3). Otherwise, iText will use the
date and time of the computer that is used for signing, or a Calendar object provided in
your code.
What was the reason for signing? — The reason is provided by you or your program.
Where was the document signed? — The location is provided by you or your program.
This information was chosen by the iText developers. It may not correspond with the way
you want to present a signature, so let’s find out how we can change this appearance.
2.4 Creating different signature appearances
Suppose your customer isn’t used to digital signatures. Suppose he doesn’t realize that
the field marked with the text “Digitally signed by…” is the visual representation of a
valid digital signature. Suppose that he wants to see an image of a wet ink signature
instead of some plain text. That image as such wouldn’t have any legal value whatsoever,
but it can be reassuring on a psychological level. That’s more or less what the
recommendations in PAdES part 6 are about, and why iText provides different methods
to create custom appearances for signatures.
2.4.1 Defining a custom PdfSignatureAppearance
In this section, I’m going to start by explaining something, and then I want you to
completely forget all about it: in early versions of the PDF specification, a signature
appearance consisted of five different layers that are drawn on top of each other. These
layers were numbered from n0 to n4: n0—Background layer.
n1—Validity layer, used for the unknown and valid state.
n2—Signature appearance, containing information about the signature.
n3—Validity layer, used for the invalid state.
n4—Text layer, for a text representation of the state of the signature
In old Acrobat versions, one would for instance create a graphic of a yellow question
mark, and put that into layer n1. On top of this yellow question mark, in layer n2, you’d
put the information about the signature. If the signature was made invalid, you’d see the
content of layer n3, usually a red cross. Layers would be made visible or not, depending
on the status of the signature. Now please forget about these layers. Since Acrobat 6
(2003) the use of layers n1, n3 and n4 is no longer recommended. The only reason I
mention them is to avoid questions about the following code snippet.
Code sample 2.9: Creating a custom appearance for the signature.
public void sign(String src, String name, String dest, Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, CryptoStandard subfilter, String reason, String
location) throws GeneralSecurityException, IOException, DocumentException { //
Creating the reader and the stamper PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest); PdfStamper stamper =
PdfStamper.createSignature(reader, os, '\0'); // Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason); appearance.setLocation(location);
appearance.setVisibleSignature(name); // Creating the appearance for layer 0
PdfTemplate n0 = appearance.getLayer(0); float x = n0.getBoundingBox().getLeft();
float y = n0.getBoundingBox().getBottom(); float width =
n0.getBoundingBox().getWidth(); float height = n0.getBoundingBox().getHeight();
n0.setColorFill(BaseColor.LIGHT_GRAY); n0.rectangle(x, y, width, height); n0.fill(); //
Creating the appearance for layer 2 PdfTemplate n2 = appearance.getLayer(2);
ColumnText ct = new ColumnText(n2); ct.setSimpleColumn(n2.getBoundingBox());
Paragraph p = new Paragraph("This document was signed by Bruno Specimen.");
ct.addElement(p); ct.go(); // Creating the signature ExternalSignature pks = new
PrivateKeySignature(pk, digestAlgorithm, provider); ExternalDigest digest = new
BouncyCastleDigest(); MakeSignature.signDetached(appearance, digest, pks, chain, null,
null, null, 0, subfilter); }
In code sample 2.6, we created a PdfAppearance for the signature field before signing,
but this appearance was lost and replaced with a default appearance chosen by iText.
Code sample 2.9 now creates a custom appearance instead of the default one. The code is
more complex than sample 2.1 and 2.5 because we use low-level methods to create a
custom PdfSignatureAppearance. We use the getLayer() method to obtain specific layers,
and we draw custom content for the background to layer 0, and information about the
signature to layer 2. NOTE: If you’ve followed the advice I gave in the first line of this
section, this is the point where you’re supposed to ask: Why are you only using layer 0
and layer 2? What happened to layer 1? The answer is: there used to be a layer 1, 3 and 4
(and you’ll find references to them in iText), but you should no longer use them.
PdfSignatureAppearance will ignore all changes applied to these layers, unless you add
the following line to your code: appearance.setAcro6Layers(false); (Again: this is not
recommended!)
In code sample 2.9, we chose a gray rectangle as background and the text “This
document was signed by Bruno Specimen” for the signature information. The result is
shown in figure 2.13.
2.5 Signatures in PDF and workflow
Imagine a book publisher drawing up a contract for a book written by two authors. Such a
book contract could contain one certification signature from the publisher, saying: “I
have written this contract. This is legally binding.” In addition, the authors could add
their approval signature, saying: “We agree to the terms of the contract.”
When I wrote my first books for Manning publications, I received a paper contract that I
had to sign with a wet ink signature. I then had to mail it from Belgium to the US using
snail mail. Wouldn’t it be great if we could use digital signatures to achieve this?
2.5.1 Sequential signatures in PDF
Figure 2.20 shows what a PDF that is signed multiple times looks like on the inside.
Figure 2.20: Schematic view of a PDF file that is signed trice
The part above the line marked with Rev1 is revision 1 of the document. It’s identical to
what we had in figure 2.1. When signing the document with a second signature, we don’t
change any of the bytes of revision 1. We add additional content (provided that the first
signature allows this content), and we create a new signature. This new signature is based
on a message digest that includes the entire byte array of revision 1. The result is revision
2. When signing the document with a third signature, the bytes of revision 2 are
preserved.
NOTE: Looking at figure 2.20, you see that the signatures have to be applied
sequentially. It’s not possible to sign in parallel. For instance: a publisher can’t send his
contract to two authors at the same time for approval, and then merge the signed
documents afterwards if the signatures need to be in the same document (as opposed to
bundled in a portfolio). One author always has to sign the contract first, then followed by
the other author.
We’ve already seen an example of a document that was signed twice by Bruno Specimen.
In the next section, we’ll make some examples that need to be signed by different parties.
2.5.2 Creating a form with placeholders for multiple signatures
Figure 2.21 is a form with three empty signature fields. In code samples 2.6 and 2.7, we
created such fields using the PdfFormField class and we used a hardcoded Rectangle to
define its position.
Figure 2.21: This form needs to be signed by Alice, Bob, and Carol In code sample 2.18,
we’ll let iText define the position. We’ll create a table with one column, and we’ll use
cell events to add the signature fields at the correct position in the table.
Code sample 2.18: Creating a form with empty fields
public void createForm() throws IOException, DocumentException { Document
document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, new
FileOutputStream(FORM)); document.open(); PdfPTable table = new PdfPTable(1);
table.setWidthPercentage(100); table.addCell("Signer 1: Alice");
table.addCell(createSignatureFieldCell(writer, "sig1")); table.addCell("Signer 2: Bob");
table.addCell(createSignatureFieldCell(writer, "sig2")); table.addCell("Signer 3: Carol");
table.addCell(createSignatureFieldCell(writer, "sig3")); document.add(table);
document.close(); } protected PdfPCell createSignatureFieldCell(PdfWriter writer, String
name) { PdfPCell cell = new PdfPCell(); cell.setMinimumHeight(50); PdfFormField field
= PdfFormField.createSignature(writer); field.setFieldName(name);
field.setFlags(PdfAnnotation.FLAGS_PRINT); cell.setCellEvent(new
MySignatureFieldEvent(field)); return cell; } public class MySignatureFieldEvent
implements PdfPCellEvent { public PdfFormField field; public
MySignatureFieldEvent(PdfFormField field) { this.field = field;}