VerifyKitv0.9.0
All posts
|9 min read|TrexoLab
adobe readersignature validitypdf verificationdigital signatures

Why Adobe Says "The Document Has Been Altered or Corrupted Since It Was Signed"

You open a signed PDF and Adobe Acrobat Reader puts a red X on it: "The document has been altered or corrupted since it was signed." Nobody edited the file. It came straight from the signing service, or from a counterparty who insists they changed nothing. Yet Adobe is calling it invalid.

That message is accurate about the bytes and misleading about the meaning. Adobe is reporting one thing — the range of bytes this signature covers is not the range of bytes in front of me — and that single observation has at least four different causes. One of them is tampering. The other three are normal, and two of them should not lower your confidence in the document at all.

This post explains what the message actually detects, how to tell the four cases apart, and how to get that distinction programmatically instead of by squinting at a warning bar.

What the message actually checks

A PDF signature does not sign "the document." It signs a byte range — literally a list of offsets, stored in the signature dictionary as /ByteRange [0 840 960 12043]. That says: hash bytes 0–840 and bytes 960–13003, skipping the gap in the middle where the signature itself lives (a signature cannot contain its own hash).

At signing time the hash of those bytes is computed and sealed inside the CMS/PKCS#7 blob. At verification time the same bytes are hashed again and the two digests are compared. If they differ by a single bit, the signature is broken and there is no way to tell what changed — only that something did. That is the red X.

So the message means exactly one of two things:

  1. The bytes inside the signed range changed. This is a real integrity failure.
  2. The signed range does not extend to the end of the file. The bytes it covers are intact, but there are bytes beyond it that nobody signed.

Adobe often presents both as alarming. They are not remotely the same problem.

Why a signed PDF is allowed to grow

Here is the part that surprises people who have not worked with the format: a signed PDF is designed to be appended to.

PDF supports incremental updates. Instead of rewriting the file, a change is written as a new chunk at the end, with a new cross-reference table pointing at it. The original bytes are never touched. This is exactly why a PDF can hold more than one signature — the second signer appends their signature after the first one's byte range ends, and the first signature still verifies, because the bytes it covers are still there, unchanged.

That means "the signature does not cover the whole file" is the expected state for every signature in a multi-signature document except the last one. A three-party contract has two signatures that do not cover the whole file, by design. Treating that as corruption would flag every countersigned agreement in existence.

There is a second legitimate case. Long-term validation data — the certificates, CRLs and OCSP responses that make a signature verifiable years later — is written into a Document Security Store (DSS) appended after the signature. Adding LTV evidence to a signed document is a permitted post-signing update defined by the standard. The file grows; the signature is untouched; nothing is wrong.

And there is a third, which genuinely does deserve a flag: content was appended after the final signature, and it is not DSS data. The signed portion is intact, but somebody added something afterwards that nobody vouched for. That is worth telling a user about — as a warning, not as "corrupted."

The four cases behind one message

Put together, the situations Adobe collapses into "altered or corrupted" separate cleanly:

What happenedSigned bytes intact?Honest verdict
Bytes inside the signed range were modifiedNoInvalid — real tampering or corruption
LTV/DSS revocation data appended after signingYesValid — a permitted update
Unsigned content appended after the last signatureYesWarning — signed part fine, extra content unvouched
An earlier signature in a multi-signature documentYesValid — normal, by design

VerifyKit reports these as four different results rather than one. The integrityCheck on each signature carries both a status and a detail string explaining which case applies:

ts
import { createVerifier } from '@trexolab/verifykit-core'
 
const verifier = await createVerifier()
const result = await verifier.verify(buffer, 'contract.pdf')
 
for (const sig of result.signatures) {
  console.log(sig.name)
  console.log('  overall:  ', sig.overallStatus)          // 'valid' | 'invalid' | 'warning' | ...
  console.log('  integrity:', sig.integrityCheck.status)
  console.log('  detail:   ', sig.integrityCheck.detail)
  console.log('  covers whole file:', sig.byteRangeCoversWholeFile)
}
 
verifier.dispose()

