Accreditamento SDICoop: configurazione SSL su Apache

Buongiorno,
da alcuni giorni stiamo cercando di configurare un web server Apache con i certificati che abbiamo ricevuto in fase di accreditamento ma non siamo ancora riusciti ad ottenere il risultato desiderato.
Abbiamo effettuato parecchie ricerche in rete ma non abbiamo trovato un esempio di configurazione funzionante e ci sfugge il significato di tutti i file ricevuti e come combinarli insieme.

Al momento della richiesta di accreditamento il server apache era configurato con:
SSLCertificateFile /etc…/fullchain.pem
SSLCertificateKeyFile /etc…/privkey.pem

La richiesta delle CSR è stata fatta generando il file top.csr con la chiave top.key.

Il sistema di interscambio ci ha inviato:

  • SDI-xxxxxxxxxxxClient.cer
  • SDI-xxxxxxxxxxxServer.cer
  • caentrate.der
  • CAEntratetest.cer
  • servizi.fatturapa.it.cer
  • SistemaInterscambioFatturaPA.cer
  • SistemaInterscambioFatturaPATest.cer
  • testservizi.fatturapa.it.cer

Qualcuno ha una guida o un esempio che spieghi come configurare il server Apache in modo da portare avanti i test di interoperabilità esponendo il certificato desiderato dal SdI combinando il certificato SSL originale con quello rilasciato dal SdI?

Per brevità non riporto tutti i test che abbiamo fatto ma i risultati ottenuti sono solo 2:

  • veniva sempre esposto solo il certificato dato dalla CA iniziale
  • il certificato risultante non era valido per l’host

Grazie in anticipo per tutte le indicazioni che ci potrete dare.

Buongiorno Oiram,

questa è la configurazione che uso io:

<VirtualHost xxx.xxx.xxx:443 [xxxx:xxxx:xxxx:xxxx::2]:443>
   ServerName fatturapa.xxxx.tld
   SSLEngine On
   SSLCertificateFile      /etc/ssl/certs/SDI-01234567890-server-prod.crt
   SSLCertificateKeyFile   /etc/ssl/private/SDI-01234567890-server-prod.key
   SSLCertificateChainFile /etc/ssl/certs/CA_Agenzia_delle_Entrate.pem
   SSLVerifyClient optional
   SSLVerifyDepth 5
   SSLCACertificateFile    /etc/ssl/certs/CA_Agenzia_delle_Entrate_all.pem

   SSLStrictSNIVHostCheck off

   ProxyPreserveHost On

   RewriteEngine On
   RewriteCond   %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS
   RewriteRule  .? - [F]
   ErrorDocument  403  "You need a client side certificate issued by CA_Agenzia_delle_Entrate to access this site"

   ProxyPass  /    http://xxxxx/
   ErrorLog  /var/log/apache2/fatturapa-prod_error.log
   CustomLog /var/log/apache2/fatturapa-prod_access.log combined
   LogLevel   Info

</VirtualHost>

SSLStrictSNIVHostCheck off probabilmente adesso non è più necessario.

Spero ti possa essere d’aiuto.
Se qualcuno in cambio volesse pubblicare una configurazione funzionante per nginx mi sarebbe utile.

s.

1 Mi Piace

Grazie mille per la risposta.

Cercando su internet le varie opzioni sull’utilizzo dei certificati ho solo ottenuto di avere in testa una grande confusione…
Perdona la mia ignoranza ma vorrei fare il mapping con i dati in mio possesso:

SDI-01234567890-server-prod.crt è il certificato server fornito dall’agenzia (io ho un file .cer ma non dovrebbe cambiare molto)

SDI-01234567890-server-prod.key capisco che sia una chiave ma non so se è stata estratta dal certificato server dell’agenzia oppure se è la chiave privata generata con la richiesta csr

CA_Agenzia_delle_Entrate.pem immagino sia il certificato di CA dell’agenzia delle entrate di produzione convertito da .cer a .pem

CA_Agenzia_delle_Entrate_all.pem non riesco a capire bene cosa sia. Forse è un certificato generato da una concatenazione con il certificato legato al dominio (rilasciato da una CA diversa dall’agenzia delle entrate)?

