p7m file

p7m digital signature: how to extract the content

Posted on Posted in Digital signature, System administrator

Digital signatures and files with extension p7m

You will have to open a file with the p7m extension, that is, those coming from the PEC and signed digitally.
Well! It happened to me a few days ago.

Having no software running the file directly I lost some time to extract both the content and the signatures and verify the correctness.

So not to waste more time with you (but also with me when I recap) I think an article is needed.

What is p7m format?

The p7m file is a CAdES (CMS Advanced Electronic Signatures) digital signature file that is an extension of the Cryptographic Message Syntax (CMS) which is based on PKCS#7.
So the p7m file is nothing more than a container that encapsulates both the original (non-encrypted) document and the personal digital signature and the CA chain that issued the certificate.
Going for order in the p7m file we find:
– In the first bytes the header PKCS#7
– then comes our original document (not encrypted)
– the signature hash (which validates by law the content)
– the personal digital certificate of those who signed it
– the CA certificate

Let’s consider the case where the signed document is a pdf (document.pdf.p7m).

The easiest way to view pdf encapsulated in document.pdf.p7m is to open it directly with a pdf editor like acroread o okular o evince since the PKCS#7 header should be a few bytes so if we rename the pdf extension file almost definitely, standard readers will be able to view the file directly without having to extract the content (maybe with some error message during opening).
To do the most accurate things we can try to edit the .p7m file with “vim” and directly delete the first bytes (50-100) up to
Per far le cose più precise possiamo provare ad editare il file .p7m con “vim” ed eliminare direttamente i primi byte (50 – 100) sino a %PDF-1 (Naturally leaving the %PDF-1 string as the .pdf file startup).
Since we are, we try to extract it correctly by also deleting the later bytes in pdf, that is, the %%EOF string.
At this point, the pdf document will be extracted correctly.

To extract it with a single command, without editing it, we can use the command

perl -ne 's/^.*%PDF/%PDF/; print if /%PDF/../%%EOF/' document.pdf.p7m >document.pdf

or we use the openssl command with the smime parameter for signature verificationo (-verify)

openssl smime -verify -noverify -in document.pdf.p7m -inform DER -out document.pdf

Extract the certificate

To extract the personal digital certificate of those who signed it just use openssl with the command

openssl pkcs7 -inform DER -in document.pdf.p7m -print_certs -out cert.pem

The certificate is in the cert.pem file and if you want to display text just use the x509 certificate command

openssl x509 -in cert.pem -text -noout

Checking the signature of the entire document

To check the signature, you must use the command

openssl smime -in document.pdf.p7m -inform DER -verify -out document.pdf

while to verify the validity of the simple certificate the command is

openssl verify cert.pem

Of course, trying it as it is, verification fails (“unable to load certificate“) because we do not have CA trust certificates.
If you try to verify the signature of a p7m file without the Certification Authority (la CA), the verification fails (“unable to load certificate“) because we do not have CA trust certificates (so-called Trusts).
These Certificate Authorities have been defined by Italian law and are registered on the CNIPA, which since December 2009 has become DigitPA, as a certificate of XML certificates and found them on the same site at https://applicazioni.cnipa.gov.it/TSL/_IT_TSL_signed.xml.
To have openssl manage them you have to put them in its format so:

