Newsgroup: it.comp.appl.access
Titolo: Estrarre fattura da p7m con CryptDecodeMessage

Ciao a tutti,
per acquisire automaticamente le fatture elettroniche ho scritto una funzione VBA che esegue l'estrazione.
La cosa funziona perfettamente, ma occasionalmente (ogni 6-12 mesi circa) salta fuori un nuovo flag della firma che non mi era ancora capitato e devo aggiornare la lista dei flag da rimuovere per l'estrazione.
Potrei continuare così, tanto finiranno prima o poi, tuttavia sto indagando altri metodi e ho trovato la funzione in oggetto che pare si possa usare allo scopo (link: 
https://resch.pro/p/vba-da-p7m-a-xml/
 nb: attenzione che alla fine nel codice c'è un piccolo errore di sintassi da correggere, si vede subito).
Tuttavia l'estrazione fallisce e non ho trovato un modo per farla funzionare.
 
Qualcuno ha mai provato con questa funzione?
 
Ciao
Davide


l'ho provato e funziona, anche con allegati
 
basta aggiungere questa funziona che in quel sorgente manca :
 
 
Function Stream_BinaryToString(sFullNameFile As String) As String
'Funzione per convertire un file da dati binari a dati di testo
'Necessario per inserire un "allegato" alla fattura elettronica
'Reperita alla seguente URL:
'
https://stackoverflow.com/questions/496751/base64-encode-string-in-vbscript
 
Const adTypeBinary = 1 ' Binary file is encoded
' Variables for encoding
Dim objXML
Dim objDocElem
' Variable for reading binary picture
Dim objStream
' Open data stream from picture
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open
objStream.LoadFromFile sFullNameFile
' Create XML Document object and root node
' that will contain the data
Set objXML = CreateObject("MSXml2.DOMDocument")
Set objDocElem = objXML.createElement("Base64Data")
objDocElem.DataType = "bin.base64"
' Set binary value
objDocElem.nodeTypedValue = objStream.Read()
Stream_BinaryToString = objDocElem.Text
' Clean all
Set objXML = Nothing
Set objDocElem = Nothing
Set objStream = Nothing
End Function
 
 
bfs

 bfs
 
Grazie per la risposta!
Intendi dire che la funzione CryptDecodeMessage vuole passata la fattura codificata in Base 64?
Perchè avevo fatto attenzione a passargliela non codificata...
 
Comunque non funziona ancora...
 
Ciao
Davide


OK, confermo che non va.
la variabile che dovrebbe contenere l'output rimane tutta fatta di null
 
ciao


creati una form chiamata maschera1
 
metti un pulsante comando0
metti una textbox chiamata testo1
 
sul click del tuo bottone metti
leggiFileFirmato "c:\blablabla\test.p7m"  'il percorso del tuo p7m
 
su un modulo incolla tutto questo
 
Option Compare Database
 
Const CMSG_DATA_FLAG = 2                    ' Raw data with no 
particular formatting
Const CMSG_SIGNED_FLAG = 4                  ' Signed message
Const CMSG_ENVELOPED_FLAG = 8               ' Enveloped (encrypted) message
Const CMSG_SIGNED_AND_ENVELOPED_FLAG = 16   ' Signed and encrypted message
Const CMSG_HASHED_FLAG = 32                 ' Hashed message
Const CMSG_ENCRYPTED_FLAG = 64              ' Encrypted message
Const CMSG_DATA = 1                         ' Raw data with no 
particular formatting
Const CMSG_SIGNED = 2                       ' Signed message
Const CMSG_ENVELOPED = 3                    ' Enveloped (encrypted) message
Const CMSG_SIGNED_AND_ENVELOPED = 4         ' Signed and encrypted message
Const CMSG_HASHED = 5                       ' Hashed message
Const CMSG_ENCRYPTED = 6                    ' Encrypted message
 
Const CERT_FIND_ANY = 0
Const CERT_FIND_EXISTING = &HD0000
Const CERT_FIND_ISSUER_OF = &HC0000
Const CERT_FIND_ISSUER_STR = &H70004
Const CERT_FIND_KEY_SPEC = &H90000
Const CERT_FIND_PROPERTY = &H50000
Const CERT_FIND_SUBJECT_STR = &H70007
Const X509_ASN_ENCODING = &H1               ' X.509 Encoding
Const PKCS_7_ASN_ENCODING = &H10000         ' PKCS #7 Message Formatting
 
Public Type CRYPT_DECRYPT_MESSAGE_PARA
     cbSize As Long
     dwMsgAndCertEncodingType As Long
     cCertStore As Long                      ' hCryptProv As Long
     pfnGetSignerCertificate As Long         ' Pointer to callback 
function    HCERTSTORE *rghCertStore;
     dwFlags As Long                         ' pvGetArg As Long  ' void 
Pointer
End Type
 
Public Type CRYPT_VERIFY_MESSAGE_PARA
     cbSize As Long
     dwMsgAndCertEncodingType As Long
     hCryptProv As Long                      ' HCRYPTPROV_LEGACY hCryptProv;
     pfnGetSignerCertificate As Long         ' Pointer to callback 
function PFN_CRYPT_GET_SIGNER_CERTIFICATE pfnGetSignerCertificate;
     pvGetArg As Long                        ' void Pointer
End Type
 
Public Declare Function CryptDecodeMessage Lib "Crypt32.dll" (ByVal 
dwMsgTypeFlags As Long, _
                                             pDecryptPara As 
CRYPT_DECRYPT_MESSAGE_PARA, _
                                             pVerifyPara As 
CRYPT_VERIFY_MESSAGE_PARA, _
                                             ByVal dwSignerIndex As 
Long, ByVal pbEncodedBlob As String, _
                                             ByVal cbEncodedBlob As 
Long, ByVal dwPrevInnerContentType As Long, _
                                             pdwMsgType As Long, 
pdwInnerContentType As Long, _
                                             ByVal pbDecoded As String, 
pcbDecoded As Long, _
                                             ppXchgCert As Long, 
ppSignerCert As Long) As Long
 
Public Function leggiFileFirmato(ByVal NomeFile As String) As String
     ' Funzione per leggere il contenuto di un file firmato p7m
     ' [NomeFile] nome del file da leggere completo di percorso
     ' Restituisce il contenuto del file firmato
 
     On Error Resume Next
     Dim dwMsgTypeFlags As Long, i As Long
     Dim pDecryptPara As CRYPT_DECRYPT_MESSAGE_PARA
     Dim pVerifyPara As CRYPT_VERIFY_MESSAGE_PARA
     Dim dwSignerIndex As Long
     Dim dwPrevInnerContentType As Long
     Dim pdwMsgType As Long, pdwInnerContentType As Long
     Dim ppXchgCert As Long, ppSignerCert As Long
     Dim m_lHCryptProv As Long               ' Handle for the 
cryptographic service provider (CSP)
 
     Dim messaggioFirmato As String
     Dim messaggioFirmato_L As Long          ' Lunghezza messaggio
     Dim messaggioDecodificato As String
     Dim messaggioDecodificato_L As Long
 
     Dim iFile As Integer
     iFile = FreeFile
     Open NomeFile For Binary Access Read As #iFile
     messaggioFirmato = Input(LOF(iFile), iFile)
     Close #iFile
 
     messaggioDecodificato = Base64Decode(messaggioFirmato)  ' Verifica 
codifica base64
     If Err.Number <> 0 Then     ' = -
2147467259
  "Errore durante 
l'analisi di "..." come tipo di dati bin.base64."
         Err.Clear
     Else
         messaggioFirmato = messaggioDecodificato
     End If
     messaggioFirmato_L = Len(messaggioFirmato)  ' Lunghezza
     messaggioDecodificato = String(messaggioFirmato_L, vbNullChar)
     messaggioDecodificato_L = 0 'Len(messaggioDecodificato)
 
     ' strutture dati
     pVerifyPara.cbSize = 20
     pVerifyPara.dwMsgAndCertEncodingType = X509_ASN_ENCODING Or 
PKCS_7_ASN_ENCODING  '--- The encoding type
     pVerifyPara.hCryptProv = m_lHCryptProv      '--- The CSP handle
     ' -
     pDecryptPara.cbSize = 16
     pDecryptPara.dwMsgAndCertEncodingType = 
pVerifyPara.dwMsgAndCertEncodingType    'X509_ASN_ENCODING Or 
PKCS_7_ASN_ENCODING
     'pDecryptPara.cCertStore = 0  '1000
     pDecryptPara.dwFlags = CMSG_SIGNED_AND_ENVELOPED
     pdwMsgType = CMSG_DATA_FLAG
     dwMsgTypeFlags = CMSG_SIGNED_FLAG    ' CMSG_DATA_FLAG
     pdwInnerContentType = 0
 
     i = CryptDecodeMessage(dwMsgTypeFlags, pDecryptPara, _
                              pVerifyPara, dwSignerIndex, _
                              messaggioFirmato, messaggioFirmato_L, _
                              dwPrevInnerContentType, pdwMsgType, _
                              pdwInnerContentType, messaggioDecodificato, _
                              messaggioDecodificato_L, ppXchgCert, 
ppSignerCert)
     i = CryptDecodeMessage(dwMsgTypeFlags, pDecryptPara, _
                              pVerifyPara, dwSignerIndex, _
                              messaggioFirmato, messaggioFirmato_L, _
                              dwPrevInnerContentType, pdwMsgType, _
                              pdwInnerContentType, messaggioDecodificato, _
                              messaggioDecodificato_L, ppXchgCert, 
ppSignerCert)
 
      Form_Maschera1.Testo1 = messaggioDecodificato
 
     If Err.Number = 0 Then
         leggiFileFirmato = messaggioDecodificato
     End If
 
End Function
 
Function Base64Decode(ByVal vCode)
     On Error Resume Next
     Dim oXML, oNode
     Set oXML = CreateObject(lb_XML_DOM)
     Set oNode = oXML.createElement("base64")
     oNode.DataType = "bin.base64"
     oNode.Text = vCode
     If Err.Number = 0 Then
         Base64Decode = Stream_BinaryToString(oNode.nodeTypedValue)
     End If
     Set oNode = Nothing: Set oXML = Nothing
End Function
 
 
 
Function Stream_BinaryToString(sFullNameFile As String) As String
'Funzione per convertire un file da dati binari a dati di testo

premi il pulsante e vedrai che nel testo1 troverai il contenuto del tuo xml
provato con una decina di fatture con allegati e no
e funziona
 
BFS
 

 BFS
 
Intanto ti ringrazio molto per la risposta completa ed esaustiva!
 
Controllando ho visto che sostanzialmente era lo stesso codice che già avevo, nondimeno, per evitare che mi fosse sfuggito qualcosa ho preso il tuo codice e l'ho sostituito al mio.
 
Il parametro lb_XML_DOM non viene riconosciuto, quindi l'ho sostituito con la stringa "MSXML2.DOMDocument"
 
Ad ogni modo, la fattura che sto usando per test non è codificata base64 quindi non dovrebbe avere bisogno di conversione.
 
Detto questo, purtroppo il risultato è lo stesso, viene restituita una stringa tutta di null (equivalente alla stringa di partenza), paro paro come se la doppia chiamata di funzione non eseguisse alcuna elaborazione, la variabile i resta uguale a zero.
 
La fattura in questione è una delle fatture da me ricevute che ho già importato con la mia routine, quindi è sicuramente ben formattata.
 
Ciao
Davide


non so che dirti
io ho fatto copia incolla da quella pagina e ho aggiunto quello che ti 
ho detto
e il risultato è questo
 
 
https://ibb.co/ZgkQWxv
 
 
fattura di un mio fornitore in p7m prelevata dal portale aruba estratta 
perfettamente
 
anche con altre fatture nessun problema
 
BFS
 
 
 
 
 
 

 BFS
 
Secondo me c'è qualcosa che non va nel codice, visto che Stream_BinaryToString apre un file e lo legge, non può funzionare all'interno della funzione Base64Decode nel modo in cui è inserita.
 
Ti chiedo un paio di cose: la funzione Base64Decode nel codice della tua applicazione riceve come input il percorso del file o il contenuto del file?
 
Il parametro lb_XML_DOM da te viene riconosciuto?
 
La funzione CryptDecodeMessage prende come input messaggioFirmato il file in chiaro o il file codificato in base64?
 
Il file in chiaro se lo visualizzi contiene fondamentalmente tutti i caratteri del file xml condito con tutti i marcatori della firma, quello in base 64 è una sequenza indistinta di lettere e numeri...
 
Ciao
Davide


fai finta che non abbia scritto nulla
leggevo un file che non centrava nulla
ti confermo che il codice riportato nel sito non funziona
 
BFS


Nessun problema, mi è servito come spunto di riflessione...
Il fatto è anche che trovo documentazione piuttosto vaga sulla funzione Cryptdecodemessage, quindi non posso neanche vedere se i parametri di input siano corretti.
 
Ciao
Davide


Hai provato a scrivere all'autore di quell'articolo chiedendo informazioni?
Magari la soluzione dell'inghippo e' dietro l'angolo
 


Certo: è la soluzione che adotto da anni e funziona perfettamente!
 
Saluti
 
P:S:  Scherzavo ... confermo che non va: un vero peccato.
 
 
 
 

I
 
funziona perfettamente.
 
es : 
https://shadoware.it/test/fatt_p7m.zip
 


No, non funziona.
Anche la tua funzione, che poi è identica alla mia, restituisce una stringa di null.
 
Ciao


Non ho trovato email sul sito, solo un link a linkedin.
 
Ciao


Io ho provato il file FATT_P7M.ZIP  e coi p7m che ho a disposizione 
funziona perfettamente, ho provato sia file vecchi del 2019 che roba 
recente del 2021
Dove per 'funziona' intendo dire che dentro la testo1  vengono 
visualizzate le righe dell'xml scelto
 


Ciao Roberto,
visto che a me continua a dare una stringa di null (non poteva essere diversamente visto che la funzione dell'esempio è identica a lla mia che avevo già testato), puoi dirmi che configurazione hai?
Che versione di Access, SO, ecc
Le fatture che importi, sono codificate Base64?
 
Ciao


Win10 64bit, Office 2013 32 bit
Le fatture sono i file che ricevo via sdi Agenzia Entrate
Per a codifica file Base64 non saprei, sono i file ricevuti direttamente 
dallo sdi Agenzia Entrate, se mi indichi come capirlo posso confermare o 
negare
Ho provato con almeno una ventina di questi file, di date diverse, e la 
procedura non ha sbagliato un colpo, nel senso che me le ha visualizzate 
tutte nella textbox
Quando parlo di 'procedura' intendo il file contenuto in
https://shadoware.it/test/fatt_p7m.zip
 


Non vorrei che fosse perchè sto usanto Access a 64 Bit...
Ora provo su access a 32
 
Comunque verificare se è codificata base 64 è facilissimo: apri la fattura col notepad, se è una sequenza continua di lettere apparentemente alla rinfusa allora è codificata base64.
 
Ciao


Questo e' il formato p7m che mi arrivano
Aperto con Notepad++
 
https://ibb.co/ZJZmrF5
 
 
Inizia con vari caratteri di controllo, poi si vede il testo come un xml 
classico

 classico
 
Il file che mostri non è codificato base64, avresti una cosa tipo:
 
agHFSDHWRJWErhHFJFDgsdfhsgfJSDFHERHTERJEWRHOWEHJJdfrbteuer
 
e così via
 
Ad ogni modo ho verificato che su access 32 bit la funzione funziona ;-) e quindi la cosa diventa interessante.
 
Resta da capire come farla funzionare su Access a 64 bit
 
Ciao

 Resta da capire come farla funzionare su Access a 64 bit 
 
 
Premetto che ho testato solo la versione da te recuperata (coincidente con quella di Shado?).
Esiti:
 
Access 64 bit   ---->   Null
Access2003     ---->   Null   (l'unica versione che ho su questo PC).
 
Quindi, pare che non vada su tutte le release a 32 bit, pur tenendo conto dell'antichità di A '2003.
Su quale versione l'hai provato?
 
Saluti
 


Access 365 (2019) 32 bit e ha funzionato
Access 365 (2019)  64 bit, restituisce stringa di null
 
Sto cercando informazioni sull'adattamento delle api a VBA 64 bit, sto provando con LongPtr al posto dei Long, ma ancora niente
 
 
Ciao

Vedi se questo ti aiuta:
 
https://stackoverflow.com/questions/40748130/adapt-crypto-api-vba-to-use-with-access-2010-64-bit
 
 
Ovviamente se gestisci in compilazione condizionale devi rendere condizionale anche i TypeStructure ecc... ma il codice esposto mi pare già estremamente chiaro.
 
Ciao
@Alex


Grazie del suggerimento.
Purtroppo ho già fatto ogni possibile modifica per adattarlo alla versione 64 bit (PtrSafe, LongPtr, ecc) ma continua a funzionare perfettamente su 32 bit e a non funzionare su access 64 bit...
 
Ciao


Buonasera, ho il tuo stesso problema, tu hai risolto in qualche modo?
 
Ciao
 

 Buonasera, ho il tuo stesso problema, tu hai risolto in qualche modo? 
 
Ciao Antonio,
in effetti no, quel codice non ne vuole sapere di funzionare su Access a 64 bit, mi sembra un grosso limite ma al momento non ho novità.
Vado col mio personale estrattore che finora ha funzionato benissimo.
 
Ciao


Cosa usi? Estrae anche le fatture firmate il cui file xml è codificato base64? Io ho riscontrato alcune fatture che sono così e con la funzione CryptDecodeMessage a 32bit e qualche modifica al codice, sono riuscito ad estrarre il file xml corretto. 
Ora che a 64bit non funziona più niente,  non riesco a trovare alternativa in VBA, quindi penso di utilizzare openssl.
Consigli?
Ciao