Blog Post

How to add digital signatures to PDFs using Laravel

Illustration: How to add digital signatures to PDFs using Laravel

In this tutorial, you’ll learn how to add digital signatures to PDF documents using Laravel. It’ll cover two methods: one using the Intervention Image library, and another leveraging the Nutrient Laravel PDF digital signature library.

What are digital certificates?

In the digital realm, digital certificates function like electronic identification cards. They’re used to confirm the legitimacy of online transactions, communications, and documents. A digital certificate contains the following:

  • Public key — An openly shared cryptographic key used for encrypting data.

  • Private key — A closely guarded key used for decrypting encrypted data and creating digital signatures.

  • Issuer information — Details about the certificate authority (CA) that issued the certificate, accompanied by the digital signature.

  • Certificate holder information — Identity details of the certificate owner.

Importance of digital certificates for digital signatures

There are a handful of reasons why digital certificates are important, including:

  1. Authentication — Think of digital certificates as the online equivalent of showing your ID. They ensure the identity of the signer.

  2. Non-repudiation — When you sign something digitally, you can’t later deny it. The certificate provides undeniable proof.

  3. Integrity — Once a document is signed, any modifications are detectable, preserving the document’s integrity.

  4. Encryption — Private keys safeguard your digital signature, making it nearly impossible to fake.

  5. Trust — Certificates are issued by trusted entities (CAs), building trust in the validity of signatures.

  6. Global acceptance — Digital certificates are universally accepted, making them suitable for international transactions.

How digital certificates work in digital signatures

When you digitally sign a document, your private key generates a unique signature. This signature, along with your digital certificate, is attached to the document. When someone receives the document, they use the public key from your certificate to verify the signature’s authenticity and ensure the document hasn’t been tampered with.

In summary, digital certificates serve as digital IDs that validate the authenticity of digital signatures. They establish trust, ensure accountability, and safeguard the integrity of electronic transactions and documents.

Information

Learn more about this in our blog post on the topic, PDF advanced electronic signatures, and why your business needs them.

Intervention image library

The Intervention Image library is primarily designed for image manipulation, offering various image editing functionalities within Laravel applications. While you can use Intervention Image to overlay an image (a signature in this case) onto a PDF image, it’s not tailored specifically for handling the complexities of creating valid and secure digital signatures, and it might lack advanced features for working with PDF structures and cryptography.

Prerequisites

To get started, you’ll need:

  • The latest stable version of Node.js

  • A package manager compatible with npm

  • PHP and Composer

  • Laravel installer

Make sure you have PHP installed on your system. You can check if PHP is installed by running php -v in your terminal. If it’s not installed, download and install it from the official PHP website.

After installing PHP, you’ll need Composer, a dependency management tool for PHP. Follow the installation instructions on the Composer website to install Composer.

Laravel provides a command-line tool called laravel that makes it easy to create new projects. You can install this tool globally using Composer:

composer global require laravel/installer

Make sure Composer’s global binaries directory is in your system’s PATH so that you can access the laravel command from anywhere:

export PATH="$HOME/.composer/vendor/bin:$PATH"

Step 1 — Setting up your Laravel project

Start by creating a new Laravel project. Open your terminal and run the following command:

laravel new DigitalSignatureProject

Press enter to accept default options when prompted. This command will create a new Laravel project named DigitalSignatureProject.

Navigate to the project directory:

cd DigitalSignatureProject

Step 2 — Installing the intervention image library

Use the Intervention Image library to manipulate images in your Laravel application. Install it using Composer:

composer require intervention/image

Step 3 — Generating the controller and routes

Generate a controller named SignatureController using the following command:

php artisan make:controller SignatureController

Open the routes/web.php file and define the routes for generating images, displaying the signature form, and saving the signature:

use App\Http\Controllers\SignatureController;

Route::get('/signature', [SignatureController::class, 'generateImages']);
Route::get('/signature-form', function () {
    $images = range(1, 3); // Replace with the actual number of pages
    return view('signature', compact('images'));
})->name('signature.form');
Route::post('/save-signature', [SignatureController::class, 'saveSignature'])->name('save.signature');

