Blog Post

How to Add Digital Signatures to PDFs Using React

Hulya Karakaya
Illustration: How to Add Digital Signatures to PDFs Using React

In this blog post, you’ll learn how to digitally sign PDF documents using React and PSPDFKit for Web. Digital signatures have become a must for modern document handling, and they’re typically used for legal and security purposes, especially in contexts where the authenticity of a document is critical, such as in legal contracts or official documents. Throughout this tutorial, you’ll navigate the intricacies of self-signed certificate generation and the application of digital signatures to your PDFs.

PSPDFKit React Digital Signature Library

PSPDFKit offers a range of essential capabilities for PDF signing. It simplifies the process of creating and validating digital signatures, accommodating various signature types, including hand-drawn, scanned, or typed signatures. The ability to store signatures locally or remotely enhances convenience, while automated workflows can be triggered based on signature actions. PSPDFKit allows for UI customization, ensuring a seamless user experience, and it supports client-side signing, eliminating the need for a dedicated server. Additionally, it can be extended to include forms, annotations, and other PDF-related functionalities, making it a versatile tool for various document tasks.

Signature Support

PSPDFKit offers two types of signatures: electronic signatures and digital signatures.

  1. Electronic signatures allow users to create signatures with ink drawings, bitmap images, or text. Our Electronic Signatures component provides a user-friendly interface, supporting draw, image, and type signature modes, and it enables signature storage for reuse.

  2. Digital signatures, on the other hand, are signed with certificates, offering reliable proof of a document’s origin and protection against unauthorized changes. While distinct, these signature types can be used together for a comprehensive signing solution.

PSPDFKit’s React PDF Library

We offer a commercial React.js PDF viewer library that can easily be integrated into your web application. It comes with 30+ features that let you view, annotate, edit, and sign documents directly in your browser. Out of the box, it has a polished and flexible user interface (UI) that you can extend or simplify based on your unique use case.

  • A prebuilt and polished UI for an improved user experience
  • 15+ prebuilt annotation tools to enable document collaboration
  • Support for more file types with client-side PDF, MS Office, and image viewing
  • Dedicated support from engineers to speed up integration

Requirements to Get Started

To get started, you’ll need:

  • The latest version of Node.js.

  • A package manager compatible with npm. This guide contains usage examples for Yarn and the npm client (installed with Node.js by default).

Creating a New React Project

  1. Create a new React app using the Create React App tool:

yarn create react-app pspdfkit-react-example
npx create-react-app pspdfkit-react-example
  1. Change to the created project directory:

cd pspdfkit-react-example

Adding PSPDFKit to Your Project

  1. Add the PSPDFKit dependency:

yarn add pspdfkit
npm install pspdfkit
  1. Copy the PSPDFKit for Web library assets to the public directory:

cp -R ./node_modules/pspdfkit/dist/pspdfkit-lib public/pspdfkit-lib

The above code will copy the pspdfkit-lib directory from within node_modules/ into the public/ directory to make it available to the SDK at runtime.

  1. Make sure your public directory contains a pspdfkit-lib directory with the PSPDFKit library assets.

Displaying a PDF

  1. Add the PDF document you want to display to the public directory. You can use our demo document as an example.

  2. Add a component wrapper for the PSPDFKit library and save it as components/PdfViewerComponent.js:

import { useEffect, useRef } from 'react';

export default function PdfViewerComponent(props) {
	const containerRef = useRef(null);

	useEffect(() => {
		const container = containerRef.current;
		let instance, PSPDFKit;
		(async function () {
			PSPDFKit = await import('pspdfkit');

			PSPDFKit.unload(container); // Ensure that there's only one PSPDFKit instance.

			instance = await PSPDFKit.load({
				// Container where PSPDFKit should be mounted.
				container,
				// The document to open.
				document: props.document,
				// Use the public directory URL as a base URL. PSPDFKit will download its library assets from here.
				baseUrl: `${window.location.protocol}//${window.location.host}/${process.env.PUBLIC_URL}`,
			});
		})();

		return () => PSPDFKit && PSPDFKit.unload(container);
	}, []);

	return (
		<div
			ref={containerRef}
			style={{ width: '100%', height: '100vh' }}
		/>
	);
}
  1. Include the newly created component in App.js:

// src/App.js

import PdfViewerComponent from './components/PdfViewerComponent';

function App() {
	return (
		<div className="App">
			<div className="PDF-viewer">
				<PdfViewerComponent document={'document.pdf'} />
			</div>
		</div>
	);
}