The two fields to read together are integrityCheck.status and byteRangeCoversWholeFile:

  • status: 'invalid' — the hash comparison failed. The document was altered. This is the only case that means what the Adobe message says.
  • status: 'valid' with byteRangeCoversWholeFile: false — the signed content is intact and the file legitimately continued past it, either because later signatures were added or because DSS data was appended for long-term validation. The detail string says which.
  • status: 'warning' — the signed content is intact, but unsigned, non-DSS content follows the last signature. Show the signature as trustworthy and tell the user the document did not stop there.

Note that byteRangeCoversWholeFile: false on its own is not a problem. It is a fact about the file's structure, and it becomes interesting only in combination with which signature it belongs to and what those trailing bytes contain.

The certification-signature case

There is one more Adobe message that lands in the same territory, and it means something more specific:

"There have been changes made to this document that invalidate the certification signature."

A certification signature (DocMDP) is stronger than an ordinary approval signature: it declares up front what changes are permitted afterwards. VerifyKit exposes the declared level as sig.mdpPermission:

mdpPermissionPermitted after certification
1Nothing. No modifications at all.
2Form filling and additional signatures.
3Annotations, form filling, and signatures.
nullNot a certification signature — an ordinary approval signature with no restriction.

When a document is certified at P=1 and something is appended afterwards, the certification is broken by definition, even though the certified bytes are untouched and hash perfectly. This is not a hash failure — it is a policy failure, and it is the one case where "the file grew" really does invalidate a signature. VerifyKit applies that rule after all signatures have been checked individually, marking the certification signature invalid with the reason spelled out.

If you see the certification message and not the corruption message, do not go looking for tampering. Look at what was appended, and at whether the document should have been certified at P=2 or P=3 in the first place. A workflow that certifies at P=1 and then expects people to fill in a form field has a configuration bug, not a security incident.

What to do when you get the red X

First, establish which case you are in. Run the file through a verifier that reports the checks separately — the live demo does this in your browser and never uploads the file. If integrityCheck is valid and only the coverage is partial, the document is fine and the message was misleading.

If the hash genuinely mismatches, stop looking for a fix. There is no repair for a broken signature; the integrity check is doing precisely its job. Find where the file was modified in transit instead. In practice the usual culprits are not attackers:

  • A pipeline that "optimises," re-compresses, linearises or flattens PDFs after signing. Anything that rewrites the file rather than appending to it destroys every signature in it.
  • An email gateway or DLP scanner that rewrites attachments.
  • A download path that mangles bytes — a text-mode transfer, a proxy rewriting content, or truncation from a failed transfer.
  • A signing implementation that computes the ByteRange before writing the final file, so the offsets are wrong from the start.

The tell for the last one is that the file has never verified anywhere, in any reader. The tell for the others is that the signing service's own copy verifies and yours does not — get the original and compare sizes.

If you are building the software that shows this to users, do not repeat Adobe's flattening. A user who sees "altered or corrupted" on a perfectly good countersigned contract will call support, and support will not be able to tell them anything either, because the message did not preserve the distinction. Show the four cases as four states.

Altered is not the same as untrusted

It is worth being precise about which failure you have, because the two most common signature complaints have opposite meanings:

  • "Signature validity is unknown" — the cryptography is perfect; Adobe cannot establish who signed. A trust problem. See Why Adobe Says "Signature Validity Is Unknown".
  • "The document has been altered or corrupted" — the identity question was never even reached; the content does not match what was signed. An integrity problem.

Fixing a trust problem means adding a root certificate. Nothing you add to a trust store will fix an integrity problem, and nothing you do to the bytes will fix a trust problem. Diagnosing one as the other is the most common wasted afternoon in this area.

If all you have in front of you is Adobe's document bar and not a specific complaint, start at Adobe: "At Least One Signature Has Problems", which covers all eight checks that can put it there.

The takeaway

"The document has been altered or corrupted since it was signed" is a report about byte coverage, not a verdict on the document. Sometimes it means tampering. More often it means the file legitimately grew after signing — a second signature, or long-term validation data — which the PDF format explicitly allows and which a good verifier reports as valid.

The fix is not to suppress the warning; it is to compute the distinction and show it. If you need that in a browser or a Node.js pipeline, with the integrity check reported separately from the other seven, that is what VerifyKit is built for.

Try it on your own file in the live demo — verification runs entirely in your browser and the document never leaves your machine. For the underlying model, see Core Concepts, and for the field-by-field reference see the Core API.

Need PDF signature verification in production?

VerifyKit is a commercial SDK — try the live demo, then get in touch for a license.