Salta ai contenuti

Come sono collocate le firme in un PDF

Spec: ETSI EN 319 142-1 Spec: RFC 5652 Evidence: Standard-backed

Una firma PDF non avvolge il file. È incorporata al suo interno: un dizionario di firma la identifica e un digest viene calcolato su un intervallo di byte dichiarato, escludendo deliberatamente il valore della firma stessa. Questa pagina illustra tale meccanismo e, con pari importanza, ciò che non garantisce.

«Il documento è firmato» è una frase su cui si prendono decisioni. Può essere collegata a un pagamento, a un’approvazione, a un obbligo legale. Se non si sa con precisione quali byte copre una firma, non si può dire cosa dimostri davvero un risultato valido. Un PDF può contenere una firma perfettamente valida e mostrare comunque al lettore contenuti che il firmatario non ha mai visto, perché tali contenuti sono stati aggiunti dopo la firma, in un’area che la firma non ha mai dichiarato di coprire. Sapere dove inizia e dove finisce la portata della firma è la differenza tra una decisione difendibile e una basata sulla speranza.

  • Una firma PDF risiede in un dizionario di firma e in un campo di firma all’interno del documento, non in un involucro esterno.
  • I byte firmati sono dichiarati dall’array ByteRange: due segmenti (offset, length) che insieme coprono l’intero file tranne il valore esadecimale della firma contenuto nella voce Contents.
  • Il digest di questi due segmenti concatenati è ciò che la firma crittografica protegge effettivamente.
  • Qualunque elemento aggiunto in seguito in una nuova revisione è al di fuori dell’intervallo di byte originale. La firma originale resta valida; non ha mai fatto alcuna asserzione sui nuovi byte.
  • Una firma di approvazione e una firma di certificazione differiscono per ambito: la certificazione (DocMDP) vincola quali modifiche successive sono consentite; l’approvazione no.

NextPDF costruisce la firma secondo il formato, seguendo un ordine fisso, così che l’intervallo di byte sia esatto anziché approssimativo.

Quando il motore scrive una firma, riserva innanzitutto uno slot di dimensione fissa per il valore Contents e scrive un segnaposto ByteRange di larghezza fissa. Attende che il documento sia scritto per intero — tabella dei riferimenti incrociati e marcatore di fine file inclusi. Solo allora calcola i due offset reali, li riscrive nel segnaposto senza spostare alcun byte, esegue l’hash dei due segmenti e inserisce l’oggetto CMS risultante nello slot riservato. Il segnaposto viene riempito con zeri fino a una lunghezza costante, in modo che l’inserimento dei numeri reali non possa spostare gli stessi byte sottoposti a hash. Questo è l’unico ordine che produce una firma coerente con sé stessa. Il motore tratta qualsiasi errore in questa sequenza come un errore irreversibile, non come un ripiego silenzioso.

L’oggetto firma in sé, per il profilo PDF 2.0, è una struttura CMS SignedData distaccata (detached). Il dizionario PDF indica dove e come; l’oggetto CMS contiene il chi e la prova crittografica.

  1. Step 1 of 4: ISO 32000-2 §12.8.1 — ByteRange digest & signature dictionary
  2. Step 2 of 4: ISO 32000-2 §12.8.3.3 — ETSI.CAdES.detached SubFilter
  3. Step 3 of 4: ETSI EN 319 142-1 PAdES baseline profile
  4. Step 4 of 4: RFC 5652 CMS SignedData in Contents
Dove è definita una firma PDF, dal formato del contenitore fino all'oggetto crittografico: ISO 32000-2 specifica il dizionario e il meccanismo dell'intervallo di byte, ETSI EN 319 142-1 lo profila per PAdES e RFC 5652 definisce l'oggetto CMS SignedData collocato in Contents.

Evidence: Standard-backed Il meccanismo è definito da Spec: ISO 32000-2, §12.8.1 . Il digest viene calcolato sull’intervallo di byte indicato dalla voce ByteRange. Tale intervallo dovrebbe corrispondere all’intero file, incluso il dizionario di firma ma escluso il valore della firma — la voce Contents. ByteRange è un array di coppie di interi — offset iniziale e lunghezza. Gli intervalli non contigui sono usati appositamente affinché il digest possa omettere il valore della firma stessa.

Per il profilo PDF 2.0, Spec: ISO 32000-2, §12.8.3.3 specifica che, quando SubFilter è ETSI.CAdES.detached, il valore Contents è un oggetto CMS SignedData codificato in DER — la stessa struttura definita da Spec: RFC 5652 — e il profilo PAdES di tale oggetto è quello descritto da Spec: ETSI EN 319 142-1 .

