How to digitally sign PDF files?

Today I want to show you how to sign PDF files and how it works. We will sign PDF by our own, self-signed certificate, but I will also explain how to use certificates from the Certificate Authority. I’ll also show you how to deal with Time Stamp in a signature. So, let’s start.

What do we want to achieve?

We want to generate a digitally signed PDF file, so the expected file should look something like:

What will we use?

  1. Spring Boot 2.1.0
  2. PDFBox 2.0.12
  3. Bouncycastle 1.60
  4. Gradle 4.6

Source code

The full example is available on my Github.

Problem understanding

If you open PDF file in a notepad you can notice that file starts with %PDF and ends with %%EOF. In between, you can find a lot of information, some of them are unreadable. What we have to do is a get full content of the document, use one of the available algorithms to create a signature and then put this signature back to the document. The signature needs to know from what byte the content starts and where it ends, for being able to check if content not changed.

The topic is easy to understand but it’s quite extensive. I don’t want to dive here in every aspect of this problem. I recommend reading a really great document created by iText. You will find there almost everything what you need to know about PDF signing, the rest you will find in this post 🙂
You can download iText document for free here. Even if I had to pay for that thousand dollars, it’s worth it!

Choosing a proper library

I assume you are already using a library to generate PDFs. It doesn’t matter if you use iText, JasperReport, PDFjet, or something else. Some of these libraries support digital signatures so you don’t need to add an extra library. I noticed that most of the libraries use under the hood bouncycastle library to create signatures, the concept is the same so you can choose any of available solutions.

In my case, I used JasperReport to generate PDF’s which doesn’t support digital signatures implicitly, so I was forced to find another library. The first idea was using iText, but it’s on AGPL license, this mean that you can use this library for free as long as your code is opensource, otherwise you have to buy a license, what didn’t suit my situation. I found a great, free replacement created by Apache. I mean about PDFBox and we will use this library in our example, but as I said, every of available solutions is really similar, so even if you’ll choose iText or something else you will be able to adjust my solution to your needs. My code is based on PDFBox Github, you can find there some examples.

Creating a keystore

First of all, we need to generate keystore where we will keep our private and public keys. The private key will be used to create the signature, and the public one will be exported in a certificate. Keystore can keep several keys pairs, each of them is created with a proper alias to be able to distinguish them. To create keystore we need to use keytool – key and certificate management tool. Keytool is provided with standard JDK, so you don’t need to install anything.

  1. Let’s open cmd and go to the directory where you want to keep a keystore
  2. Enter the command:
  3. You have to enter keystore password. This password will be used to open the keystore which contains keys pair. We will use it in our application to create a signature.
  4. In the next step, you can provide some information which will be added to your self-signed certificate with provided alias. These fields aren’t necessary so you can omit that for test purpose.
  5. In the end, you can provide a separate password for your certificate.
  6. The keystore with keys pair was created in a current directory.

What exactly have we done?

We generated a 2048-bit RSA key pair valid for 365 days under the specified alias. The alias name depends on you. The key pair was added to a keystore file with default ‘.jks’ extension. We will use the private key to sign a pdf, the public one will be used to signature verification. The keystore and certificate should be adjusted to your needs so read the documentation and play with that.

Adding keystore to our project

The private key has to be protected. When private key will be stolen, someone can impersonate us, that sounds like big trouble! So we cannot hardcode our keystore password. For our example, I put my keystore path and password in environment variables, and then I set some variables in an application.yml file.

Signing Service

Finally, we can start writing some code.  I suggest starting by creating a SigningService where we will load keystore, get bytes to sign, and then call the proper method which digitally signs our document.

Let’s inject our keystore path, password, and alias.

Right now we can load our keystore.

The KEY_STORE_TYPE is a keystore file extension, by default it’s .jks but you could also generate .pkcs12 or whatever. I keep this extension in private static final variable.

The next step is to create a Signature class. I remind you that you can find fully implementation on my Github.
The Signature class has to implement SignatureInterface from PDFBox library. As you can see there is only one method to override, the PDFBox will call this method internally.

