Fatture firmate DER ed openssl

Salve, ricevo alcune fatture in PKCS#7 firmato in formato DER. Sono firmate da Namirial. Non riesco ad estrarre l’xml tramite openssl in nessun modo.
Qualcuno ha un suggerimento?

usando

openssl asn1parse -inform DER -in [fattura].xml.p7m

0:d=0 hl=2 l=inf cons: SEQUENCE 2:d=1 hl=2 l= 9 prim: OBJECT :pkcs7-signedData 13:d=1 hl=2 l=inf cons: cont [ 0 ] 15:d=2 hl=2 l=inf cons: SEQUENCE 17:d=3 hl=2 l= 1 prim: INTEGER :03 20:d=3 hl=2 l= 13 cons: SET 22:d=4 hl=2 l= 11 cons: SEQUENCE 24:d=5 hl=2 l= 9 prim: OBJECT :sha256 35:d=3 hl=2 l=inf cons: SEQUENCE 37:d=4 hl=2 l= 9 prim: OBJECT :pkcs7-data 48:d=4 hl=2 l=inf cons: cont [ 0 ] 50:d=5 hl=2 l=inf cons: OCTET STRING 52:d=6 hl=4 l=8192 prim: OCTET STRING :<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

visualizza il contenuto ma come estrarlo?
Fino a febbraio di quest’anno non avevo mai avuto problemi, ora di tanto in tanto mi si presentano fatture che non riesco ad estrarre direttamente tramite openssl.

Facendo una brevissima ricerca su internet ho trovato questo link:

Istruzioni

All’interno dello stesso documento sono riportate le seguenti istruzioni:

10.3 Il comando pkcs7

Nome

pkcs7 - utility PKCS#7.

Sintassi

openssl pkcs7 [-inform PEM | DER] [-outform PEM | DER] [-in filename] [-out filename] [-print_certs] [-text] [-noout]

Descrizione

Il comando pkcs7 processa file PKCS#7 in formato DER o PEM

Opzioni del Comando

  • -inform PEM | DER: Specifica il formato di input. Il formato DER è una struttura DER codificata PKCS#7. Il formato PEM (che è quello di default) è una versione codificata base64 del formato PEM con linee di intestazione e chiusura.

  • -outform PEM | DER: Specifica il formato di output. Le opzioni hanno lo stesso significato di quelle dell’opzione -inform.

  • -in filename: Specifica il nome del file di input da cui leggere o lo standard input se questa opzione non è specificata.

  • -out filename: Specifica il nome del file di output su cui scrivere o lo standard output per default.

  • -print_certs: Stampa ogni certificato o CRL contenuto nel file. Essi sono preceduti dal loro soggetto e dal nome dell’emittente in formato ad una linea.

  • -text: Stampa i dettagli completi dei certificati piuttosto che solo il soggetto ed il nome dell’emittente.

  • -noout: Non fornisce in output la versione della struttura PKCS#7 (o i certificati se –print_certs è attivato).

Esempi

  1. Converte un file PKCS#7 da formato PEM a DER:

openssl pkcs7 -in file.pem -outform DER -out fil.der

  1. Stampa tutti i certificati in un file:

openssl pkcs7 -in file.pem -print_certs -out certs.pem

Spero possa essere d’aiuto.

Il comando giusto dovrebbe essere “openssl cms”.

Prova a leggere qui:

E qui:

Grazie, provato in tutte le salse. Genera ugualmente errore. Premetto che nel tempo ho implementato 5 tipologie differenti di conversione proprio per gestire casistiche diverse. Qui si tratta di problematiche legate all’integrità del file.
Ad esempio i tag xml vengono spezzati con caratteri speciali windows e mandati a capo. Possibilità di trovarsi ad esempio <alle \ngati>. Con windows non accade nulla ma su sistemi linux non si riesce a gestire. Ritengo che lo SDI dovrebbe bocciare questa procedura in quanto non risulta essere standard su tutti i sistemi.
In ogni caso, al momento ho risolto forzando la lettura manuale e ripulendo il file.
E’ affidabile? No! E’ standard? No! Purtroppo non ho alternative, confido che lo SDI dia una stretta e non accetti più immondizia… ma non ci spero troppo.

Grazie ha funzionato su 1 file su 5 ricevuti. Ho una possibilità in meno di generare un errore.

