Compute a SHA-256 hash of the signed PDF the moment it arrives, save that hash somewhere separate from the file, and compare it against a fresh hash whenever you need to show the file is unchanged. Here is the whole approach in Node.js using only built-in modules. Save it as an .mjs file so the top-level await works:
import { createHash } from 'node:crypto';
import { createReadStream } from 'node:fs';
// Stream the file so large PDFs never sit fully in memory.
async function sha256File(path) {
const hash = createHash('sha256');
for await (const chunk of createReadStream(path)) hash.update(chunk);
return hash.digest('hex');
}
// 1. When the signed PDF arrives, hash the exact bytes you received.
const path = './signed/contract-1042.pdf';
const record = {
documentId: 'contract-1042',
sha256: await sha256File(path),
receivedAt: new Date().toISOString(),
};
// Save `record` somewhere separate from the file itself.
// 2. Later, recompute and compare.
async function isUnchanged(path, storedHex) {
return (await sha256File(path)) === storedHex;
}
console.log(record.sha256);
console.log(await isUnchanged(path, record.sha256)); // trueWhy this works
A SHA-256 hash is a fixed-length fingerprint of a file's bytes. Change anything in the file, even appending a single space or updating the title in its metadata, and you get a different hash. If the hash you compute today matches the one you recorded on the day the document was signed, the file is byte-for-byte the same file you received.
Because SHA-256 is a standard algorithm, anyone can check the result without your code. An auditor or opposing counsel can run sha256sum contract-1042.pdf or shasum -a 256 contract-1042.pdf in a terminal and get the same 64-character hex string you stored.
Caveats
Hash the exact bytes you received. Do it in the webhook handler or download step, before anything else touches the file. If you flatten it, compress it, or add metadata first, you are fingerprinting a different document than the one that was signed.
Store the hash where it can't quietly change along with the file. If a buggy script or an attacker can rewrite both the PDF and its hash in the same bucket, a match proves nothing. Keep hashes in a separate database or an append-only log, and limit who can update them.
A hash proves your copy hasn't changed since you recorded it. On its own, it does not prove who signed or what they saw at signing time. For that, keep the e-signature provider's audit trail, and any digital certificate it embedded in the PDF, alongside the hash.
Back to All Questions