wget -O - https://applicazioni.cnipa.gov.it/TSL/IT_TSL_signed.xml | perl -ne 'if (/<X509Certificate>/) {
s/^\s+//; s/\s+$//;
s/<\/*X509Certificate>//g;
print "-----BEGIN CERTIFICATE-----\n";
while (length($_)>64) {
print substr($_,0,64)."\n";
$_=substr($_,64);
}
print $_."\n";
print "-----END CERTIFICATE-----\n";
}' >CA.pem

This way we have all the certificates in a single CA.pem file, unfortunately even those that may have expired. Even if it’s over, we may need to check out an old file so we can also serve the expiration.

For script updates, please visit https://github.com/eniocarboni/getTrustCAP7m

Now we can check the certificate with a positive result with:

openssl verify -CAfile CA.pem cert.pem

and the file p7m with the command:

openssl smime -in documento.pdf.p7m -inform DER -verify -CAfile CA.pem -out documento.pdf

Of course, this last command, as well as checking the validity of the signature, also extracts the original document.

External links

CAdES see

CMS see

PKCS#7 see

22 thoughts on “p7m digital signature: how to extract the content

    1. Ciao Antonio,
      direi che per creare un p7m dato il file pdf “documento.pdf” basta utilizzare il comando “smime”

      openssl smime -sign -outform DER -binary -md sha256 -in documento.pdf -out documento.pdf.p7m -signer your_priv_certs.crt.pem -inkey your_priv_certs.key.pem -noverify -nodetach
      

      Per l’ultima versione smime, spero anche compatibile con il CNIPA, credo che bisogna utilizzare “cms” al posto di “smime”:

      openssl cms -sign -outform DER -binary -md sha256 -in documento.pdf -out documento.pdf.p7m -signer your_priv_certs.crt.pem -inkey your_priv_certs.key.pem -noverify -nodetach
      

      Perché sia valido a livello legale dovresti controllare il file con qualche tool presente online e fare tutti i controlli possibili.

  1. Io invece ho una cartella zeppa di file in formato .p7m ed ho notato che la ricerca di una stringa di testo di windows non funziona; c’è qualcuno che può suggerire una soluzione?

    1. Ciao Andrea,
      non so bene come funzioni in windows ma non credo che riesca ad indicizzare direttamente i documenti presenti in file .p7m.
      Sotto Linux basta utilizzare un semplice script come questo:

      #! /bin/bash
      
      stringa_da_trovare=$1;
      
      for file in *.p7m; do
        trovato=$(openssl smime -verify -noverify -in "$file" -inform DER  2>/dev/null | pdftotext - - | grep  "$stringa_da_trovare")
        if [ $? == 0 ]; then
          IFS="\n"
          t=$(echo $trovato | sed 's/^/\t/g')
          echo -e "trovato in $file:\n$t"
          IFS=''
        fi
      done

      Supponiamo di salvarlo nel file find_in_p7m.sh nella stessa cartella dove sono i .p7m e supponendo sempre che i .p7m contengano pdf (per gli xml o altro bastano poche modifiche).
      Lanciamo il comando per rendere eseguibile lo script:

      chmod +x find_in_p7m.sh

      e poi supponiamo di dover cercare la stringa “fattura” basta lanciare il comando:

      ./find_in_p7m.sh "fattura"

      che restituisce sia i file .p7m trovati che il contenuto trovato.
      Credo che con powershell dovresti riuscire a far la stessa cosa sotto windows magari scaricandoti pdftotext da http://www.foolabs.com/xpdf/download.html e naturalmente avendo a disposizione openssl (http://gnuwin32.sourceforge.net/packages/openssl.htm)

  2. Buona sera, se volessi estrarre il contenuto pdf da un p7m in php come potrei fare? sul mio host il servizio exec di php non è abilitato e devo usare solo funzioni di php
    Grazie

    1. Ciao Fabio,
      purtroppo credo che in php la libreria OpenSSL (http://php.net/manual/en/ref.openssl.php) non permetta di usare “smime” per estrarre documenti nel formato CAdES (ma forse mi sbaglio e qualcuno mi correggerà).
      Potresti, comunque, provare ad estrarlo direttamente dal file .p7m come l’ho descritto nell’articolo ovvero aprendo il file, saltando tutti i caratteri sino a (ma escluso) %PDF-1 e poi predi tutto sino a %%EOF ed il resto lo tralasci.
      Non dovrebbe esser un operazione difficile (direi poche righe di codice).

      1. Grazie della risposta. Ho provato anche quello ma, soprattutto per i pdf/a, l’estrazione della stringa così come descritto non produce un pdf valido

        1. Ciao Fabio,
          è molto strano che per i pdf/a non funzioni dato che il pdf/a non è altro che un sottoinsieme del pdf dove vengono eliminate tutte le funzioni che non permettono l’archiviazione a lungo termine (ad esempio la crittografia con password del testo).
          Se hai un .pdf.p7m non personale che è possibile visionare prova a postare qui il link per poterlo verificare.

  3. Io ho provato a cancellare le righe che stanno prima di %PDF-1 e tutto ciò che sta dopo %%EOF ma il PDF che ottengo è vuoto. Ho provato anche a lanciare la riga di comando:

    perl -ne 's/^.*%PDF/%PDF/; print if /%PDF/../%%EOF/' documento.pdf.p7m >documento.pdf

    E ottengo comunque un PDF con una pagina sola e completamente bianca

    1. Ciao Roberto,
      Forse nel tuo pdf c’è un’altra stringa %%EOF, magari come commento. In tal caso prova con:

      perl -ne ‘s/^.*%PDF/%PDF/; print if /%PDF/../^%%EOF/’ documento.pdf.p7m >documento.pdf

      dove in tal caso considero che %%EOF deve stare ad inizio riga.
      Inoltre se il file pdf ha delle “modifiche incrementali” (incremental update) o revisioni queste vengono messe in coda aggiungendo i vari pezzi e terminando con un ulteriore %%EOF (ad esempio se il pdf firmato viene modificato e poi rifirmato nelle modifiche).
      In tal caso devi lasciare tutti i %%EOF esistenti sino all’ultimo ed il comando “perl” non funzionerà mentre “openssl” dovrebbe andar sempre bene.
      Fammi sapere se sei in uno di questi casi e se sei riuscito a risolvere.

      1. Ciao Enio,
        grazie innanzitutto della risposta. No anche con quella stringa non riesco, ottengo lo stesso PDF vuoto.

        Alla fine ho usato l’altro tuo suggerimento (openssl) ma ora volevo chiederti una cosa in più se la sai. Sarebbe possibile tramite Java far la stessa cosa usando (immagino) la libreria openssl? Ne sai qualcosa? In questa maniera potrei fare io un .jar al volo che funzionerebbe anche su Windows.

        Grazie e ciao.

  4. Caio Enio,
    bellissimo articolo! Mi hai aiutato a risolvere diverse grane (e a non usare Windows…)
    In questi giorni ho scoperto che mentre alla pubblica amministrazione piacciono molto i file .P7M, spesso all’estero usano firmare i documenti tenendo separati file e firma (che ho scoperto si possono ottenere senza l’opzione -nodetach, quindi generando una “clear signature”).
    Sai se esiste un modo, usando OpenSSL, per fare una sorta di “merging” tra il file e la sua firma (che come standard ha estensione .P7B) ottenendo un file .P7M?
    Complimenti ancora! Ciao.

  5. Ciao Enio,
    di recentissimo mi sono imbattuto nei problemi di firma digitale… e quindi in questo “fantastico” meccanismo messo in piedi dalla nostra PA.
    Sai dov’è possibile trovare la specifica del file P7M? Spero l’abbiano fatta open…
    Poi mi frulla un’altra idea, fatto 30 facciamo 31: chiaro il meccanismo con cui leggo e produco il file P7M con openSSL… ma come recupero la mia chiave privata imprigionata nella smart card che mi hanno dato? Sai se openSSL possa interfacciarsi ai lettori, oppure se c’è qualche tool che possa interaggire?

    Grazie mille e ottimo lavoro!
    Albe

    1. Da quello che ho capito io: usando il driver apposito per la tua smartcard si può estrarre il certificato (io ci sono riuscito) però poi non è possibile usare openssl per generare una CADES a meno di non usare una versione modificata con una patch che, al momento, non è stata accettata nel programma originale. I dettagli li trovi qui: http://www.blia.it/firmadigitale/index.php

  6. Io vorrei conoscere una applicazione che mi apre in serie, ovvero mi estrae immediatamente con un solo right-click da piu file selezionati, il file non firmato.
    Oltre al DIKE che ne apre uno solo ed uno alla volta, ognuno in una finestra separata, non esiste altro ?

    Grazie

  7. è possibile generalizzare gli scripts di estrazione in modo tale che agiscano in batch su più files?

    perl -ne ‘s/^.*%PDF/%PDF/; print if /%PDF/../%%EOF/’ documento.pdf.p7m >documento.pdf
    openssl smime -verify -noverify -in documento.pdf.p7m -inform DER -out documento.pdf

    grazie per l’ottimo articolo

    1. Grazie per i complimenti.
      Per elaborare in batch diversi file .pdf.p7m basta metterli in una directory e lanciare da lì la seguente riga di codice bash:

      for file in *.pdf.p7m; do pdfname=$(basename "$file" .p7m); openssl smime -verify -noverify -in "$file" -inform DER -out "$pdfname"; done 
      1. grazie per la cortese risposta
        anche io sono riuscito a mettere insieme qualcosa del genere (sintassi leggermente differente e meno compatta)

        ############
        for file in *.p7m
        do
        filename=”${file##*/}”
        filename_new=”${filename%.*}”
        openssl smime -verify -noverify -in “$filename” -inform DER -out “$filename_new”
        done

        ##########

Leave a Reply

Your email address will not be published. Required fields are marked *