Firma di un PDF secondo la normativa? Proviamo a rispondere

21/02/2013

Introduzione
Dopo le perplessità sollevate dall'articolo[1] di Alessandro Premoli relativo all'erronea validazione delle firme digitali apposte ad un documento PDF da parte dei maggiori prodotti di verifica online, abbiamo ritenuto opportuno stendere un documento tecnico che spiegasse sia il problema, sia il modo più semplice per riprodurlo per fini di assessment e verifica delle proprie soluzioni.

L'anomalia in questione non è relativa alla firma digitale in sé, bensì a un mancato rispetto della normativa da parte degli applicativi di verifica: inserendo degli elementi firmati digitalmente all'interno di un documento PDF, il verificatore riconosce correttamente la validità della firma, senza però specificare che è relativa solo ad alcune sezioni e non all'intero documento, contravvenendo perciò alle norme vigenti per la verifica dei documenti firmati digitalmente.

Dettagli Tecnici

Vediamo, per sommi capi, com'è strutturato internamente un documento PDF.

All'inizio del file deve sempre comparire la sigla “%PDF” seguita dal numero di versione, mentre il documento deve terminare con la sigla “%%EOF”.

Il contenuto è presente sotto forma di object di varia natura ed ogni object può avere 0 o più attributi/campi.

Inoltre, usualmente è presente una tabella di riferimento denominata XREF, la quale ci esprime il numero di oggetti contenuti nel PDF e la loro posizione relativa all'inizio del file.

A titolo esemplificativo, mostriamo una semplice tabella XREF:

xref
0 271
0000000000 65535 f
0000000015 00000 n
0000000102 00000 n
...

