Problema Test Interoperabilità

Ciao a tutti,

dopo aver registrato il canale, ho iniziato con i test di interoperabilità. Simulando l’invio di fatture dall’ambiente di test dello SDI al mio webservice, riscontro però il seguente errore:

javax.net.ssl.SSLHandshakeException: com.ibm.jsse2.util.j: PKIX path building failed: com.ibm.security.cert.IBMCertPathBuilderException: unable to find valid certification path to requested target

Ho letto vari argomenti a riguardo su questo forum, e il problema sembra essere legato all’handshake che fallisce. Se ho capito bene, i certificati con cui si presenta il Client (SDI) al mio Server sembrano non combaciare.

Il webservice che espongo per ricevere le fatture dallo SDI è di questo tipo: https://CUSTOMDOMAIN/cxf/test/ReceiveInvoice, dove CUSTOMDOMAIN presenta la seguente concatenazione di certificati:

Certificato Server (firmato dall’Agenzia delle Entrate) + caentrate.cer (Root)

Il certificato Server inoltre ha una configurazione TLS dove nella Trust List ho aggiunto sia caentrate.cer che caentratetest.cer.

Qualcuno di voi ha riscontrato un problema simile e ha risolto?

Assicurati che il certificato venga presentato anche quando ti colleghi direttamente all’indirizzo IP corrispondente a CUSTOMDOMAIN (https://x.y.z.w/cxf/....), perché il client usato da SdI non usa SNI e quindi non puoi usare il dominio per decidere di presentare il certificato del tuo canale. Deve essere quello di default associato all’indirizzo IP (se il client non richiede un altro dominio con SNI).

Ciao Vladan,

grazie per la risposta. Ma non credo di aver capito precisamente cosa intendi. Purtroppo su questa questione dei certificati non me ne intendo. Potresti essere più preciso su quello che dovrei fare?

La gestione dei certificati è la parte più complessa della configurazione di un canale e non sempre è facile venirne fuori se non si ha esperienza. Cosa bisogna fare esattamente dipende dall’ambiente e dai software usati.
Per verificare se la configurazione è corretta, puoi eseguire i seguenti due comandi con openssl:

openssl s_client -connect CUSTOMDOMAIN:443 -servername CUSTOMDOMAIN
openssl s_client -connect CUSTOMDOMAIN:443

In entrambi i casi dovresti ottenere lo stesso output. In particolare, dovrebbe esserci qualcosa del genere:

Certificate chain
 0 s:C = IT, O = Agenzia delle Entrate, OU = Fatturazione Elettronica, OU = Server, CN = SDI-99999999999
   i:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
 1 s:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
   i:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate

Ovvero, dovrebbero esserci il certificato CA dell’AdE e il certificato server del tuo canale (il CN potrebbe anche avere il tuo dominio invece della partita IVA , se hai creato il CSR così).
Ti darà anche errore di validazione del certificato, ma è normale, perché la CA dell’AdE non è tra quelle riconosciute dal sistema operativo (e dai browser).

Se l’output del secondo comando non contiene il certificato server del tuo canale, allora hai il problema che dicevo, ovvero che il server non presenta il certificato a meno che il client non indichi esplicitamente il hostname durante il handshake TLS, cosa che il client SDI non fa.
In questo caso devi scoprire come fare ad impostare un certificato “di default”, non legato ad uno specifico hostname (che viene usato nel caso in cui il client non lo specifica).

Ho fatto la prova che mi hai suggerito:

openssl s_client -connect sdi.hipoges.com:443 -servername sdi.hipoges.com
CONNECTED(0000019C)
depth=1 C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
verify error:num=19:self signed certificate in certificate chain
verify return:1
depth=1 C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
verify return:1
depth=0 C = IT, O = Agenzia delle Entrate, OU = Fatturazione Elettronica, OU = Server, CN = SDI.HIPOGES.COM
verify return:1

Certificate chain
0 s:C = IT, O = Agenzia delle Entrate, OU = Fatturazione Elettronica, OU = Server, CN = SDI.HIPOGES.COM
i:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
1 s:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
i:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate

openssl s_client -connect sdi.hipoges.com:443
CONNECTED(000001B0)
depth=1 C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
verify error:num=19:self signed certificate in certificate chain
verify return:1
depth=1 C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
verify return:1
depth=0 C = IT, O = Agenzia delle Entrate, OU = Fatturazione Elettronica, OU = Server, CN = SDI.HIPOGES.COM
verify return:1

Certificate chain
0 s:C = IT, O = Agenzia delle Entrate, OU = Fatturazione Elettronica, OU = Server, CN = SDI.HIPOGES.COM
i:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
1 s:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate
i:C = IT, O = Agenzia delle Entrate, CN = CA Agenzia delle Entrate

ed effettivamente entrambi contengono il certificato server firmato dall’AdE.

Però ho approfondito la questione dello SNI di cui mi parli.
Il dominio sdi.hipoges.com è un dominio custom che uso per mascherare il reale endpoint del mio Server (lavoro su SAP e questo era un prerequisito fondamentale da implementare). Quindi se chiamo l’endpoint https://sdi.hipoges.com/cxf/hiplaw/ItalyReceiveInvoice vengo direzionato correttamente al vero endpoint del server (ho testato questa cosa tramite Postman).
Ma visto che SDI non invia il campo SNI, invece di richiedere sdi.hipoges.com lo SDI si connette direttamente all’indirizzo IP del dominio. Infatti facendo il seguente test trovo:
openssl s_client -connect 20.82.83.59:443
CONNECTED(0000015C)
Can’t use SSL_get_servername
depth=1 C = US, O = DigiCert Inc, CN = DigiCert Global G2 TLS RSA SHA256 2020 CA1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = DE, ST = Baden-W\C3\BCrttemberg, L = Walldorf, O = SAP SE, CN = *.cf.eu20-001.hana.ondemand.com
verify return:1

Certificate chain
0 s:C = DE, ST = Baden-W\C3\BCrttemberg, L = Walldorf, O = SAP SE, CN = *.cf.eu20-001.hana.ondemand.com
i:C = US, O = DigiCert Inc, CN = DigiCert Global G2 TLS RSA SHA256 2020 CA1
1 s:C = US, O = DigiCert Inc, CN = DigiCert Global G2 TLS RSA SHA256 2020 CA1
i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root G2

e qui giustamente il certificato firmato dall’AdE non c’è.

Mi confermi di aver capito bene il problema? Se ho capito bene dovrò essere io a trovare una soluzione, tramite magari un reverse proxy. Cosa che però mi sembra abbastanza strana, perchè nella guida ufficiale SAP che ho seguito non fa nessun riferimento a questo.

Ovviamente intendevo dire “Mi confermi che ho capito bene IO il problema?”:joy:

Sì, hai capito bene. Però sono io che non avevo capito come funziona il comando s_client di openssl.

Le due prove che ti ho detto di fare sono equivalenti. Se non viene specificata l’opzione -servername, openssl usa comunque SNI con il nome host specificato in -connect. Per non farglielo usare, bisogna fare come hai fatto tu (usare l’indirizzo IP), oppure specificare l’opzione -noservername.

Siccome senza SNI il server SAP restituisce un certificato generico (e non quello tuo), mi sa che serve un reverse proxy che gestisca la parte SSL. Non so come mai la guida non menziona la cosa (o se c’è qualche altro modo per aggirare il problema, tipo un indirizzo IP dedicato su cui mettere il proprio certificato).
Mi è anche venuto il dubbio che magari nel frattempo il client SDI è stato aggiornato e ora usa SNI, ma no. Ho appena controllato e continua a non usarlo.