Utilizzare i voucher
Ciclo di vita
L'intero ciclo di vita dei voucher si compone di una serie di interazioni machine to machine. Il flusso è diverso se l'aderente sta richiedendo un voucher da spendere su un e-service del catalogo in qualità di fruitore o se vuole dialogare con le API di PDND Interoperabilità per ottenere informazioni.
I voucher sono di fatto dei token JWT. Il flusso di autenticazione implementato è un OAuth 2.0 che fa riferimento all'RFC6750 per quanto riguarda l'uso del Bearer token e all'RFC7521 per l'autorizzazione del client tramite client assertion.
Il paradigma utilizzato per l'implementazione di tutte le API esposte di PDND Interoperabilità è REST.
Ulteriori prove autorizzative o informazioni legate al dominio del fruitore possono essere previste nella comunicazione tra erogatore e fruitore, a discrezione delle parti.
Richiesta di un voucher
L'aderente costruisce una client assertion e la firma con una chiave privata la cui omologa pubblica è nota a PDND Interoperabilità. Successivamente, chiama il server autorizzativo di PDND Interoperabilità richiedendo un voucher valido per quell'asserzione firmata. In caso tutti i controlli diano riscontro positivo, PDND Interoperabilità restituisce un voucher all'aderente.
Flusso voucher spendibile presso le API di Interoperabilità
L'aderente fa richiesta di un voucher. Una volta ottenuto, lo inserisce come header autorizzativo nelle successive chiamate verso le API di PDND Interoperabilità.
Flusso voucher spendibile presso un e-service del catalogo
Il fruitore fa richiesta di un voucher. Una volta ottenuto, lo inserisce come header autorizzativo nelle chiamate che effettua verso l'e-service di un erogatore.
L'erogatore, ricevuta la richiesta con il voucher, effettua i debiti controlli. È possibile che alcuni di questi controlli richiedano il recupero di informazioni detenute da Interoperabilità. In quel caso, l'erogatore stesso dovrà richiedere un voucher spendibile presso le API di Interoperabilità. Una volta terminati i controlli, l'erogatore autorizza il fruitore all'accesso al servizio per la specifica finalità richiesta.
Implementazione
Si suggerisce di fare sempre riferimento alla guida passo passo implementata nel back office di PDND Interoperabilità. Alcune informazioni sono tuttavia riportate anche qui per completezza.
Richiesta di un voucher spendibile presso le API di Interoperabilità
Il pre-requisito per poter ottenere un voucher valido è aver caricato almeno una chiave pubblica, parte del proprio materiale crittografico, all'interno di un client api interop (disponibile sull'interfaccia del back office alla voce Fruizione > I tuoi client api interop). Per saperne di più, leggi la sezione dedicata.
Il primo passo è costruire una client assertion valida e firmarla con la propria chiave privata (che deve essere l'omologa della chiave pubblica depositata sul client su PDND Interoperabilità). La client assertion è composta da un header e un payload.
Nell'header andranno inseriti tre campi:
kid
: l'id della chiave che si usa per firmare l'asserzione, reperibile su PDND Interoperabilità;alg
: l'algoritmo usato per firmare il JWT (per ora, sempreRS256
);typ
: il tipo di oggetto che si sta inviando (sempreJWT
).
Nel payload ci saranno invece sei campi:
iss
: l'issuer, in questo caso il clientId;sub
: il subject, in questo caso sempre il clientId;aud
: l'audience, reperibile su PDND Interoperabilità;jti
: il JWT ID, un id unico random assegnato da chi vuole creare il token, si usa per tracciare il token stesso. Deve essere cura del chiamante assicurarsi che l'id di questo token sia unico per quanto riguarda la client assertion;iat
: l'issued at, il timestamp riportante data e ora in cui viene creato il token, espresso in UNIX epoch (valore numerico, non stringa);exp
: l'expiration, il timestamp riportante data e ora di scadenza del token, espresso in UNIX epoch (valore numerico, non stringa).
Una volta firmata l'asserzione, prendere l'output e tenerlo da parte.
Il secondo passaggio è chiamare il server autorizzativo di PDND Interoperabilità con la client assertion per ottenerne in cambio un voucher spendibile presso l'API di di PDND Interoperabilità (ossia un token valido). L'URL dell'endpoint cambia in funzione dell'ambiente e sarà chiaramente visibile sull'interfaccia all'interno del back office. L'endpoint andrà chiamato con alcuni parametri in body:
client_id
: di nuovo il clientId usato nell'assertion;client_assertion
: il contenuto dell'asserzione firmata nel primo passaggio;client_assertion_type
: il formato della client assertion, come indicato in RFC (sempreurn:ietf:params:oauth:client-assertion-type:jwt-bearer
);grant_type
: la tipologia di flusso utilizzato, come indicato in RFC (sempreclient_credentials
).
Se tutto è impostato correttamente, PDND Interoperabilità risponderà con un voucher valido all'interno del body della risposta alla proprietà access_token
. Sempre nella risposta, sarà contenuta anche la durata di validità del token in secondi (es. "expires_in": 600
indica un token con validità 10 minuti, 10 * 60 secondi = 600).
Il token andrà inserito nell'header di tutte le chiamate successive verso le API gateway di PDND Interoperabilità come header Authorization: Bearer
.
Richiesta di un voucher spendibile presso un e-service del catalogo
Le istruzioni sono essenzialmente uguali a quelle del paragrafo precedente, con due eccezioni.
La prima è che il pre-requisito per poter ottenere un voucher valido è aver caricato almeno una chiave pubblica all'interno di un client e-service associato ad una finalità attiva, invece di un client api interop. Anche qui, si veda la sezione dedicata.
La seconda è che all'interno del payload della client assertion va specificato anche il purposeId
, ossia l'id della finalità per la quale si richiede il voucher. Questo parametro è disponibile nel back office di PDND Interoperabilità.
Verifica di un voucher da parte di un erogatore di e-service
L'erogatore di un e-service deve ovviamente poter verificare la legittimità di qualsiasi richiesta riceva. Prima di tutto, estrae il voucher dalla richiesta, dall'header Authorization: Bearer
nel quale arriva, e lo deserializza.
Contenuto di un voucher
A titolo esemplificativo, di seguito un esempio di contenuto di voucher deserializzato.
Header:
Payload:
Verifiche sugli header
Il voucher deve essere di tipo at+jwt
.
Verifica sulla firma
L'erogatore scarica la lista di chiavi in uso da un file esposto nella cartella .well-known
di PDND Interoperabilità (l'URL corretta è disponibile sull'interfaccia nel back office all'interno della scheda di ogni singolo e-service e cambia in funzione dell'ambiente; a titolo esemplificativo, questa è quella di produzione).
All'interno del file, l'erogatore cerca l'oggetto che ha lo stesso kid
presente nell'header del voucher. In quello stesso oggetto troverà la chiave pubblica al parametro n
. Effettuerà dunque una verifica della firma, che la chiave privata usata per firmare il voucher corrisponda a quella pubblica appena ottenuta.
Verifiche sul payload
Quelli che interessano ai fini della verifica sono:
iss
: l'issuer del voucher, che deve rappresentare il dominio corrispondente all'authorization server di PDND Interoperabilità che ha rilasciato il voucher stesso (ad esempio, l'issuer di produzione èinterop.pagopa.it
)exp
: la scadenza del voucheraud
: l'audience, ossia l'indicazione di quale servizio dell'erogatore il fruitore intenda consumare con il voucher
La strategia di definizione dell'audience all'atto della creazione di una versione di un e-service dipende dai processi interni e dalla strategia di implementazione dei servizi dell'ente. Virtualmente, è possibile che ogni versione di ogni e-service abbia un'audience diversa, che solo alcune versioni dello stesso e-service abbiano la stessa audience, che diversi e-service abbiano la stessa audience.
In ultimo, il parametro purposeId
dà il riferimento della finalità per la quale il fruitore fa richiesta all'erogatore. Attraverso successive chiamate all'API gateway di PDND Interoperabilità è possibile richiedere tutte le informazioni di contesto, in caso siano necessarie (ossia i client associati, la richiesta di fruizione e l'e-service di riferimento, ecc).
Trasmettere e tracciare dati complementari alla richiesta
Per l'erogatore può essere necessario ottenere informazioni aggiuntive che non fanno parte dei campi standard previsti da PDND Interoperabilità all'interno della client assertion. Un esempio può essere l'indirizzo IP del chiamante, oppure informazioni relative all'operatore che sta effettuando la richiesta.
Il meccanismo messo a punto permette all'erogatore di verificare che il fruitore abbia depositato una traccia su PDND Interoperabilità, che agisce da notaio in questo processo. Allo stesso tempo, le informazioni sono scambiate direttamente tra il fruitore e l'erogatore, senza che PDND Interoperabilità ne sia a conoscenza.
Per maggiori informazioni rimandiamo alla documentazione di AgID sui pattern di sicurezza.
Per poter vedere il "Documento operativo - Pattern di sicurezza" e relativi esempi contenuti nell'allegato "Linee guida sull’interoperabilità tecnica delle Pubbliche Amministrazioni" è necessario che lo stesso venga scaricato sul proprio pc e aperto con l'applicazione Adobe Acrobat. In questo modo si avrà un indice di tutti gli allegati contenuti nel document come da screen.
Il flusso in breve
Dal punto di vista tecnico, l'interazione si articola in alcuni passaggi, che sono di fatto una variazione sul tema rispetto al flusso standard di richiesta e verifica di un voucher, descritto nelle sezioni sopra. In particolare:
il fruitore impacchetta le informazioni complementari: il fruitore costruisce un
JWS
secondo la specifica definita nell'RFC7519 inserendo nell'header ilkid
di una chiave pubblica depositata su PDND Interoperabilità. Con la chiave privata corrispondente a quella pubblica firmerà questoJWS
. Nel corpo (payload) delJWS
inserisce le informazioni complementari da inviare all'erogatore;il fruitore calcola l'hash: il fruitore calcola un hash non reversibile a partire dal
JWS
secondo l'algoritmo di hashingSHA256
;il fruitore costruisce la client assertion: il fruitore inserisce l'hash nel campo
digest
nella client assertion, per il resto compilata come nel flusso standard;il fruitore richiede un voucher a PDND Interoperabilità: il fruitore inoltra a PDND Interoperabilità la richiesta per ottenere un voucher sulla base della client assertion, come da flusso standard;
PDND Interoperabilità restituisce un voucher al fruitore: PDND Interoperabilità rilascia un voucher al fruitore come nel flusso standard. Inoltre, il voucher rilasciato riporta al suo interno anche l'hash che il fruitore ha inserito nel campo
digest
client assertion inviata nella richiesta;il fruitore fa una richiesta di dati all'erogatore: il fruitore invia quindi una richiesta all'erogatore, inserendo il voucher di PDND Interoperabilità nell'header autorizzativo
Authorization
secondo il flusso standard. Nella stessa chiamata, inserisce in un secondo header chiamatoAgid-JWT-TrackingEvidence
ilJWS
con le informazioni complementari;l'erogatore effettua le verifiche standard: l'erogatore estrae il voucher rilasciato da PDND Interoperabilità dall'header
Authorization
ed effettua le sue verifiche, come da flusso standard;l'erogatore effettua verifiche aggiuntive sul JWS: l'erogatore estrae il
JWS
dall'headerAgid-JWT-TrackingEvidence
. Verifica quindi sulle API di PDND Interoperabilità che la chiave pubblica corrispondente alkid
inserito nell'header delJWS
sia effettivamente depositata su PDND Interoperabilità e che ilJWS
sia stato firmato con la chiave privata corrispondente;l'erogatore calcola e confronta l'hash: l'erogatore effettua la stessa operazione effettuata dal fruitore al punto 2. e calcola l'hash del
JWS
. Lo confronta quindi con quello inserito all'interno del voucher rilasciato da PDND Interoperabilità.
I due hash dovranno essere uguali, segno che quello che il fruitore ha dichiarato di aver trasmesso su PDND Interoperabilità è effettivamente ciò che l'erogatore (e lui solo) trova nelle informazioni complementari. Di seguito i passaggi nel dettaglio.
NB: è fatto divieto di inserire all'interno delle issue Github pubbliche le proprie client assertion o qualsiasi altro materiale di natura potenzialmente riservata o sensibile. Le issue che contengono informazioni potenzialmente sensibili potranno essere rimosse senza preavviso dagli amministratori.
Il flusso in dettaglio
1. Il fruitore impacchetta le informazioni complementari
Un JWS
di esempio può avere header
Il payload sarà invece costituito dalle informazioni aggiuntive che il fruitore vuole trasmettere all'erogatore. Il fruitore firma quindi il JWS
con una chiave privata. La chiave pubblica corrispondente deve essere depositata su PDND Interoperabilità e avere per kid
quello inserito nell'header del JWS
.
NB: questa chiave e questo kid
non devono necessariamente essere gli stessi con i quali si firma la client assertion al passaggio 3.
2. Il fruitore calcola l'hash
A partire dalla codifica del JWS
(ossia il JWS
codificato secondo l'algoritmo inserito nell'header, in genere inizia per ey
) il fruitore applica l'algoritmo di hashing SHA256
al JWS
, ottenendone un hash non reversibile a lunghezza fissa.
A scopo esemplificativo, è possibile inserire in un terminale il seguente comando, previa installazione del pacchetto openssl
per ottenere l'hash del JWS
. Ad esempio, a fronte del JWS esempio con codifica
si ottiene l'hash a lunghezza fissa
NB: la flag -n
che viene passata nel primo comando indica che vengano rimosse eventuali "newline" non viste dall'operatore. Un'eventuale "newline" presente nel token fa cambiare il valore dell'hash che poi non corrisponderà all'atto della verifica dell'erogatore.
3. Il fruitore costruisce la client assertion
Il fruitore prende quindi l'hash appena ottenuto, e lo inserisce nel payload della client assertion nel campo digest.value
. Il campo digest.alg
per adesso accetta solamente il valore SHA256
(corrispondente all'algoritmo di hashing che è stato applicato al JWS
).
L'header della client assertion rimane invece invariato rispetto al flusso standard. La client assertion andrà firmata con la chiave privata corrispondente alla pubblica caricata su PDND Interoperabilità il quale kid
è nell'header di questa asserzione.
4. Il fruitore richiede un voucher a PDND Interoperabilità
In questo passaggio non ci sono variazioni rispetto al flusso standard. Il fruitore fa una richiesta di voucher al server autorizzativo di PDND Interoperabilità inviando la client assertion.
5. PDND Interoperabilità restituisce un voucher al fruitore
Anche in questo passaggio non ci sono variazioni. Da notare che l'unica verifica che PDND Interoperabilità effettua sul campo digest è che rispetti la lunghezza della stringa prevista da SHA256
(64 caratteri).
6. Il fruitore fa una richiesta di dati all'erogatore
In questo passaggio, il fruitore costruisce una richiesta verso il servizio dell'erogatore secondo quanto descritto nel file di interfaccia e nella documentazione tecnica a corredo fornita dall'erogatore attraverso PDND Interoperabilità.
Nella richiesta inserirà l'header standard Authorization
che conterrà come Bearer
token il voucher rilasciato da PDND Interoperabilità allo step precedente. Inoltre, il fruitore inserirà il JWS
che contiene le informazioni complementari all'interno di un secondo header denominato Agid-JWT-TrackingEvidence
.
7. L'erogatore effettua le verifiche standard
Per questo passaggio, si veda la sezione dedicata nel flusso standard.
8. L'erogatore effettua le verifiche aggiuntive sul JWS
Dopo aver completato le verifiche standard, l'erogatore può effettuare le verifiche aggiuntive necessarie a garantire l'attendibilità delle informazioni complementari inserite nel JWS
aggiuntivo.
Quindi verificherà l'autenticità e la validità della chiave privata con la quale è firmato il JWS. Per farlo:
si autentica sulle API di Interoperabilità come descritto nel flusso dedicato;
effettua una chiamata
keys/{kid}
dove kid è valorizzato con il kid inserito nell'header delJWS
;ottiene da PDND Interoperabilità una chiave pubblica in risposta all'interno del campo
n
;verifica la firma del
JWS
, effettuata dal fruitore con la chiave privata, con la chiave pubblica appena ottenuta.
Se l'erogatore ottiene un errore con status code 404 - Not found
, significa che la chiave non è presente su PDND Interoperabilità e dunque la richiesta è da ritenersi inattendibile.
9. L'erogatore calcola e confronta l'hash
Se la chiave è presente e corrisponde, può procedere ad una seconda verifica, ossia quella notarile. In pratica, verifica che la traccia depositata su PDND Interoperabilità corrisponda a quella inserita all'interno del voucher rilasciato da PDND Interoperabilità.
Se c'è corrispondenza, vuol dire che le informazioni complementari inserite all'interno del JWS sono effettivamente quelle che il fruitore ha dichiarato su PDND Interoperabilità di aver inserito.
Per farlo, l'erogatore prende il JWS ed effettua la stessa operazione effettuata dal fruitore nel secondo passaggio. Ottiene quindi l'hash non reversibile del JWS.
L'erogatore confronta quindi questo hash con quello presente all'interno del campo digest.value
del voucher rilasciato da PDND Interoperabilità presente nell'header Authorization
. Se i due hash sono uguali, il fruitore ha reso una dichiarazione che corrisponde ed è tracciata su PDND Interoperabilità.
Indicazioni architetturali
Il meccanismo descritto al passaggio 8 della guida nella sezione precedente ha un limite: per ogni verifica di voucher, sarebbe necessario ottenere la chiave pubblica corrispondente al kid
da PDND Interoperabilità, con evidenti ripercussioni sulle performance e sostenibilità del sistema.
Per ovviare, suggeriamo agli erogatori che sfruttano questa funzionalità di dotarsi di una cache nella quale conservare una copia delle chiavi pubbliche e dei relativi kid
.
Per supportare gli erogatori in quest'attività, PDND Interoperabilità mette a disposizione un secondo endpoint sulle proprie API:/events/keys
.
Come funzionano gli eventi sulle chiavi?
Gli eventi sulle chiavi hanno lo stesso meccanismo di funzionamento di quello degli altri eventi, descritto nella sezione dedicata. In sostanza, è possibile chiamare l'endpoint /events/keys
passando come parametri lastEventId
(richiesto) e limit
(opzionale, default a 100). Il primo indica l'id dell'ultimo evento scaricato, il secondo quanti eventi si intende scaricare.
Ogni risultato ha la seguente struttura:
NB: L'endpoint deve essere chiamato dall'aderente in modalità polling.
Un esempio di logica di business per l'implementazione della cache
Utilizzando l'endpoint /events/keys
è possibile chiamare puntualmente l'endpoint /keys/{kid}
per recuperare tutte le chiavi nuove che vengono aggiunte (quelle con eventType == "ADDED"
).
Quelle chiavi per le quali l'evento è DELETED
vanno rimosse dalla propria cache. Se si vuole ottenere una controprova, anche qui è possibile chiamare l'endpoint /keys/{kid}
con il kid
della chiave che si è riscontrato essere stata cancellata, e il sistema restituirà uno status code 404 - Not Found
.
Dove viene indicato se sono richieste informazioni aggiuntive?
La richiesta e il dettaglio implementativo delle informazioni aggiuntive dovranno essere esplicitate dall'erogatore all'interno della documentazione tecnica a corredo dell'e-service.
Garanzia dell'integrità della risposta
È possibile per gli erogatori mettere a disposizione dei fruitori un ulteriore livello di sicurezza, che garantisca l'integrità della risposta fornita.
In sostanza, gli erogatori firmano la propria risposta con una chiave privata, la cui corrispondente chiave pubblica è depositata su PDND Interoperabilità per le dovute verifiche.
Il meccanismo di caricamento e gestione delle chiavi è analogo a quello dei client, ed è disponibile nella sezione Erogazione > I tuoi portachiavi. Si rimanda alla sezione dedicata per tutte le operazioni legate alla gestione di un portachiavi.
Precondizioni
l'erogatore deve avere creato un portachiavi;
deve averlo associato ad un e-service attraverso la tab "Portachiavi" disponibile all'interno della scheda e-service (Erogazione > I tuoi e-service);
un suo operatore di sicurezza o amministratore deve aver caricato almeno una chiave pubblica all'interno di quel portachiavi.
Sarà quindi possibile per l'erogatore firmare la propria risposta con la propria chiave privata. Il fruitore potrà verificare la corrispondente chiave pubblica depositata su PDND Interoperabilità.
Il ModI lascia discrezione all'erogatore nell'indicare qual debba essere la procedura corretta di firma del payload e verifica da parte del fruitore. Per questa ragione, PDND Interoperabilità non impone alcun vincolo in questo senso ad accezione dell'utilizzo di chiavi asimmetriche di tipo RSA in conformità con gli standard di sicurezza già adottati.
Di seguito riportiamo a titolo di esempio una possibile gestione del meccanismo di firma del payload di risposta di un e-service.
Definizione della struttura della risposta
Questa sezione descrive come firmare un payload di risposta HTTP utilizzando RSA, per garantire che i dati provenienti da un e-service e non siano stati modificati.
La risposta JSON che l'erogatore invia al fruitore sarà strutturata come segue:
dove
data: contiene il payload, ossia i dati effettivi che l'e-service trasmette verso i fruitori;
signature: contiene la firma digitale del campo data, calcolata dall'e-service utilizzando una chiave privata RSA (appartenente ad un portachiavi all'e-service) e codificata in formato base64;
kid: identificatore della chiave usata per la firma; consente al fruitore di sapere quale chiave pubblica utilizzare per verificare la firma.
Erogatore: firmare una risposta
Creazione dell'hash
Il contenuto del campo data viene convertito in una stringa di byte e sottoposto a una funzione di hash utilizzando un algoritmo come SHA-256.
Firma dell'hash
L’hash calcolato è poi firmato utilizzando una delle chiavi RSA, identificata univocamente tramite kid, appartenente ad un portachiavi associato all'e-service. La firma garantisce che solo chi possiede la chiave privata corrispondente a kid (erogatore) possa generare la firma specifica per quel contenuto. Il kid della chiave pubblica che si è caricata è disponibile all'interno del portachiavi, aprendo la pagina relativa alla singola chiave (Erogazione > I tuoi portachiavi, clicchi sul portachiavi di interesse, tab Chiavi pubbliche, e clicchi sulla chiave di tuo interesse).
Integrazione della firma nella risposta
La firma è codificata in base64 e inclusa nel payload JSON nel campo signature, mentre kid identifica univocamente la chiave usata, per consentire al fruitore di selezionare la chiave pubblica corretta per la verifica.
Fruitore: verifica di una firma
Il fruitore può verificare l’autenticità e l’integrità dei dati ricevuti nella risposta utilizzando la chiave pubblica RSA associata all'e-service. All'interno della risposta troverà il campo kid, che identifica la chiave pubblica corrispondente alla privata che l'erogatore ha utilizzato per firmare la risposta.
Identificazione della chiave corretta
La chiave corrispondente al kid è disponibile sulle API esposte da PDND Interoperabilità al path GET /keys/{kid}
, dal quale otterrà la chiave pubblica corrispondente al kid in formato JWK.
Ricalcolo dell'hash
Il fruitore calcola l’hash del contenuto di data usando lo stesso algoritmo utilizzato dell'erogatore (SHA-256).
Verifica della firma
Con la chiave pubblica individuata tramite kid, il fruitore verifica che la firma (campo signature) sia valida rispetto all’hash calcolato. Se corrispondono, il payload è autentico e integro; in caso contrario, potrebbe essere stato alterato o non provenire dall'e-service.
Sicurezza e standard di riferimento
Il processo di firma e verifica segue specifiche internazionali che garantiscono la sicurezza. Gli standard di riferimento includono:
RFC 8017 (PKCS #1): definisce le modalità di utilizzo dell’algoritmo RSA per la firma digitale;
RFC 7518 (JSON Web Algorithms): specifica gli algoritmi di firma, come RSA e SHA-256;
RFC 7517 (JSON Web Key — JWK): questo documento specifica il formato JSON per rappresentare le chiavi crittografiche, sia pubbliche che private.
Last updated