export default App;
  1. Your project structure should now look like this:

pspdfkit-react-example
├── public
│   ├── pspdfkit-lib
│   └── document.pdf
├── src
│   ├── components
│   |   └── PdfViewerComponent.js
|   └── App.js
├── package.json
└── yarn.lock
  1. Start the app and run it in your default browser:

yarn start
npm start

Adding a Digital Signature to a PDF Using PSPDFKit

PSPDFKit requires an X.509 certificate and a private key pair for adding a digital signature to a PDF document. To do this, follow the steps in the next section.

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 do this:

  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 PSPDFKit, refer to our digital signatures guide.

Signing a PDF Document Using PSPDFKit

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

Step 1: Installing the Forge Library

  1. Install the Forge library using npm. Open your terminal, navigate to the project directory, and run the following command:

npm install node-forge
  1. Import the Forge library in your PdfViewerComponent file:

import forge from 'node-forge';

Step 2: Generating the PKCS#7 Signature

PSPDFKit 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:

import { useCallback } from 'react';

export default function PdfViewerComponent(props) {
	// Other code...
	const generatePKCS7 = useCallback(({ 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 PSPDFKit and Signing the Document

Now you can initialize PSPDFKit 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, you should supply fileContents. This parameter is used as a callback object containing an ArrayBuffer housing a document’s content. The method returns a promise that resolves to the ArrayBuffer, or it rejects if an error arises.

useEffect(() => {
	const container = containerRef.current;
	let instance, PSPDFKit;
	(async function () {
		try {
			const PSPDFKit = await import('pspdfkit');

			PSPDFKit.unload(container); // Ensure that there's only one PSPDFKit instance.

			const instance = await PSPDFKit.load({
				// Container where PSPDFKit should be mounted.
				container,
				// The document to open.
				document: props.document,
				// Use the public directory URL as a base URL. PSPDFKit will download its library assets from here.
				baseUrl: `${window.location.protocol}//${window.location.host}/${process.env.PUBLIC_URL}`,
			});

			await instance.signDocument(null, generatePKCS7);
			console.log('document signed.');
		} catch (error) {
			console.error('The document could not be signed.', error);
		}
	})();

	return () => PSPDFKit && PSPDFKit.unload(container);
}, [generatePKCS7, props.document]);

This code initiates the signing process. Upon successful completion, you’ll see the message Document signed. in the console. If there were 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.

Here’s the complete code for the PdfViewerComponent:

import { useEffect, useRef, useCallback } from 'react';
import forge from 'node-forge';

export default function PdfViewerComponent(props) {
	const containerRef = useRef(null);

	const generatePKCS7 = useCallback(({ 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);
		});
	}, []);

	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;
	}

	useEffect(() => {
		const container = containerRef.current;
		let instance, PSPDFKit;
		(async function () {
			try {
				const PSPDFKit = await import('pspdfkit');

				PSPDFKit.unload(container); // Ensure that there's only one PSPDFKit instance.

				const instance = await PSPDFKit.load({
					// Container where PSPDFKit should be mounted.
					container,
					// The document to open.
					document: props.document,
					// Use the public directory URL as a base URL. PSPDFKit will download its library assets from here.
					baseUrl: `${window.location.protocol}//${window.location.host}/${process.env.PUBLIC_URL}`,
				});

				await instance.signDocument(null, generatePKCS7);
				console.log('document signed.');
			} catch (error) {
				console.error('The document could not be signed.', error);
			}
		})();

		return () => PSPDFKit && PSPDFKit.unload(container);
	}, [generatePKCS7, props.document]);

	return (
		<div
			ref={containerRef}
			style={{ width: '100%', height: '100vh' }}
		/>
	);
}
Information

We recently added support for CAdES signatures, which are advanced digital signatures. To learn more about CAdES signatures, refer to our digital signatures guide.

Conclusion

In this blog post, you learned how to add digital signatures to PDF documents using React and PSPDFKit for Web. If you’re interested in exploring PSPDFKit further, you can request a free trial of our SDK or visit our demo page to experience the capabilities of our product firsthand.

Author
Hulya Karakaya Technical Writer

Related Products
Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
DESIGN  |  Baseline UI • Web

Part II — Why We Built Our Own Design System: Introducing Baseline UI

TUTORIALS  |  Web • Angular • How To

How to Use jsPDF and Angular to Generate PDFs

DESIGN  |  Baseline UI • Web

Part I — Design Systems: Building Blocks for Seamless User Experiences