Step 4 — Generating PDF images

In SignatureController, define the generateImages method to generate images from a PDF using the Intervention Image library. This method will save images for each page of the PDF:

// app/Http/Controllers/SignatureController.php

<?php

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Spatie\PdfToImage\Pdf;
use Intervention\Image\Facades\Image;

class SignatureController extends Controller
{
    public function generateImages(Request $request)
    {
        $pdfPath = public_path('document.pdf'); // place your PDF file in the public folder
        $pdf = new Pdf($pdfPath);

        for ($pageNumber = 1; $pageNumber <= $pdf->getNumberOfPages(); $pageNumber++) {
            $pdf->setPage($pageNumber)->saveImage(public_path("page{$pageNumber}.jpg"));
        }

        return response()->json(['message' => 'Images generated successfully']);
    }

    // ...
}

Step 5 — Displaying the signature form

Create a form in a view file named signature.blade.php to allow users to upload their signature. Include the form in the signature-form route.

Step 6 — Saving the signature and generating the signed PDF

In SignatureController, define the saveSignature method to load the PDF image, position the signature image on it, and save the modified image as a new PDF file:

public function saveSignature(Request $request)
{
    $signature = $request->file('signature');
    $pageNumber = 1; // Change this to the page number where you want to add the signature.
    $pdfImagePath = public_path("page{$pageNumber}.jpg");
    $signatureImagePath = $signature->path();

    // Load the PDF image.
    $pdfImage = Image::make($pdfImagePath);

    // Load the signature image.
    $signatureImage = Image::make($signatureImagePath);

    // Position the signature image on the PDF image (adjust the x and y coordinates).
    $x = 100; // Adjust this value.
    $y = 100; // Adjust this value.
    $pdfImage->insert($signatureImage, 'top-left', $x, $y);

    // Save the modified PDF image as a new PDF file.
    $pdfImage->save(public_path("page{$pageNumber}.pdf"));

    return redirect()->route('signature.form')->with('success', 'Signature added successfully');
}

Step 7 — Displaying the result

Display the result to users after they’ve uploaded their signature and the PDF has been signed.

You’ve successfully implemented the process of adding a digital signature to a PDF using Laravel and the Intervention Image library. This technique enhances document security and verifies the authenticity of electronic documents, making it a valuable addition to your web application.

Nutrient as a better solution

Nutrient’s Digital Signatures SDK is designed specifically for working with PDF documents, offering robust capabilities for adding digital signatures to PDFs in a secure and compliant manner. Digital signatures enhance the security and authenticity of your PDF documents, making them suitable for various business and legal use cases. Now you’ll learn how to add digital signatures using Nutrient.

Setup

  1. Create a new Laravel project:

laravel new pspdfkit-app

Press enter to accept default options when prompted.

  1. Change to the created project directory:

cd pspdfkit-app

Adding Nutrient to your project

  1. Install Nutrient Web SDK using npm:

npm install pspdfkit
  1. Copy the Nutrient Web SDK distribution to the /public/assets/ directory in your project’s folder:

mkdir public/assets && cp -R ./node_modules/pspdfkit/dist/ ./public/assets/

Make sure your /public/assets/ directory contains the pspdfkit.js file and a pspdfkit-lib directory with the library assets.

Displaying a PDF

  1. Rename the PDF document you want to display in your application to document.pdf, and then add the PDF document to the public directory. You can use this demo document as an example.

  2. Navigate to the resources/views/welcome.blade.php file.

  3. Add an empty <div> element with a defined height to where Nutrient will be mounted:

<div id="pspdfkit" style="height: 100vh"></div>
  1. Include pspdfkit.js on the welcome.blade.php page:

<script src="assets/pspdfkit.js"></script>
  1. Initialize Nutrient Web SDK in Laravel by calling PSPDFKit.load():

<script>
	PSPDFKit.load({
		container: "#pspdfkit",
  		document: "document.pdf" // Add the path to your document here.
	})
	.then(function(instance) {
		console.log("PSPDFKit loaded", instance);
	})
	.catch(function(error) {
		console.error(error.message);
	});