La prima linea ci specifica quanti elementi contiene il PDF, mentre le altre linee riportano lo spiazzamento dell'oggetto a cui fanno riferimento partendo dall'inizio del file, il generation number (revisioni dell'oggetto) e un campo flag che ci chiarisce se l'oggetto è in uso (n) o libero (f).

La firma digitale di un documento PDF è anch'essa un oggetto descritto dalla tabella XREF, ed in particolare il valore principale del suo contenuto (la firma) può essere espresso  tramite la seguente espressione:

Signature Value = Sign(Hash(ByteRange))

L'espressione dice che il signature value è il risultato della firma digitale applicata ad uno hash (Sha256) dei dati presenti all'interno del range di byte specificato da ByteRange. Il campo ByteRange dell'oggetto “firma digitale”, è un array avente la seguente forma:

[inizio1, dimensione1, inizio2, dimensione2]

e descrive l'esatto range di byte da utilizzare per il calcolo del digest. Quindi per esempio, nella figura sottostante il range del quale verrà calcolato il digest inizierà all'offset 0 ed avrà dimensione 840, e riprenderà all'offset 960 con dimensione 240.

La firma viene poi scritta all'interno della struttura PDF in uno spazio di memoria escluso dal ByteRange, non contribuendo perciò al calcolo dell’hash in fase di verifica.

In particolare, relativamente all'esempio riportato nella figura il risultato può essere espresso tramite

Signature Value = Sign(Hash([0,840) U (960,1200]))

All'interno del campo Contents relativo all'oggetto contenente Signature Value troviamo anche altri valori, come il tipo di firma utilizzato o il timestamp. Per fare un esempio pratico, ecco l'intero campo Contents dell'esempio fornito:

<</Contents <signature value>/Type/Sig/SubFilter/adbe.pkcs7.detached/Name(PINELLI VITTORIO)/M(D:20130201143339+01'00')/ByteRange [47128 83656 130825 7 ]

Passiamo ora a descrivere come creare un PDF che sia in grado di ingannare gli strumenti di verifica.

L'idea alla base è quella di creare un nuovo documento PDF che contenga un oggetto che racchiuda l'intero documento originale (firmato ) senza che però venga visualizzato. La verifica della firma verrà poi eseguita solo su un sottoinsieme di questo nuovo PDF sfruttando il campo ByteRange, ovvero solo sul documento originale che è stato inserito, nonostante  il contenuto effettivamente visibile sia diverso rispetto a quello a cui si riferisce la firma.

Il documento originale, è un documento ufficiale firmato digitalmente dalla persona di cui si vuole assumere l'identità.

Per ricondurci alla vulnerabilità individuata da Premoli, prendiamo un documento originale firmato da Vittorio Pinelli e lavoriamo su quest'ultimo.

In particolare quello che è necessario che il nuovo documento PDF (fakePDF) contenga sono un oggetto contenente la firma digitale ed un oggetto contenente il documento originale.

Procedimento

Qui di seguito riportiamo il procedimento seguito per creare  fakePDF.

Dal PDF originale e correttamente firmato (origPDF) è necessario estrarre i dati specificati dal suo ByteRange [0 12345 67891  101112] ad esempio, il quale corrisponderà all'intero PDF originale eccettuato il Signature Value. Ricordiamo che Signature Value è tutto ciò che è al di fuori del ByteRange.

Le prossime operazioni saranno eseguite su fakePDF. Innanzitutto dobbiamo creare un nuovo PDF con uno strumento a piacere (es. LibreOffice) e nel quale verrà inserito il contenuto del testo che desideriamo “firmare” con la firma presa in prestito.

Per il nostro esempio il contenuto che abbiamo deciso di impostare è

 “Questo documento è fasullo, ma è firmato piuttosto bene...

A questo punto, una volta aperto fakePDF con un editor (esadecimale od equivalenti), dovremo effettuarvi le seguenti operazioni:

  • Inserire l'oggetto del documento originale che contiene il Signature Value
  • Aggiornare la tabella XREF inserendo il nuovo oggetto ed aggiornare di conseguenza gli offset che possono venire modificati dall'inserimento del nuovo oggetto
  • Aggiornare l'offset della XREF all'interno del campo startxref
  • Inserimento di un oggetto contenente i dati che erano del ByteRange del PDF originale
  • Simulare la divisione in due range dell'oggetto inserito al punto precedente tramite opportuni offset dal ByteRange del fakePDF (es. inserendo ~40 byte tra il primo ed il secondo range)

Passo 1 - Inserimento dell'oggetto del documento originale che contiene la Signature Value:

All'interno del fakePDF va inserito l'oggetto contenente la firma nel documento originale, come mostrato di seguito.

22 0 obj

<</Contents <Signature
Value>/Type/Sig/SubFilter/adbe.pkcs7.detached/Name(PINELLI VITTORIO)/M(D:20130201143339+01'00')/ByteRange [47128 83656 130825 7 ] /Filter/Adobe.PPKLite>>

endobj

L'indice del nuovo oggetto dovrà essere pari al numero di object già presenti nella xref table ed andrà aggiunto per ultimo, dopo gli altri object e prima della xref table.

Passo 2 - Aggiornare la tabella XREF inserendo il nuovo oggetto ed eventualmente aggiornando di conseguenza gli offset che possono venire modificati dall'inserimento del nuovo oggetto

Aggiornare la tabella XREF, incrementando di uno il numero di oggetti presenti (da 22 a 23 per esempio) ed inserire in fondo una nuova riga, contenente l'offset del nuovo oggetto.

xref
0 23
0000000000 65535 f
0000030605 00000 n
0000000019 00000 n
...
...
0000031245 00000 n
trailer
<</Size 23/Root 19 0 R
/Info 20 0 R
/ID [ <89E8ED9E3017E0A8AE4078B713A224A8>
<89E8ED9E3017E0A8AE4078B713A224A8> ]
/DocChecksum /E8D8BDD06011B6ECA671BCCAE33D1E11
>> 

Passo 3 - Aggiornare l'offset della XREF all'interno del campo startxref

Inserendo un nuovo oggetto prima della tabella XREF si va a modificare l'offset dell'XREF stesso, pertanto dobbiamo propagare questo scostamento nel valore del campo startxref posto di norma in fondo al documento.

startxref

46451

Passo 4 - Inserimento di un oggetto contenente i dati che erano del ByteRange del PDF originale

Dopo la tabella XREF di fakePDF inseriamo un nuovo oggetto con indice pari al numero di oggetti specificati nella size dell'XREF (nel nostro caso 23) senza aggiungerlo nella tabella XREF, in modo che non venga mostrato. L'oggetto riporterà come lunghezza la dimensione del file PDF originale e dovrà essere un oggetto di tipo stream.

23 0 obj
<</Length 100000>>
stream
%PDF-1.6 %âãÏÓ
2 0 obj
<</Length 6060/Subtype/XML/Type/Metadata>>stream
...
...
%%EOF
endstream
endobj

Il contenuto di questo stream è rappresentato dai dati contenuti nel ByteRange del documento originale.

Passo 5 - Simulare la divisione in due range dell'oggetto inserito al punto precedente tramite opportuni offset dal ByteRange del fakePDF (es. inserendo ~40 byte tra il primo ed il secondo range)

Per mantenere coerenza con la struttura del ByteRange dei documenti firmati digitalmente è necessario che anche il ByteRange del fakePDF sia suddiviso in due sezioni.

Per rispettare questa regola abbiamo suddiviso il payload (lo stream di dati inseriti al punto precedente) nella seguente maniera:

Il primo intervallo del ByteRange inizia dal primo byte dello stream inserito al punto precedente e termina prima del %%EOF prima della chiusura dello stream.

[Inizio Intervallo 1]
%PDF-1.6 %âãÏÓ
2 0 obj
<</Length 6060/Subtype/XML/Type/Metadata>>stream
...
...
endobj
startxref
100051
[Fine intervallo 1]

Dato che abbiamo escluso dal primo intervallo solo %%EOF, il secondo intervallo inizierà ~40 byte dopo e comprenderà %%EOF del fakePDF

[Inizio Intervallo 2]
%%EOF
[Fine Intervallo 2]
[Fine del file fakePDF]

Nel documento fornito come esempio, il secondo intervallo ha dimensione 7 per via di del CRLF dopo %%EOF

Sottoponendo il documento così generato ad una verifica, è possibile controllare se lo strumento utilizzato rispetta o meno le normative.

Normativa di Riferimento

Sulla base di quanto riportato nell'ART. 21 paragrafo 15 della delibera 45_2009 e successiva determinazione commissariale 69/2010, sono riconosciuti a livello nazionale o internazionale il formato di busta crittografica e di firma descritti nello standard ISO/IEC 32000 – Portable Document Format (PDF) sviluppati in conformità alle specifiche ETSI TS 102 778 - PAdES.

Lo standard PAdES, definito nell'ART 1  paragrafo 1 comma u  della delibera 45_2009 e successiva determinazione commissariale 69/2010 viene definito come formato di busta crittografica definito nella norma ETSI TS 102 778 basata a sua volta sullo standard ISO/IEC 32000 e successive modificazioni.

Secondo le specifiche ETSI TS 102 778 parte 2 paragrafo 5.1 l'oggetto PDF Signature deve soddisfare i seguenti requisiti:

  • L'oggetto PDF Signature deve essere specificata all'interno del documento ISO 32000-1 , clausola 12.8.
  • Le informazioni della firma devono essere inserite all'interno del documento stesso, e il ByteRange deve coprire l'intero file, includendo il dizionario della firma ma escludendo l'oggetto PDF Signature.
  • L'oggetto PDF Signature (oggetto binario codificato DER PKCS7) deve essere inserita all'interno del campo Contents del dizionario della firma.
  • L'oggetto PKCS7 deve essere conforme alle specifiche PKCS7 specificate nell'RFC 2315. Deve includere, come minimo, il certificato di firma X509 del firmatario.
  • La marca temporale e le informazioni di revoca dovrebbero essere incluse nell'oggetto PDF Signature.
  • Se presenti, le informazioni di revoca devono essere un attributo firmato dell'oggetto PDF Signature.
  • L'uso dei certificati con attributo, specificati in RFC 3281 associato con il certificato di firma non è raccomandato.
  • Deve esserci solo un singolo firmatario (una sola struttura SignerInfo) in un oggetto PDF Signature.

Secondo le specifiche ETSI TS 102 778 parte 2 paragrafo 5.2 i gestori per la verifica di conformità del documento devono soddisfare i seguenti requisiti:

  • Durante la verifica di una firma. il lettore PEF può sostituire un gestore per la verifica di conformità che non sia specificato in Filter fin tanto che supporta le specifiche del formato SubFilter
  • Devono essere utilizzati solo 2 valori per i SubFilter elencati nel ISO 3200-1 clausola 12.8.3.3.1 (iadbe.pkcs7.detached e adbe.pkcs7.sha1)

Secondo le specifiche ETSI TS 102 778 parte 2 paragrafo 5.3 la validazione della firma deve soddisfare i seguenti requisiti:

  • Verificare che il digest del documento sia identico a quello della firma, come specificato nella ISO 32000-1, clausola 12.8.1

Le normative di riferimento specificano inequivocabilmente come firmare e verificare un documento PDF, pertanto il problema riscontrato è puramente applicativo, difatti alcuni software potrebbero non accorgersi che la firma del documento è solo parziale.

Conclusioni

L’obiettivo di questa nota tecnica era principalmente quello di fornire dei suggerimenti affinché chi svolge attività di verifica possa adeguare il proprio software alle normative di riferimento.

Augurandoci di avere fornito un utile contributo, siamo disponibili per ulteriori approfondimenti ai seguenti indirizzi:

Alessandro Rinaldi - a.rinaldi@disbrain.com
Pierre Falda - p.falda@disbrain.com
Giuseppe Damiano – gdamiano@intesigroup.com

[1] http://www.premoli.net/forged-ds-pdf/

 

Milano, 21 febbraio 2013

Copyright © 2019 INTESI GROUP S.p.A
|
|
|
Via Torino, 48 - 20123 Milano - Italia
P.IVA 02780480964
|
Capitale soc.: Euro 600.000,00 i.v. - REA: Milano - 1562415
|