Poi avrei ancora un paio di domande: noi avevamo sviluppato degli stub dei web services in php ma non avevamo ancora compreso tutta la complessità dell’autenticazione richiesta dall’agenzia.
Tu hai sviluppato i web services in php? Hai usato una libreria particolare per gestire la WS-Security? Oppure hai sviluppato in java con tomcat (o altro application server)? Da quanto ho capito l’opzione Java + Application Server sembra essere la più utilizzata…

Grazie ancora per le informazioni.

Salve,

fortunatamente sto cercando (senza successo :frowning: ) di spostare la configurazione su nginx per cui risponderti mi serve anche per fare il punto.

Una nota sui formati: i certificati possono essere in formato binario, in formato PKCS o in formato testo PEM (che sarebbe la rappresentazione Base64 del contenuto binario racchiuso tra una riga -----BEGIN CERTIFICATE----- e una riga -----END CERTIFICATE-----.
I file .pem, .crt, .key di solito sono in formato PEM. Le estensioni .cer e .der possono indicare un formato binario o un formato PEM, in base alla convenzione preferita da chi li produce. SOGEI ci dà i file CAEntrate.der in formato PEM e SDI-xxxxxxxxxxxClient.cer in formato binario.
Nel dubbio ti consiglio di usare uno strumento come XCA (http://hohnstaedt.de/xca/) che legge, interpreta e scrive tutti i formati.

I file per la configurazione su Apache HTTPD sono tutti in formato PEM. Quindi:

  • SDI-xxxx-server-prod.crt contiene il certificato firmato dall’Agenzia (convertito da binario a PEM)
  • SDI-xxxx-server-prod.key contiene la chiave privata usata per generare la richiesta di certificato (sempre in formato PEM)
  • CA_Agenzia_delle_Entrate.pem contiene il certificato dell’autorità di certificatione dell’agenzia (cantrate.der)
  • CA_Agenzia_delle_Entrate_all.pem contiene la concatenazione del certificato dell’autorità di certificazione dell’agenzia più il certificato dell’autorità di certificazione di test (caentrate.der + CAEntrateTest.der). In questo modo lo stesso server accetta le chiamate sia dal servizio di test che dal servizio di produzione (nel tuo scenario potresti scegliere di organizzarti diversamente).

In quanto all’implementazione dei servizi, noi usiamo Java (CXF), ma non credo ci siano problemi particolari con altre piattaforme soprattutto perché WS-Security non è usata. L’autenticazione è gestita a livello di trasporto tramite, appunto, questi certificati e, nel nostro caso, demandata ad Apache.

s.

2 Mi Piace

Grazie mille per la chiarezza nella spiegazione.

Come ho già detto, la mia ricerca su internet mi aveva confuso le idee più che chiarirmi il funzionamento portandomi a pensare a WS-Security.

Non mi resta che capire come utilizzare gli altri certificati distribuiti dall’agenzia nel KitDiTest (testservizi.fatturapa.it.cer, SistemaInterscambioFatturaPATest.cer per il test e servizi.fatturapa.it.cer, SistemaInterscambioFatturaPA.cer per il server di produzione) e poi avrò risolto gran parte dei nostri problemi.

Grazie ancora per la disponibilità.

Salve a tutti, anche noi abbiamo lo stesso problema.
Condivido la nostra problematica nella speranza di trovare una via comune risolutiva.

Il nostro Web Service è caricato in un server Linux ed implementato tramite PHP.

PARTE 1

Fino ad ora abbiamo provato ad eseguire l’invio delle fatture tramite la classe SoapClient di php, con il seguente codice:

$ssl_params=array(
//‘ciphers’=>“ECDHE-RSA-AES128-SHA256”, //non influente
//‘crypto_method’=>non necessario
‘verify_peer’=>true,
‘local_cert’ =>“SDI-XXXXXXXXXXX.pem”,
//‘local_pk’ =>“SDI-XXXXXXXXXXX.key”,
‘cafile’=>“caentrate_full.der”,
);

$client_params=array(
“location”=>$endpoint,
“cache_wsdl”=>WSDL_CACHE_NONE,
“trace”=>TRUE,
“stream_context”=>stream_context_create([‘ssl’ => $ssl_params]);
);

$client=new SoapClient($wsdl,$client_params);

Abbiamo eseguito un’infinità di combinazione dei certificati ottenendo sempre lo stesso risultato:
{“faultstring”:“Forbidden”,“faultcode”:“HTTP”}

Faccio presente che in ambiente PHP i file tipo P12 o PFX non sono supportati quindi bisogna convertirli in PEM. Ma anche eseguendo questa conversione otteniamo lo stesso risultato: “FORBIDDEN”.

E’ assai strano che in un precedente Test di Accreditamento, con il codice appena esposto, siamo riusciti a inviare le fatture, ma avviando un nuovo test non funzionava più.

PARTE 2

A questo punto abbiamo supposto che la classe SoapClient di php non sia in grado di supportare efficacemente il protocollo SSL, quindi abbiamo creato una classe derivata “SDISoapClient” (estendendo SoapClient) ed abbiamo fatto l’override della funzione _doRequest(). L’intento è quello di inviare le SOAP request tramite CURL che offre maggiori controlli.

function __doRequest($request, $location, $action, $version){
$certspath="./path_to_certs/";
//CA file
$cafile=“caentrate_full.pem”;
//PRIVATE KEY file
$keyFile = “SDI-XXXXXXXXXXX.key”;
//Client CERT file
$clientCertFile = “SDI-XXXXXXXXXXX.pem”;
$headers = array(
"Content-type: text/xml;charset=“utf-8"”,
“Accept: text/xml”,
“Cache-Control: no-cache”,
“Pragma: no-cache”,
“SOAPAction: {$action}”,
//"Content-length: ".strlen($request),
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
//curl_setopt($ch, CURLOPT_PORT, 443);
curl_setopt($ch, CURLOPT_SSLKEY, $certspath.$keyFile);
curl_setopt($ch, CURLOPT_SSLCERT, $certspath.$clientCertFile);
curl_setopt($ch, CURLOPT_CAINFO, $certspath.$cafile);
curl_setopt($ch, CURLOPT_URL, $location);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request); // la SOAP request
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

//INVIO
$dati = curl_exec($ch);
$err_num=curl_errno ($ch);
$err_desc=curl_error($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
//DEBUG
echo “—CURL RESPONSE:\ndati={$dati}\nerr_num={$err_num}\nerr_desc={$err_desc}\nhttpcode={$httpcode}”;die;
}

Anche in questo caso abbiamo tentato una combinazione infinita di certificati e chiavi private (cifrate e non cifrate) ottenendo sempre lo stesso risultato:

Error 403: AuthenticationFailed

Abbiamo aperto un ticket per il SdI ormai da una settimana ma non abbiamo ricevuto alcuna risposta.

Spero che qualcuno riesca a trovare una soluzione.

Attendo con speranza un vostro contributo.

Ciao Antonello,

nella versione con CURL ti consiglio di impostare l’opzione curl_setopt($ch, CURLOPT_VERBOSE, true); in modo da far uscire un po’ di log che poi puoi riportare qui per aiutarti a capire dove sta il problema.

s.

Ciao Stefano, ti posto il report:
-----------------------------------INIZIO DEBUG-----------------------------------------
* Trying 217.175.53.96…
* Connected to testservizi.fatturapa.it (217.175.53.96) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: ./certs/caentrate_full.pem
CApath: none
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: C=IT; O=Agenzia delle Entrate; OU=Servizi Sicuri; CN=testservizi.fatturapa.it
* start date: Feb 14 12:18:26 2017 GMT
* expire date: Feb 14 12:28:00 2020 GMT
* common name: testservizi.fatturapa.it (matched)
* issuer: C=IT; O=Agenzia delle Entrate; OU=Servizi Telematici; CN=CA Agenzia delle Entrate
* SSL certificate verify ok.
> POST /ricevi_file HTTP/1.1

Host: testservizi.fatturapa.it

Content-type: text/xml;charset="utf-8"

Accept: text/xml

Cache-Control: no-cache

Pragma: no-cache

SOAPAction: http://www.fatturapa.it/SdIRiceviFile/RiceviFile

Content-length: 9755

Expect: 100-continue



< HTTP/1.1 100 Continue

* We are completely uploaded and fine
< HTTP/1.1 403 Forbidden

< Date: Fri, 18 May 2018 08:10:46 GMT

< X-Powered-By: Servlet/3.0

< $WSEP: 

< Transfer-Encoding: chunked

< Content-Type: text/html;charset=utf-8

< Content-Language: en-US

< 

* Connection #0 to host testservizi.fatturapa.it left intact

-----------------------------------FINE DEBUG-----------------------------------------

Ti invio anche l’analisi della RESPONSE:

dati=Error 403: AuthenticationFailed
err_num=0
err_desc=
httpcode=403

Ciao,

la parte TLS sembra a posto. Adesso il problema sembra più nella parte SOAP, ma su PHP non so dire molto.
Il servizio è molto semplice e l’unica cosa importante è attivare il protocollo MTOM. Potresti voler verificare se il tuo client lo usa.

s.

PHP non supporta alcuna funzionalità per il protocollo MTOM. Mi guardo le specifiche w3c e vedo se riesco a sviluppare una funzione.
Aggiornerò la discussione appena ho qualcosa.

Per quanto riguarda le impostazioni del curl ho aggiunto anche l’opzione:

CURLOPT_SSL_ENABLE_ALPN=>false

poichè dal report di debug risultava che il server non la supporta.

Intanto ti ringrazio infinitamente, sei stato di grande aiuto.

Ultimi aggiornamenti:
di punto in bianco il server SdI ha restituito la SOAP response.
Ho fatto un invio di test (alle ore 16:22) per verificare i dati di flusso; mi attendevo il consueto ERR: 403 ed invece ha restituito la Soap response con Identificativo SDI e tutto il resto.

Non ho capito perché prima non funzionava e adesso si, visto che non ho apportato modifiche, ma
vi assicuro che è stata una felicissima sorpresa, dopo lunghe e frustranti settimane improduttive.

Detto questo rimangono ancora da risolvere alcuni problemi:

  1. Sviluppare la funzione per il protocollo MTOM;

  2. A quanto pare, quando il server SdI invia i messaggi al mio WebServer incontra un errore:

    javax.net.ssl.SSLHandshakeException: General SSLEngine problem

Visto che nel nostro server è già installato un certificato validato e valido per i domini di primo e secondo livello, abbiamo creato un sottodominio in cui:
a) abbiamo sviluppato il WebServer;
b) abbiamo installato il certificato Server fornitoci dal SdI;

Cercheremo di capire quali sono le problematiche che generano la SSLHandshakeException.

Intanto se qualcuno ha qualche suggerimento è ben accetto.

Ciao Antonello,

personalmente ho incontrato quell’errore in due casi:

  • quando il server ospitava più siti virtuali sulla stessa porta 443 utilizzando SNI.
  • quando uno strumento che ho usato per configurare i certificati ha aggiunto una chiave Diffie-Hellmann (dhparam) alla configurazione.

Riguardo al primo caso non ho chiaro se SDI supporta SNI o no. All’inizio (cioè nel 2014) sicuramente SNI non era supportato e occorreva dedicare un indirizzo IP al servizio. Diversi mesi fa sono state fatte delle operazioni che facevano pensare all’introduzione del supporto SNI (ad esempio è stata introdotta la possibilità di generare i certificati con il nome DNS del servizio, anziché con il CN impostato a SDI-partitaiva). Al momento nel mio caso SNI non è supportato. In assenza dell’intestazione SNI durante l’handshake TLS il tuo server restituirà il certificato di default del server e l’handshake fallisce.

Riguardo al secondo caso, la mia chiave DH era a 3072 bit ed è probabile che SOGEI usi una versione di JVM che non supporta chiavi più grandi di 2048 bit. Ho risolto togliendo del tutto la chiave DH dalla configurazione.

s.

In effetti eseguendo una verifica tramite openssl ottengo questi risultati:

  1. openssl s_client -connect soto.dominio.it:443

mi rende i dati del certificato primario del dominio di primo e secondo livello (dominio.it);

  1. definendo invece il SNI nel parametro -servername tramite il comando

openssl s_client -connect sotto.dominio.it:443 -servername sotto.dominio.it

Ottengo i dati del certificato SOGEI fornitomi proprio per quel sottodominio (e correttamente installato nel server).

Aggiorno i progressi della discussione:

Finalmente abbiamo portato a termine il test di Accreditamento SdI; di seguito faccio il riepilogo di quello che abbiamo fatto per riuscire in questa impresa:

  1. Avviato la Richiesta di accreditamento indicando gli endpoint dei WebService in un sottodominio del nostro server;

  2. Installato il certificato SOGEI (dell’Agenzia delle Entrate) nel sottodominio;

  3. Visto che i sistemi SdI non supportano l’SNI: abbiamo fatto in modo di rendere PRIMARIO il certificato SOGEI (rispetto al certificato del dominio principale). Questo grazie all’impagabile contributo dei tecnici NETSONS (i fornitori dei miei servizi; non è pubblicità, semplicemente un modo di dare merito a chi spetta);

  4. abbiamo ridefinito (tramite override di funzioni) le classi PHP SoapClient e SoapServer in modo da supportare MTOM; (vista la complessità sarebbe poco opportuno postare qui le soluzioni applicate, se qualcuno ha bisogno può contattarmi, sarò lieto di dare una mano);

  5. abbiamo eseguito PASSO/PASSO le procedure di test in base a quanto indicato nel Piano allegato alla richiesta di Accreditamento;

Nelle varie fasi di test abbiamo dovuto rimodulare il nostro codice in modo che elaborasse correttamente i dati inviati dal SdI. Abbiamo constatato che, in alcuni casi, questi dati non rispondono esattamente alle specifiche tecniche definite dallo stesso SdI. Per cui vi suggerisco di creare una funzione di monitoraggio del flusso in entrata e in uscita dal vostro Server in modo da utilizzare i dati di monitoraggio per il debug e l’ottimizzazione del codice.

Rimango a disposizione nel caso qualcuno avesse bisogno.

Ciao Antonello,
abbiamo inoltrato il modulo con la richiesta di accreditamento e siamo in attesa che venga approvata in modo da ottenere i certificati.

Nel frattempo volevo chiederti che tipo di librerie (se le hai usate) hai usato/adattato per apporre la firma in formato Xades
grazie

Daniele

Ciao Antonello,
sto avendo lo stesso problema nel cercare di ricevere ed inviare i file con supporto MTOM da php poichè la SoapClient e la SoapServer non lo supportano, avresti qualche dritta a riguardo?
Grazie

Ciao Roberto,
Purtroppo online non c’è niente che faccia al caso nostro. Il supporto MTOM per PHP è stato rimosso dalla versione 5.4 (se non sbaglio).
Noi abbiamo risolto riscrivendo le classi SoapClient e SoapServer, ovvero creando delle classi derivate e modificando le funzioni che gestiscono la generazione degli XML e l’invio delle richieste.

Ho risposto (in parte) sullo stesso argomento in un altro ticket; ti rimando al link in modo da non creare duplicati sugli stessi temi:
https://forum.italia.it/t/supporto-mtom/3760

1 Mi Piace

Antonello ti ringrazio,
purtroppo non ho i permessi per accedere al link in questione, sono un nuovo iscritto e non so se devo attendere per essere abilitato.

Finalmente con l’attivazione delle nostre credenziali (risolto errore 403)
abbiamo potuto iniziare i test di comunicazione.

attualmente stiamo provando con usa chiamata semplice usando la funzione nativa soapClient
$client = new SoapClient("https://testservizi.fatturapa.it/SdI2AccoglienzaWeb/SdIRiceviFile_service/WEB-INF/wsdl/SdIRiceviFile_v1.0.wsdl",$options );

Unica differenza è che abbiamo dovuto impostare il link al wsdl effettivo perchè usando https://testservizi.fatturapa.it/ricevi_file?wsdl
l’ xsd schema TrasmissioneTypes_v1.0.xsd non veniva trovato.

con questo metodo riusciamo a raggiungere SDI e leggere l’elenco delle funzioni dichiarate nel wsdl

il problema è l’invio di File e NomeFIle.
il response che otteniamo è

looks like we got no XML documentResponse

mentre accedendo al sistema di gestione del canale risultano solo notifiche di scarto con 2 errori.
uno esterno alla notifica
javax.net.ssl.SSLHandshakeException: General SSLEngine problem
uno all’interno della notifica
codice 0200 File non conforme al formato

il file XML della fattura però, se verificato sul portale fatturapa.it, passa i controlli.

A noi funziona solo inviando un file firmato, quindi in formato .xml.p7m