</script>
  1. Run the following command to start the Laravel development server:

php artisan serve

Navigate to http://127.0.0.1:8000 to view the website.

Adding a digital signature to a PDF using Nutrient

Nutrient requires an X.509 certificate and a private key pair for adding a digital signature to a PDF document. Follow the steps below to do this.

Step 1 — Generating a self-signed certificate and private key

You can generate a self-signed certificate and private key using OpenSSL, which is a widely used tool for working with certificates and keys. Here’s how you can generate them:

  1. Open your terminal in the project directory.

  2. Run the following OpenSSL command to generate a self-signed certificate and private key:

openssl req -x509 -sha256 -nodes -newkey rsa:2048 -keyout private-key.pem -out cert.pem
  • -x509 — Tells OpenSSL to create a self-signed certificate.

  • -sha256 — Specifies the hash function to use for the certificate.

  • -nodes — Prevents encryption of the private key. You can remove this option for production keys if encryption is desired.

  • -newkey rsa:2048 — Generates a new RSA private key with a key size of 2,048 bits.

  • -keyout private-key.pem — Specifies the name of the private key file.

  • -out cert.pem — Specifies the name of the certificate file.

Follow the prompts to provide information for the certificate, such as the Common Name (CN), organization, and location. These details will be embedded in the certificate.

Step 2 — Verifying your certificate

After generating the certificate and private key, you can verify if the certificate is correctly PEM-encoded using the following command:

openssl pkcs7 -noout -text -print_certs -in cert.pem

This command will display certificate details and shouldn’t produce any errors. It confirms that “cert.pem” is a PEM-encoded X.509 certificate.

Alternatively, if you want to verify DER-encoded certificates, you can use the following command:

openssl pkcs7 -inform der -noout -text -print_certs -in cert.pem

This command checks if “cert.pem” is a DER-encoded PKCS#7 certificate.

With these steps, you’ll have generated the required X.509 certificate and private key pair for adding digital signatures to your PDF documents. Ensure that you store these files securely, as they’re essential for signing documents.

For more information on adding a digital signature to a PDF using Nutrient, refer to our digital signatures guide.

Signing a PDF document using Nutrient

To add a digital signature to your PDF document using Nutrient, follow the steps below.

Step 1 — Including the Forge library

To work with cryptography and digital signatures, you need to include the Forge library in your project. You can add it to your HTML file with the following script tag:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/forge.min.js"></script>

Step 2 — Generating a PKCS#7 signature

Nutrient utilizes the cryptographic Distinguished Encoding Rules (DER) PKCS#7 format for digital signatures. You’ll need to create a valid PKCS#7 signature containing your certificate and other relevant information.

Define a function, generatePKCS7, to generate the digital signature for your PDF. This function will perform the necessary cryptographic operations:

function generatePKCS7({ fileContents }) {
	const certificatePromise = fetch('cert.pem').then((response) =>
		response.text(),
	);
	const privateKeyPromise = fetch('private-key.pem').then((response) =>
		response.text(),
	);

	return new Promise((resolve, reject) => {
		Promise.all([certificatePromise, privateKeyPromise])
			.then(([certificatePem, privateKeyPem]) => {
				const certificate = forge.pki.certificateFromPem(
					certificatePem,
				);
				const privateKey = forge.pki.privateKeyFromPem(
					privateKeyPem,
				);

				const p7 = forge.pkcs7.createSignedData();
				p7.content = new forge.util.ByteBuffer(fileContents);
				p7.addCertificate(certificate);
				p7.addSigner({
					key: privateKey,
					certificate: certificate,
					digestAlgorithm: forge.pki.oids.sha256,
					authenticatedAttributes: [
						{
							type: forge.pki.oids.contentType,
							value: forge.pki.oids.data,
						},
						{
							type: forge.pki.oids.messageDigest,
						},
						{
							type: forge.pki.oids.signingTime,
							value: new Date(),
						},
					],
				});

				p7.sign({ detached: true });

				const result = stringToArrayBuffer(
					forge.asn1.toDer(p7.toAsn1()).getBytes(),
				);

				resolve(result);
			})
			.catch(reject);
	});
}

