Signing PDFs Using A Digital Certificate

  • Control F5
  • 5 min read

Did you ever had the need to attach a digital certificate to a pdf? No? Goodbye then!

Naah, just kidding 😅. In today’s study case we are going to find out what digital certificates are, why you should care about them and when you should put them to good use.

Background story

Let’s say I was a contractor, providing services to your customers through a web application. If I am going to sell my services online, then there will surely be documents involved, for example contracts. In a legal matter I need to be able to prove that it was my application through which I was selling my services and not some other malicious website. So I need to prove my application’s authenticity when sending my contracts as pdfs to the customers. That’s where we come to the next point, the digital certificates.

Digital certificates

You can think about digital certificates as passports. A passport contains a unique identification number, information about the person and it is issued by a legal authority.

The difference is that when digital certificates are verified, the issuing authority or the certificate authority, is the one who is verified.

Solution

Now 77.5% of all web servers are running on php and you must be thinking “How in the world can I attach that digital certificate to my documents?”.

I am happy to introduce you to two packages which are going to help us do the job. 

FPDI https://github.com/Setasign/FPDI – FPDI is a package used for importing pdf documents.

TCPDF https://github.com/tecnickcom/TCPDF – A php library for generating pdf documents. 

We will combine FPDI, the documents importer and TCPDF, the documents signer in order to reach our goal. Let’s move on to the next step, the installation.

 

Instalation

To install FPDI and TCPDF we are going to need 2 commands:

				
					composer require setasign/fpdi
composer require tecnickcom/tcpdf

				
			

Implementation

First, I added a new config variable called digital_certificate_path in config/app.php

				
					'digital_certificate_path' => env('DIGITAL_CERTIFICATE_PATH')
				
			

I created a dummy pdf called Contract.pdf containing a simple string and moved it under storage/app/public folder:

I created a service called PdfSignatureService. It will contain 3 methods

  • getStorage – returns an instance of our local storage
  • getDigitalCertificatePath composes the absolute digital certificate path
  • applyDigitalSignature signs the pdf using the digital certificate

This is how the service is going to look

				
					class PdfSignatureService

{

   /**

    * @return Filesystem

    */

   public function getStorage(): Filesystem

   {

       return Storage::disk('public');

   }


   /**

    * @return string

    */

   public function getDigitalCertificatePath(): string

   {

       return 'file://' . storage_path('app/certs') . '/' . config('app.digital_certificate_path');

   }


   /**

    * @throws CrossReferenceException

    * @throws PdfReaderException

    * @throws PdfParserException

    * @throws PdfTypeException

    * @throws FilterException

    */

   public function applyDigitalSignature(): void

   {

       $fpdi = new \setasign\Fpdi\Tcpdf\Fpdi();


       $resource = $this->getStorage()->readStream('Contract.pdf');

       $pages = $fpdi->setSourceFile(new StreamReader($resource));


       $digitalCertificatePath = $this->getDigitalCertificatePath();


       for ($i = 1; $i <= $pages; $i++)

       {

           $fpdi->AddPage();

           $page = $fpdi->importPage($i);

           $fpdi->useTemplate($page);


           $fpdi->setSignature($digitalCertificatePath, $digitalCertificatePath);

       }


       $contents = $fpdi->Output(dest: 'S');


       $this->getStorage()->put('SignedContract.pdf', $contents);

   }

}


				
			

Diving into applyDigitalSignature method

Let’s find out how this works under the hood.

In applyDigitalSignature we create a new Fpdi instance.

We open a stream resource to our Contract.pdf and set Fpdi to use this resource.

We will iterate through all pdf pages, import them, set the signature using the digital certificate and finally store the new content to a new SignedContract.pdf file.

But we don’t have the certificate yet, so let’s create it.

Creating the certificate

First we will create a new certs folder under storage/app/ and then we will create the digital certificate using the following command

				
					openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout app_cert.crt -out app_cert.crt
				
			

Test of truth

I am going to test this using Laravel’s php artisan tinker command

Done, now let’s have a look at the new SignedContract.pdf using LibreOffice Draw since it can detect such digital signatures

We have confirmation! Our certificate exists! Don’t worry about the warning, it is displayed because we created a self signed certificate. For the purpose of this demo this certificate is going to be enough. Now let’s display the certificate details. You can do this in LibreOffice by clicking File>Digital Signatures and voila

Our certificate details, as we expected. Awesome! It works 🎉.

Conclusion

Digital certificates and CA’s play a very important role when verifying the identity of an application (server) and not just only that, there are also other types like client and email certificates. They show if an application is trustworthy.

That is it for today, in our next blog, we are going to explore how to make fillable documents using the Google Docs API.

Now make a break, go outside, enjoy life and as always, happy coding!

Useful links:

Github repositories:

Marius Cristea

Marius Cristea

Full stack developer @ Control F5

Leave a Reply

Your email address will not be published. Required fields are marked *