I tag spezzati sono indice del fatto che non stai decodificando correttamente il file. I “caratteri speciali” di cui parli in realtà fanno parte della struttura del formato ASN.1 (che sta alla base di tutto). Il file XML può essere spezzettato in più blocchi, ciascuno dei quali ha un suo header, e i header dei blocchi sono un paio di byte che tu vedi “in mezzo” al file XML.
Se usi openssl asn1parse per vedere la struttura, noterai che il file XML è spezzato in più blocchi “OCTET STRING”.

Personalmente non ho mai trovato una fattura che non venisse decodificata con il comando:
openssl cms -inform DER -verify -noverify -in fattura.xml.p7m -out fattura.xml
ma ammetto che, a parte test fatti anni fa, non lo uso. Dato che non facciamo la verifica della firma elettronica (la fa già il SdI), usiamo direttamente un parser ASN.1 per estrarre il file XML dal file p7m.

Però ho provato ora con una fattura recente firmata da Namirial e non mi ha dato errori.

Un’altra cosa che potrebbe darti problemi è che alle volte il file .p7m non è un file binario, ma è codificato in base64 (oltre alla codifica base64 già prevista dal protocollo SOAP). In questo caso devi prima decodificarlo da base64 e poi usare il comando openssl.

Per estrarre il file .XML dal file .p7m io utilizzo la seguente istruzione, da riga di comando:

“C.\TuaDirectory\openssl cms -verify -noverify -in C:\TuaDirectory\Fattura.p7m -inform DER -out c:\TuaDirectory\Fattura.xml”, 1

Questa riga di comando sfrutta “cms” al posto di “snime”.

Per completezza riporto anche la riga di comando che sfrutta “snime”:

“C.\TuaDirectory\openssl smime -decrypt -in C:\TuaDirectory\Fattura.p7m -inform DER -verify -noverify -out c:\TuaDirectory\Fattura.xml”, 1

comandi = [
            f"openssl smime -verify -noverify -in '{nome_file_p7m}' -inform PEM -out '{nome_file}'",
            f"openssl smime -verify -noverify -in '{nome_file_p7m}' -inform DER -out '{nome_file}'",
            f"openssl cms -verify -noverify -inform DER -in '{nome_file_p7m}' -out '{nome_file}'",
            f"openssl cms -verify -noverify -inform DER -in '{nome_file_p7m}' -no_attr_verify -out '{nome_file}'"
        ]

Uso questi comandi in una funzione in caso di errore passa alla successiva… ma non riesco. Secondo me è linux… o meglio è creato da windows ma non riesco ad estrarlo in linux. Anche con una chiavetta non riesco a leggerne il contenuto. In windows riesco.

Non va bene, secondo me. Nel caso esporrò a SDI la questione

Ho usato i comandi,
Error reading S/MIME message
40A71DA4C0770000:error:068000A8:asn1 encoding routines:asn1_check_tlen:wrong tag:…/crypto/asn1/tasn_dec.c:1188:
40A71DA4C0770000:error:0688010A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error:…/crypto/asn1/tasn_dec.c:349:Type=PKCS7_ISSUER_AND_SERIAL
40A71DA4C0770000:error:0688010A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:…/crypto/asn1/tasn_dec.c:685:Field=issuer_and_serial, Type=PKCS7_SIGNER_INFO
40A71DA4C0770000:error:0688010A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:…/crypto/asn1/tasn_dec.c:654:Field=signer_info, Type=PKCS7_SIGNED
40A71DA4C0770000:error:0688010A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:…/crypto/asn1/tasn_dec.c:685:
40A71DA4C0770000:error:0688010A:asn1 encoding routines:asn1_template_ex_d2i:nested asn1 error:…/crypto/asn1/tasn_dec.c:537:Field=d.sign, Type=PKCS7

Hai controllato se per caso il file p7m è codificato in base64?

Si, è la prima verifica che faccio ed in realtà anche lì ho riscontrato problemi. Nel senso che se uso openssl per decodificare il b64 entra in errore (alcune volte), perchè la stringa si presenta spezzettata e su newline ed openssl è molto poco permissivo (ha anche ragione, non è possibile fare a piacimento o quantomeno non dovrebbe essere permesso). Devo usare delle librerie python che sono meno restrittive, quindi estraggo il p7m ed analizzo. Questo codice lo uso da almeno 5 anni e l’errore si è presentato per la prima volta a fine febbraio di quest’anno. Fortuna sono pochi file, ma sto indagando.