The CMSSignedDataGenerator is a class from bcpkix which we already added to the project. It’s nothing else like Cryptographic Message Syntax with Signed-data content type.
To the Cert variable, we assign our certificate from the certificate chain. I assumed that we have only one certificate in a chain, but of course, there could be a lot of them, so you need to play with that in this case. The next three lines is a bouncycastle magic, what’s important for you that you should pass proper signature algorithm as a parameter. I recommend you to use Sha256WithRSA as in an example. Now we have to create a CMSProcessableInputStream which implements CMSTypedData, there is nothing fancy, just two fields and three simple methods to override, check my Github once again. The if statement will be described in a next post about Time Stamps. In a shortcut, you should also ensure that your signature was generated in a specific, valid time. On the Internet, you can find a lot of Time Stamp Authorities which allows you to create signatures with Time Stamp.  The last step is getting an encoded signed data.

Our Signature is fully implemented, that was easy, huh? Let’s back to our SigningService, now we are able to create a Signature object.

We need to store PDFs temporarily. I mean, we will store our not signed PDF, and create an empty PDF file which will be filled by the signed bytes. I recommend using File.createTempFile method. This method will save the object in a default temp file, and at the end, we can simply remove them by calling a deleteOnExit method. Something like that:

The last step is to implement a signDetached function.

And once again, nothing fancy here, we just need to call some PDFBox functions. You can easily customize the above parameters. The most important thing is to use the saveIncremental function instead of save. The first one allows to add signature without changing data, the second one may override some information, so the signature won’t be valid. Be careful!

The last step is just to call the signPdf method from our SigningService. Open the file where you generate PDF, in my case that will be PdfResources class, inject our service and pass bytes to the proper method. In my case it looks like:

Now, the best part, run an application, go to localhost:8080/api/pdf/export, and open an PDF! 🙂


Finally, we can see the result of our implementation. I just remind that you have to use Acrobat Reader to open PDF if you want to see signature.

The PDF and signature should look like:

Wait a second, why we get a message that at least one signature has problems?

Untrusted certificate – problem understanding

We have to be aware that our self-signed certificate isn’t worth too much as long as it’s not trusted. What it does mean? Everyone who gets the signed PDF by our certificate has to trust that it’s correct, and manually add the certificate to trusted ones.
Let’s assume that your company is called X and you want to send signed files to a company called Y. The default scenario should look like:

  1. First, your company (X) send a certificate with a public key to the company Y
  2. Y have to manually add the certificate to the trusted store
  3. Now you can send a signed PDFs which will be valid
  4. If you want to send similar files to another company e.g. called Z, then you have to start from point 1.

The above scenario sounds creepy, huh? And it is, but don’t worry, we can deal with that.

The Adobe has owned Trusted Certificates list, and if you have one of them you don’t have to send the certificate to your clients. How to obtain a proper certificate, which can be used on a production? Just visit the Adobe site where you can find an Approved Trust List, then contact with one of the Certification Authority and request a certificate. When you get your certificate just put them to your keystore and that’s it.

Setting a certificate as trusted

Even if we don’t have a certificate from a Certificate Authority we want to get a Green Tick:) In this purpose, we have to add our self-signed to the trusted ones. We can achieve the Green Tick in two ways.

The simplest one:

  1. Open our signed PDF in Acrobat Reader
  2. Click Signature Panel
  3. Expand Signature Details
  4. Click Certificate Details…
  5. Go to Trust tab
  6. Click Add to Trusted Certificates…
  7. You can set here some privileges, but for our purpose, the default is enough
  8. Click OK
  9. Reopen PDF
  10. Finally get a Green Tick 🙂

The second one:

  1. Open the cmd where you keep keystore
  2. Enter command:[code]keytool -export -keystore keystore.jks -alias jvmfy -file jvmfy.cer</b><span style=”font-family: Consolas, Monaco, monospace;”>[/code]
  3. You just generated jvmfy.cer file
  4. Open the Windows Certificate Manager
  5. Add generated certificate to the Personal or Trusted store

The final result

Next steps

Of course, we can still improve our signature by adding Time Stamp. This step allows to change the strange message Signing time is from the clock on the signer’s computer to the nice info like The signature includes an embedded timestamp. But we will do it in the next post.

Leave a Reply