This function fetches your certificate and private key, and then it uses Forge to create a PKCS#7 signed data structure.

Step 3 — Converting a string to an array buffer

You’ll need a utility function, stringToArrayBuffer, to convert a binary string into an ArrayBuffer:

function stringToArrayBuffer(binaryString) {
	const buffer = new ArrayBuffer(binaryString.length);
	let bufferView = new Uint8Array(buffer);

	for (let i = 0, len = binaryString.length; i < len; i++) {
		bufferView[i] = binaryString.charCodeAt(i);
	}

	return buffer;
}

Step 4 — Initializing Nutrient and signing the document

Now, you can initialize Nutrient and invoke the PSPDFKit.Instance#signDocument method. This method takes two arguments.

  • Argument 1 — You can use this argument to fine-tune the signing process by providing essential data, such as certificates and private keys. If you don’t have specific signing requirements, you can pass null.

  • Argument 2 — In the second argument, supply fileContents. This parameter is used as a callback object containing an ArrayBuffer housing the document’s content. The method returns a promise that resolves to the ArrayBuffer or rejects if an error arises:

PSPDFKit.load({
    container: "#pspdfkit",
    document: "document.pdf" // Specify the path to your PDF document here.
})
.then(function(instance) {
    console.log("PSPDFKit loaded", instance);

    instance
        .signDocument(null, generatePKCS7)
        .then(() => {
            console.log('Document signed.');
        })
        .catch((error) => {
            console.error('The document could not be signed.', error);
        });
})
.catch((error) => {
		console.error(error.message);
});

This code initiates the signing process. Upon successful completion, you’ll see the message ‘Document signed.’ in the console. In case of errors during the signing process, an error message will be logged.

After successfully building and implementing this code, the signing process will run automatically, and the document will reload with the freshly added digital signature.

Information

We recently added support for CAdES-based signatures, which are advanced digital signatures. To learn more about them, check out our guide.

Conclusion

In this tutorial, you learned about two methods for adding digital signatures to PDFs in Laravel: Intervention Image for simpler needs, and Nutrient for advanced security and compliance.

Intervention Image is great for basic image manipulation and adding signatures to PDFs. However, it might not cover complex PDF structures and cryptography.

Nutrient, on the other hand, specializes in secure and compliant digital signatures within PDFs. It’s the choice for businesses that demand top-notch security, compliance, and professional document handling.

If you’re interested in exploring Nutrient further, you can request a free trial of our SDK or visit our demo page to experience the capabilities of our product firsthand.

FAQ

Here are a few frequently asked questions about digital signatures in PDFs.

How can I add a digital signature to a PDF using Laravel?

To add a digital signature to a PDF in Laravel, you can use libraries like Intervention Image for basic image overlay, or Nutrient for a more secure and compliant approach.

What are the best libraries for handling PDFs in Laravel?

Popular libraries for handling PDFs in Laravel include Intervention Image for image manipulation and Nutrient for advanced PDF features and digital signatures.

What is the role of a digital certificate in digital signatures?

A digital certificate verifies the identity of the signer and ensures the document’s integrity through cryptographic means.

How can I generate a self-signed certificate for digital signatures?

You can generate a self-signed certificate using OpenSSL with the command: openssl req -x509 -sha256 -nodes -newkey rsa:2048 -keyout private-key.pem -out cert.pem.

How do I set up Nutrient for signing a PDF document?

Install Nutrient using npm, include it in your project, and use the PSPDFKit.load() method to initialize it and sign the document with a PKCS#7 signature.

Author
Hulya Masharipov Technical Writer

Hulya is a frontend web developer and technical writer at Nutrient who enjoys creating responsive, scalable, and maintainable web experiences. She’s passionate about open source, web accessibility, cybersecurity privacy, and blockchain.

Related products
Share post
Free trial Ready to get started?
Free trial