Questa cosa che ti funziona in Windows ma non in Linux non mi è molto chiara…
vorresti dire che il comando openssl in Windows funziona e in Linux no, con lo stesso file? Come ci arriva il file in Linux?
Se prendi il file “funzionante” che hai in Windows e lo copi così com’è (evitando python) in Linux e poi usi openssl a riga di comando (l’ultimo dei comandi dovrebbe essere quello giusto), cosa succede?

In linux arriva tramite lo SDI. Lo posiziona all’interno di specifiche directory tramite protocollo sftp.
Il medesimo file, trasferito in ambiente windows (per test), viene gestito correttamente. Il file viene convertito tramite semplice chiavetta per firma (non ho testato tramite comando openssl in windows, non mi interessa in realtà).
In ogni caso, ribadisco, ho dovuto anche utilizzare librerie apposite per trasformare un b64 perchè tramite comando “openssl base64 …” restituisce errore in quanto la stringa codificata viene spezzettata su più linee. Idem come prima. Con chiavetta su windows funziona, con chiavetta o libreria openssl in linux non funziona.
PArlo di 2 situazioni diverse, ma che creano problemi ad openssl in linux.
Siccome il mio server è basato su Debian, ho problemi in tal senso, ma non dovrebbe essere possibile. Ribadisco, fortunatamente al momento sono stati circa 6/7 file a causare problemi su circa 55000 file del 2025. Ma è fastidioso, molto.

Se può essere utile per fare una prova, a questo indirizzo web
qui
metto a disposizione il file “Base64.exe”, zippato nel file “Base64.zip”, da richiamare tramite riga di comando.
Per decodificare:

("c:\TuaDirectory\Base64.exe /decode " & FileInput & " " & FileOutput, 1)

Per codificare:

("c:\TuaDirectory\Base64.exe /encode " & FileInput & " " & FileOutput, 1)

Io lo utilizzo regolarmente con S.O. Windows senza problemi. E’ da provare con Linux. In teoria non dovrebbero esserci problemi.

Per la decodifica di dati base64 in Linux c’è già il comando base64, che è sempre presente (fa parte di coreutils). Per decodificare il file basta fare
base64 -di input.txt > output.bin
L’opzione -i serve per dirgli di ignorare eventuali caratteri non validi. Se non lo si mette, dà errore se il file contiene i caratteri CR (presenti nei file di testo prodotti in Windows).

Mi piacerebbe dare un’occhiata ad uno di questi file problematici. Non è che per caso potresti farmene avere uno? In caso puoi mandarmi un link via messaggio privato.

Mi dispiace, ma sono dati sensibili, trattandosi di fatture.
Comunque, si uso una libreria python per decodificare il base64, dato che lo script gira in python. La cosa strana e che dovrebbe essere compatibile con openssl ed invece non lo è. Mi “lamento” di questo. In ogni caso ho risolto gestendo appunto la lettura tramite libreria python che sembrerebbe essere più flessibile.

Allora, openssl gestisce due tipi di dati base64:

  • Quelli spezzettati su più righe (convenzione derivante dallo standard MIME e usata anche nei file PEM), che puoi decodificare con openssl base64 -d. Credo però che le righe debbano essere di lunghezza <= 80 caratteri, altrimenti non funziona (ed è il motivo perché una singola riga lunga non funziona).
  • Quelli che stanno tutti su una riga, senza caratteri estranei nel mezzo (come specificato dal RFC 4648). Questi li puoi decodificare con openssl base64 -d -A.

Il problema è che devi sapere a priori in quale dei due casi sei. Non c’è un comando openssl che si “arrangi” a gestire entrambi i casi. Quindi o analizzi i dati prima di chiamare openssl, oppure devi pre-elaborare i dati, per esempio eliminando i caratteri estranei.
Dato però che usi python, non vedo il motivo di chiamare openssl per fare questa operazione, visto che python include già il metodo base64.b64decode()

La domanda che uno potrebbe farsi è: perché i file fattura arrivano da SdI codificati in entrambi i modi?
Per motivi che non so, i file p7m possono essere sia binari che codificati in base64. Non so se da qualche parte sta scritto quali varianti di base64 si possono usare, ma ho il sospetto che SdI accetti dati base64 con spazi e new line aggiunti in posizione arbitraria, perché penso che usino lo stesso formato accettato nel tipo xsd:base64Binary usato nei file XML, che dice esplicitamente che ci possono essere caratteri “whitespace” nel mezzo.

Quindi direi che openssl non è comunque lo strumento adatto per decodificare i file p7m in base64, perché potenzialmente potrebbero contenere spazi o essere spezzati in righe più lunghe di 80 caratteri.