Integrity & Security Overview
This library provides optional local integrity checks via SHA-256 hashing. This guide explains what that means, what it does and doesn't provide, and how to use it.
What Is Integrity Checking?
Integrity checking verifies that a PDF, template, and signed field values have not been tampered with after creation.
How It Works
The Signer computes SHA-256 hashes of:
- Original PDF: Hash of the unmodified PDF file
- Template: Hash of the template JSON (canonicalized for consistency)
- Field Values: Hash of all filled field values
These hashes are stored in the manifest. Later, anyone can recompute the hashes and compare:
- If hashes match: PDF and template are authentic
- If hashes differ: Something was modified
Example Workflow
1. Designer creates template → Stores template hash
2. User signs PDF with template → Hashes computed, stored in manifest
3. Later, verify hashes match stored values → Authentic ✓What It Does NOT Provide
This library does not provide:
- PKI/Digital Certificates: No cryptographic proof of identity or signing authority
- Legal Admissibility: These signatures are not court-recognized or legally binding
- Server Attestation: No timestamp authority or external verification
- Tamper Detection on Signatures: The signature image itself can be modified in the PDF
For legally binding signatures or court-recognized proof, you must integrate with a server-side PKI solution.
When to Use Integrity Mode
Use Integrity Checking When:
- ✅ You want to detect accidental document tampering
- ✅ You need audit trails and basic verification
- ✅ You want users to verify "this PDF wasn't modified locally"
- ✅ You're signing sensitive internal documents (non-legal)
Don't Use Integrity Checking When:
- ❌ You need legally binding signatures
- ❌ You need to prove WHO signed (no identity verification)
- ❌ You need to prove WHEN it was signed (no timestamp)
- ❌ You need server-backed cryptographic proof
Usage
Enable Integrity Mode
<Signer
mode="integrity"
:expected-hashes="{
pdfHash: 'abc123...',
templateHash: 'def456...'
}"
@finalized="onSigned"
/>Hash Verification Modes
verification-mode="warn" // Show warning if hashes don't match (default)
verification-mode="strict" // Block finalization if hashes don't match
verification-mode="disabled" // Skip verification (for testing)Access Computed Hashes
<script setup>
function onHashComputed(hashes) {
console.log('PDF Hash:', hashes.pdfHash)
console.log('Template Hash:', hashes.templateHash)
console.log('Values Hash:', hashes.valuesHash)
// Store for later verification
localStorage.setItem('original-hashes', JSON.stringify(hashes))
}
</script>
<Signer
mode="integrity"
@integrity-check="onHashComputed"
/>Verification Workflow
First Signing: Capture Hashes
// User signs the first time
const manifest = payload.manifest // Contains hashes
const expectedHashes = {
pdfHash: manifest.pdfHash,
templateHash: manifest.template.hash,
valuesHash: manifest.valuesHash,
}
// Store these for comparison later
await saveToDatabase(expectedHashes)Later: Verify Hashes Match
// Load the same PDF and template
const signer = new Signer({
mode: 'integrity',
expectedHashes: expectedHashes,
verificationMode: 'strict',
})
// If hashes don't match, the user sees a warning
// In strict mode, they can't finalize without overridingExample: Complete Verification Flow
<template>
<div>
<div v-if="verificationResult" class="verification-banner">
<div v-if="verificationResult.summary === 'all-match'" class="success">
✓ All hashes match. Document is authentic.
</div>
<div v-else class="warning">
⚠️ Hash mismatch detected. Document may have been modified.
</div>
</div>
<Signer
mode="integrity"
:expected-hashes="expectedHashes"
verification-mode="strict"
@integrity-verification="verificationResult = $event"
@finalized="onSigned"
/>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { Signer } from '@sign-kit/core'
const verificationResult = ref(null)
const expectedHashes = ref({
pdfHash: 'abc123...',
templateHash: 'def456...',
valuesHash: 'xyz789...',
})
function onSigned(payload) {
console.log('Signed! New hashes:')
console.log(' PDF:', payload.manifest.pdfHash)
console.log(' Template:', payload.manifest.template.hash)
console.log(' Values:', payload.manifest.valuesHash)
}
</script>
<style scoped>
.verification-banner {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 4px;
}
.success {
background: #e6ffed;
color: #1a7f4a;
border: 1px solid #1a7f4a;
}
.warning {
background: #fff4e6;
color: #9a5a00;
border: 1px solid #9a5a00;
}
</style>Security Considerations
Limitations
- Hashes are not secret: Anyone with the manifest can see the hashes. They're for integrity, not confidentiality.
- Hash collisions are theoretically possible: SHA-256 is cryptographically secure, but no hash is 100% collision-proof.
- No replay protection: Nothing prevents someone from signing the same document twice.
- Signature image verification: The signature image itself can't be verified; only the template and values can.
Best Practices
- ✅ Store hashes in a tamper-proof location (signed audit log, blockchain, timestamp authority)
- ✅ Use integrity mode for sensitive internal workflows
- ✅ Combine with server-backed signatures for legal documents
- ✅ Include manifests in audit trails for accountability
- ✅ Display clear UI: tell users what integrity checking does and doesn't guarantee
Server Integration
For legally binding signatures, integrate with a server:
// After user signs locally, send to server for PKI signing
const manifest = payload.manifest
const response = await fetch('/api/sign-pki', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
pdfHash: manifest.pdfHash,
templateHash: manifest.template.hash,
signer: manifest.signer,
}),
})
const { signedPdfWithCert } = await response.json()
// Now you have a PKI-signed PDF with certificateSee Server Integration for detailed examples.
FAQ
Q: Can I verify a manifest without the original PDF?
A: No. You need the original PDF bytes to recompute the PDF hash. Store the original PDF alongside the manifest.
Q: What if someone modifies the signature image in the PDF?
A: The signature image is stored as a raster (PNG bytes) in the PDF. Modifying it won't change the valuesHash (which hashes field text values), but you'll notice the signature image changed visually. Combine with server attestation for stronger guarantees.
Q: Can I use this for e-signatures in regulated industries?
A: No. Regulatory frameworks (e.g., eIDAS, ESIGN Act) require PKI-backed signatures with timestamps and certificates. This library is for local workflows only. Integrate with a server PKI solution for compliance.
Q: How do I handle hash mismatches in production?
A: Log the mismatch, alert the user, and block finalization. Ask the user to reupload the original PDF or contact support.