L’ambito non è lo stesso per tutte le firme. Spec: ISO 32000-2, §12.7.4.5 definisce il permesso MDP: un valore di 0 rende la firma una firma di approvazione, mentre i valori 13 la rendono una firma di certificazione che vincola quali modifiche successive mantengono il documento conforme. Stesso meccanismo dell’intervallo di byte; promessa diversa sul futuro.

Il motore di NextPDF implementa esattamente questo: un segnaposto ByteRange di larghezza fissa, il digest dei due segmenti concatenati e un oggetto CMS distaccato in uno slot Contents riservato, finalizzato solo dopo che il file è completo.

Raramente si costruisce a mano un ByteRange. Lo scopo dell’esempio è mostrare la forma del risultato in modo che sia riconoscibile quando si ispeziona un file firmato.

<?php
declare(strict_types=1);
use NextPDF\Security\Signature\ByteRangeCalculator;
// Offsets the engine knows only after the whole PDF is written:
// $contentsStart — byte just before the '<' of the hex signature
// $contentsEnd — byte just after the '>' that closes it
// $fileLength — total file size in bytes
$range = ByteRangeCalculator::calculate(
contentsStart: $contentsStart,
contentsEnd: $contentsEnd,
fileLength: $fileLength,
);
// $range === [0, $contentsStart, $contentsEnd, $fileLength - $contentsEnd]
// Segment 1: file start → just before the signature value
// Segment 2: just after the signature value → end of file
// The signature value itself is the gap. It is never hashed.
$signedMessage = ByteRangeCalculator::extractSignedData($pdfBytes, $range);
// $signedMessage is segment 1 concatenated with segment 2 — exactly the
// bytes the cryptographic digest is computed over.

Lo spazio tra i due segmenti è il valore della firma. Non può essere incluso nel proprio digest, motivo per cui l’intervallo è composto da due parti e non da una.

La trappola sta nel credere che una firma valida significhi che l’intero file che si sta visualizzando sia ciò che è stato firmato. Non è così. Significa che i byte all’interno dell’intervallo dichiarato sono integri. Una revisione successiva può legittimamente aggiungere contenuti — una seconda firma, dati di modulo, materiale di convalida — al di fuori di tale intervallo. La prima firma resta valida e non dice nulla riguardo all’aggiunta. Un visualizzatore corretto indica che una firma copre «il documento così com’era al momento della firma», non «ogni byte sullo schermo». Trattare i due aspetti come la stessa cosa è il modo in cui un documento firmato acquisisce contenuti non firmati che sembrano firmati.

Questa pagina illustra la struttura, non la fiducia. Un ByteRange e un oggetto CMS formati correttamente indicano che i byte sono integri e quale chiave li ha firmati. Non indicano, da soli, se quella chiave appartenga al soggetto atteso, se il suo certificato fosse valido al momento della firma o se sia stato successivamente revocato. Questo è compito del percorso di certificazione e della revoca, trattato in Convalidare correttamente una firma. Questa pagina inoltre non tratta quando la firma è avvenuta con un’autorità indipendente. Un orario autodichiarato della firma non è un orario attendibile — vedere Marche temporali e orario attendibile. NextPDF costruisce la struttura qui descritta; i certificati, le ancore di fiducia e l’autorità di marcatura temporale sono forniti dalla distribuzione in uso, non dal motore.

Ciò che il motore fornisce, per ciascun livello, è la capacità di costruire la struttura:

PAdES signature structure (byte range, signature dictionary, detached CMS) — edition availability
Edition Availability
Core

PAdES B-B: il dizionario di firma, il ByteRange di larghezza fissa e l’oggetto CMS SignedData distaccato descritti in questa pagina.

Pro

Aggiunge PAdES B-T — una marca temporale attendibile applicata al valore della firma — sulla stessa struttura.

Enterprise

Aggiunge i profili a lungo termine (B-LT, B-LTA): materiale di convalida incorporato e marche temporali del documento stratificate sulla stessa base dell’intervallo di byte.

  • Dizionario di firma — il dizionario PDF che identifica il gestore della firma, il SubFilter, il ByteRange e il valore Contents.
  • ByteRange — un array di coppie di interi (offset, length) che dichiara i byte esatti coperti dal digest della firma.
  • Contents — la voce esadecimale che contiene il valore della firma (per PDF 2.0, un oggetto CMS SignedData distaccato); è esclusa dal proprio digest.
  • CMS SignedData — struttura Cryptographic Message Syntax (RFC 5652) che contiene il certificato del firmatario e i byte della firma.
  • PAdES — PDF Advanced Electronic Signatures: il profilo ETSI delle firme CMS per PDF, definito nella serie ETSI EN 319 142.
  • Firma di approvazione — una firma con permesso MDP 0; attesta il contenuto senza vincolare le modifiche successive.
  • Firma di certificazione — una firma con permesso DocMDP (MDP 13) che limita quali modifiche successive mantengono il documento conforme.