UNIVERSITA’ POLITECNICA DELLE MARCHE FACOLTA’ DI INGEGNERIA Corso di Laurea in Ingegneria Elettronica Dipartimento di Elettronica, Intelligenza Artificiale e Telecomunicazioni UN’ ARCHITETTURA SOFTWARE COMPLETA PER TELECONSULTO E TELEREFERTAZIONE: PROGETTO DI APPLICAZIONI SICURE LATO CLIENT Tesi di Laurea di: Roberto Di Rosa Relatore: Prof. Aldo Franco Dragoni Correlatoei: Prof. Paolo Puliti Anno Accademico 2004-2005 INDICE Introduzione .......................................................................... Capitolo 1. Telemedicina ...................................................... 1 1.1 Introduzione ................................................................... 2 1.2 Applicazioni .................................................................... 4 1.2.1 Vantaggi ................................................................... 5 1.2.2 Problemi.................................................................... 5 1.3 Stato dell’Arte................................................................. 6 1.4 Progetti dell’ ASUR Marche zona 7 ..................................... 8 1.4.1 Sistema di teleconsulto ospedale di Osimo con stazioni fisse e stazioni mobili .................................................. 8 1.4.2 Assistente Virtuale .................................................... 11 1.4.3 Delocalizzazione della Refertazione Diagnostica............. 12 Capitolo 2. Progetto MiRo................................................... 14 2.1 Finalità ........................................................................ 15 2.2 Architettura generale ..................................................... 17 2.2.1 Repository ............................................................... 19 2.2.2 Laboratorio .............................................................. 21 2.2.3 Medico .................................................................... 22 2.3 Soluzioni tecniche.......................................................... 26 2.3.1 Comunicazione sicura................................................ 27 2.3.2 Messaggi SOAP ........................................................ 29 -I- Capitolo 3. Mutuo Riconoscimento e Interfacce Grafiche....30 3.1 Laboratorio................................................................... 31 3.1.1 Web Service laboratorio............................................. 31 3.1.2 Mutuo Riconoscimento............................................... 32 3.1.3 Interfacce grafiche.................................................... 32 3.2 Medico ......................................................................... 36 3.2.1 Mutuo Riconoscimento............................................... 36 3.2.2 Interfacce grafiche.................................................... 37 3.2.3 Firma del referto ...................................................... 39 Capitolo 4. Tecnologie Software ......................................... 42 4.1 Java Platform................................................................ 43 4.1.1 Java Server Pages .................................................... 46 4.2 Apache Tomcat ............................................................. 57 4.2.1 Comunicazione SSL................................................... 71 4.3 Strumenti di Sviluppo ..................................................... 84 4.3.1 Apache Struts .......................................................... 84 Capitolo 5. Crittografia A Chiave Pubblica .......................... 89 5.1 Crittografia ................................................................... 91 5.1.1 Introduzione ............................................................ 91 5.1.2 Crittografia a chiave simmetrica ................................. 94 5.1.3 Sistemi a crittografia a chiave pubblica ........................ 96 5.2 La Firma Digitale ........................................................... 99 5.2.1 Algoritmo RSA........................................................ 104 5.2.2 Algoritmo SHA-1 .................................................... 106 5.3 Certificati elettronici..................................................... 108 5.3.1 Standard X.509 per i certificati ................................. 110 5.3.2 Necessità di coppie di chiavi distinte per cifratura e firma.................................................................. 112 5.4 Infrastruttura a chiave pubblica (PKI)............................. 117 5.4.1 Requisiti di una PKI................................................. 121 5.5 Gestione dei certificati.................................................. 123 5.5.1 Ciclo di vita dei certificati ......................................... 123 5.5.2 La gestione delle chiavi ........................................... 125 5.5.3 La revoca di un certificato ........................................ 126 5.5.4 I cammini di certificazione ....................................... 130 - II - Capitolo 6. Conclusioni..................................................... 133 Appendice A. Codice sorgente .......................................... 146 A.1 Client Application ........................................................ 148 A.2 Web Service Client. ..................................................... 178 A.3 Common Library...........................................................207 Bibliografia ...................................................................... 224 - III - INTRODUZIONE Con il continuo sviluppo tecnologico con l’ avvento dei Digital Data e con la larga banda si sente forte la necessità di ristrutturare l’intero sistema sanitario nazionale spingendosi verso le nuove frontiere della Telemedicina. Il progetto MiRo, nasce, proprio da questo bisogno e dalla collaborazione tra ASUR Marche zona 7 di Ancona e l’ Università Politecnica delle Marche. Il primo passo fatto è stato analizzare il significato del termine Telemedicina, realizzando uno stato dell’ arte del sistema sanitario nazionale. Successivamente si è approfondito uno dei settori di maggior interesse (la teleradiologia) pensando e producendo un architettura software “flessibile” completa per teleconsulto e teleassistenza. Nei prossimi telemedicina, capitoli facendo si inquadrerà particolare il concetto riferimento di alle problematiche delle aziende sanitarie locali (in particolare della Asur di Ancona); successivamente si passerà ad una descrizione del progetto MiRo e del suo funzionamento in maniera dettagliata . - IV - CAP. 1 - TELEMEDICINA CAPITOLO 1 TELEMEDICINA Introduzione al concetto di telemedicina, stato dell’ arte della situazione sanitaria italiana e approfondimento dei progetti dell’ ASUR Marche zona 7. 1.1 Introduzione 1.2 Applicazioni 1.2.1 Vantaggi 1.2.2 Problemi 1.3 Stato dell’Arte 1.4 Progetti dell’ASUR 1.4.1 Sistema di teleconsulto ospedale di Osimo con stazioni fisse e stazioni mobili 1.4.2 Assistente Virtuale 1.4.3 Delocalizzazione della Refertazione Diagnostica -1- CAP. 1 - TELEMEDICINA 1.1 INTRODUZIONE Una definizione della Telemedicina che ha riscosso un notevole successo è la seguente: la Telemedicina rappresenta l’erogazione di servizi sanitari, laddove la distanza rappresenti professionisti un fattore nell’assistenza critico sanitaria da che parte di utilizzino tecnologie dell’informazione e della comunicazione per lo scambio di trattamento informazioni e la rilevanti, prevenzione l’educazione continuativa nell’interesse del per delle degli miglioramento la diagnosi, patologie e operatori della il per sanitari, salute e delle comunità assistite. La Telemedicina telecomunicazione per utilizza erogare le tecnologie assistenza della sanitaria specialistica, spesso a notevoli distanze, con la possibilità di contenere i costi delle prestazioni. Questo avviene in special modo quando l’Assistenza Sanitaria è rivolta ad aree isolate o comunque dove non sia disponibile direttamente la prestazione specialistica del medico. La Telemedicina consente di conseguire notevoli benefici sia per erogatori di assistenza remota, sia naturalmente, per gli utenti dei servizi, siano essi pazienti o operatori sanitari. -2- CAP. 1 - TELEMEDICINA Lo sviluppo e l’applicazione della Telemedicina richiede significativi expertise nei settori delle telecomunicazioni, delle erogazioni dei servizi sanitari e dell’information tecnology and comunications. L’applicazione della Telemedicina ben si presta ad un opera di cooperazione fra i paesi dotati di maggiori tecnologie e quelli ancora in via di sviluppo. Fig. 1.1 – Telemedicina -3- CAP. 1 - TELEMEDICINA 1.2 APPLICAZIONI Il concetto di telemedicina non significa dunque soltanto “medicina praticata a distanza“, ma include tutti quegli aspetti legati all’ assistenza sanitaria in senso lato. Una serie di nuovi concetti sono ora riferiti alla telemedicina; due di questi sono e-health e tele-health(tele assistenza). La telemedicina/teleassistenza può essere applicata in vari modi in tutti i servizi sanitari; può facilitare e razionalizzare la comunicazione e lo scambio di informazioni: tra servizi di primaria assistenza e gli ospedali , tra ospedali diversi, tra ospedali e i laboratori, tra i servizi di riabilitazione telemedicina e le riguarda organizzazioni anche la paramediche. direzione La e l’amministrazione dell’unità sanitarie locali, i pazienti e i loro familiari. 1.2.1 Vantaggi L’ avvento dell’ informatica medica e la conseguente introduzione di nuove tecnologie nell’ambito sanitario hanno portato e porteranno notevoli vantaggi sia per i pazienti che per il personale medico. L’ introduzione della telemedicina permette : -4- CAP. 1 - TELEMEDICINA • Diagnosi e cure più rapide • Minor numero di spostamenti sia del personale medico che dei pazienti • Riduzione dei costi per personale (compreso quello di emergenza) • Comunicazioni più veloci • Aggiornamento più semplice e rapido delle informazioni riguardanti diagnosi e metodi di cura • Miglior sostegno allo staff medico per la formazione sia teorica che pratica 1.2.2 Problemi In passato i costi per l’attrezzatura e le telecomunicazioni sono stati troppo alti da permetterne un introduzione su larga scala, oggi tuttavia la situazione sotto questo punto di vista è notevolmente migliorata il vero ostacolo da affrontare è cercare di introdurre metodi che favoriscano l’ accettazione e la comprensione da parte del personale interessato, delle applicazioni di telemedicina/teleassistenza nei servizi sanitari. Bisogna sottolineare che l’introduzione della telemedicina influenza i tradizionali metodi di lavoro, si vanno a modificare i modelli di cooperazione, si creano nuove figure lavorative e molto spesso sorgono problemi di distribuzione dei costi e di investimenti delle risorse. -5- CAP. 1 - TELEMEDICINA A livello mancanza internazionale, di uniformità ad oggi, si uso del sull’ registra concetto una di telemedicina; tutto ciò rende impossibile la creazione di standard che possano regolamentare tale disciplina. 1.3 STATO DELL’ARTE Oggigiorno i sistemi sanitari mondiali stanno facendo passi da gigante nell’utilizzo massiccio della telemedicina, grazie soprattutto allo sviluppo di nuove tecnologie. In ambito nazionale ogni regione si sta attrezzando nello sviluppo di sistemi di informatica medica . Uno dei principali progetti a livello nazionale è rappresentato da As.ter: un sistema informativo-informatico per la gestione di tutte le attività del territorio, sviluppato dalla USL 11 di Empoli. As.Ter integra, con l’ausilio di una piattaforma tecnologica innovativa, tutti gli applicativi ed i database delle attività sanitarie e sociali svolte sul territorio, consentendo la rilevazione dei bisogni complessivi dei cittadini, l’individuazione delle modalità e dei tempi di risposta ai loro bisogni, la rilevazione dei costi; tutto ciò al fine di pianificare e gestire le attività sociosanitarie sul territorio in un ottica manageriale di costi/benefici. Un altro progetto è stato proposto dal servizio sanitario regionale emilia-romagna con il nome di SOLE . Esso è finalizzato a realizzare una rete telematica di collegamento -6- CAP. 1 - TELEMEDICINA tra i servizi ospedalieri e i servizi territoriali per agevolare la comunicazione tra operatori sanitari e, di conseguenza, agevolare l'erogazione dei servizi con importanti e positive ricadute sulla continuità assistenziale e sulla semplificazione dell’accesso ai servizi per il cittadino. -7- CAP. 1 - TELEMEDICINA 1.4 PROGETTI DELL’ASUR MARCHE ZONA 7 L’ ASUR della regione Marche zona 7 ha sviluppato una serie di progetti che riguardano l’ ambito medico tra i quali troviamo: • Sistema di Teleconsulto ospedale di Osimo con stazioni fisse e stazioni mobili • Assistente Virtuale • Delocalizzazione della refertazione diagnostica 1.4.1 Sistema Osimo di con teleconsulto stazioni ospedale fisse e di stazioni mobili La Telelettrocardiografia è stata la prima applicazione derivate dalle esperienze aerospaziali sia americane che russe. Lo sviluppo enorme in tutti i paesi industrializzati della telefonia cellulare consente di erogare questo tipo di servizio di Telerefertazione con una eccezionale capillarità territoriale. Il sistema è composto da un server situato presso l’Ospedale di Osimo, le caratteristiche del server sono le -8- CAP. 1 - TELEMEDICINA seguenti: Server Compaq Proliant ML 370 T, P3 1266 GB, Cache 256 K ram 256 MB, 3 Hard Disk da 18 GB, il software permette la connessione fino a 25 apparecchiature in rete Ethernet. E’ inoltre composto da un client situato presso l’Ospedale di Osimo, le caratteristiche del client sono: Compaq EVO D500 SFFP 4 – 1.5 GB Hard Disk da 20GB, schede di rete CD Rom 48 per monitor 17 e stampante laser HP 1200 e da tre stazioni fisse composte da tre elettrocardiografi del tipo Archimed Base Plus il quale consiste in un elettrocardiografo portatile per l’acquisizione contemporanea di dodici derivazioni dotate di monitor Lcd retroilluminato ad alta risoluzione di grandi dimensioni per la visualizzazione di sei tracce Ecg, tastiera alfanumerica, Driver per floppy da 3,5 e hard disk da un giga per la memorizzazione dei tracciati. L’elettrocardiografo è provvisto di varie uscite esterne, tra cui quattro seriali RS 232, una rete Ethernet per trasmissione e ricezione di tracciati ecg con relativo referto e stampante Archimed 4240 termica formato A4. Inoltre di una stazione mobile composta da una valigetta AT 4 Gsm che è una 24 ore completa di elettrocardiografo portatile computerizzata, dotato di Display Lcd di grandi dimensioni che permette di visualizzare contemporaneamente tre tracce ecg oltre una stampante per l’acquisizione contemporanea di dodici derivazioni ecg in modalità automatica, l’apparecchio è dotato di memoria interna per l’archiviazione di quaranta -9- CAP. 1 - TELEMEDICINA ecg (10 secondi 12 derivazioni) fornita di cellulare Gsm Dual Band con interfaccia per elettrocardiografo e cavi di connessione per la trasmissione digitale dell’ecg alla Centrale di Ascolto, funzione di viva voce per comunicare con la Centrale di Ascolto. Il Client è posto nella zona di controllo della Unità di Terapia Critica del Dipartimento di Medicina Interna ove un operatore è costantemente presente durante le ore del giorno e della notte. Le tre stazioni fisse sono al momento situate presso le Unità di degenza mediche del Presidio Ospedaliero di Osimo ed una presso i Poliambulatori di Camerano situati all’interno di una struttura residenziale per anziani con circa 80 degenti. La stazione mobile viene al momento affidata agli operatori della Emergenza Urgenza Territoriale che fanno capo al Distretto Ancona Sud. Fig. 1.2 – Telemedicina - 10 - CAP. 1 - TELEMEDICINA 1.4.2 Assistente Virtuale E’ un progetto ancora in fase embrionale che ha come obbiettivo quello di creare un assistente personale virtuale che sia in grado di offrire ad ogni singolo utente un valido appoggio in riferimento alla propria situazione clinica. Fig. 1.3 – Interfaccia dell’assistente virtuale - 11 - CAP. 1 - TELEMEDICINA 1.4.3 Delocalizzazione della Refertazione Diagnostica Nell’ottica di capillarizzare l’offerta sanitaria così come negli obiettivi della sanità regionale, può essere importante pensare ad indipendenti una le struttura due fasi informatica tipiche della che renda diagnostica: l’esecuzione dell’esame e la sua refertazione. Infatti mentre è possibile pensare di localizzare alcuni macchinari (di basso costo) anche nei poliambulatori più piccoli, può risultare sconveniente portare in questi poliambulatori personale di alta specializzazione . La struttura informatica che ci si propone di realizzare in questa fase, poi potrà anche servire per altri scopi come consulti medici oppure per fornire servizi (a pagamento) di refertazione per altri enti. Fig. 1.4 – Delocalizzazione diagnostica - 12 - CAP. 1 - TELEMEDICINA Sostanzialmente un sistema per la refertazione a distanza altro non è che un repository di eventi sanitari con relativo output dell’esame (ad esempio una radiografia, un elettrocardiogramma, ……). In questo repository gli enti periferici (poliambulatori etc.) depositano gli output degli esami e prelevano i referti mentre il centro di refertazione preleva gli output degli esami ed inserisce i referti relativi. Per mettere in piedi la procedura appena descritta è indispensabile: • Che tutti i partecipanti siano collegati ad una rete intranet o internet • Essere collegati ad una infrastruttura PKI funzionante • Utilizzare un metodo di trasferimento sicuro (SSL) La nostra tesi nasce proprio come sviluppo a livello software del progetto di delocalizzazione . Nei prossimi capitoli si approfondirà il progetto MiRo, sottolineando la sua grande flessibilità ed efficacia per i sistemi di teleconsulto e telerefertazione. - 13 - CAP. 2 – PROGETTO MIRO CAPITOLO 2 PROGETTO MIRO Descrizione dell’ architettura del Progetto MiRo, spiegazione del funzionamento delle varie parti del sistema: repository, medico e laboratorio. Soluzioni tecniche adottate. 2.1 Finalità 2.2 Architettura Generale 2.2.1 Repository 2.2.2 Laboratorio 2.2.3 Medico 2.3 Soluzioni Tecniche 2.3.1 Comunicazione sicura 2.3.2 Messaggi SOAP - 14 - CAP. 2 – PROGETTO MIRO 2.1 FINALITÀ Il progetto MiRo nasce dalla collaborazione tra l’Università Politecnica delle Marche con l’ azienda ASUR Marche zona 7 di Ancona, come sviluppo del progetto che l’ASUR aveva sulla delocalizzazione della refertazione diagnostica. L’ obbiettivo del nostro progetto era quello fornire all’azienda sanitaria locale, un prodotto flessibile che implementasse un servizio di refertazione asincrono; cioè in grado di separare non solo da un punto di vista temporale, ma anche da quello spaziale, l’ esecuzione dell’ esame dalla refertazione dello stesso. Fig. 2.1 – Contesto di lavoro - 15 - CAP. 2 – PROGETTO MIRO Tutto ciò doveva essere fatto garantendo un livello di sicurezza e di affidabilità, del trattamento del dato clinico, accettabile. Lo scopo di MiRo è stato creare un applicazione software completa in grado di gestire l’ intero telerefertazione e di teleconsulto. Fig. 2.2 – Introduzione di Miro - 16 - processo di CAP. 2 – PROGETTO MIRO 2.2 ARCHITETTURA GENERALE Il primo passo fatto nel progettare MiRo (così si chiama il progetto) è stato cercare di capire come si dovesse integrare con il sistema informativo sanitario già esistente; la prima ipotesi semplificativa adottata è stata quella di creare una piattaforma flessibile per lo sviluppo di servizi sanitari che avesse un’ architettura orientata ai servizi. Si è pensato di creare una struttura semplice, basata su standard diffusi e riconosciuti, come Internet, in modo da creare le basi per facilitare le applicazioni sia in ambito sanitario a livello di personale medico che in futuro direttamente ai cittadini. MiRo si basa sul concetto di evento, questo aspetto rappresenta il punto focale dell’ intero sistema. L’ effettuazione di un esame presso un laboratorio (qualsiasi, di una qualunque struttura ospedaliera) e la conseguente archiviazione del dato in forma digitale genera ciò che viene chiamato evento. L’ evento non è il dato clinico vero e proprio ma rappresenta una sorta di meta-dato-clinico del dato digitale prelevato in laboratorio. Esso consiste in una serie di informazioni che riguardano il dato digitale prodotto dall’ evento come ad esempio: l’ unità erogante il dato, la data e l’ ora dell’esame, la struttura che l’ha prodotto, il codice - 17 - CAP. 2 – PROGETTO MIRO impegnativa, il link da dove si può scaricare il dato, lo stato ecc…. Ogni evento quindi viene immagazzinato all’ interno di un apposito raccoglitore, definito repository, che costituisce il cuore del sistema e che contiene la definizione del legame tra evento ed esame. Qualsiasi esame che può essere memorizzato in forma digitale può essere associato ad un evento, in questo modo siamo riusciti a dare a MiRo una certa flessibilità ed adattabilità che venivano richieste in fase di progettazione. In figura è mostrata l’architettura generale di MiRo dove si notano i tre attori principali del sistema: • Repository • Laboratories • Doctors Fig. 2.3 –Architettura generale di MiRO - 18 - CAP. 2 – PROGETTO MIRO Il cuore del sistema è rappresentato dal Repository centrale offerto da un azienda o da un ente erogante (vedi ASUR), il quale si impegna nel corretto funzionamento dell’ intero sistema di refertazione e nella manutenzione dello stesso. A destra del repository troviamo il laboratorio, cioè la struttura che usufruisce del servizio per generare gli eventi dovuti all’ acquisizione dei dati digitali di un esame clinico. A sinistra del repository si trova colui che scrive il referto ossia il medico o un equipe di medici specializzati, i quali pubblicano il proprio referto autenticandolo con la propria firma digitale. 2.2.1 Repository La struttura del sistema prevede la presenza di un repository centrale, altro non è che un database relazionale nel quale vengono memorizzati le informazioni relativi ai dati digitali prodotti nei laboratori. Un record di questo repository costituisce quello che noi chiamiamo “evento” Fig. 2.4; l’ evento rappresenta una sorta di meta-dato informativo riguardante il dato digitale prodotto nei laboratori . - 19 - CAP. 2 – PROGETTO MIRO Fig. 2.4 – Struttura Dati Le informazioni che vengono registrate sono data e ora dell’inserimento nella base dati, il tipo di esame eseguito e il laboratorio in cui è stato effettuato, informazioni sulla provenienza della richiesta di prestazione sanitaria e il percorso (pathname) dove recuperare l’ esame o meglio il dato digitale riguardante l’ esame vero e proprio. Dopo lunghe fasi di consultazioni con l’ azienda sanitaria si è ritenuto opportuno organizzare il record in questa maniera, in modo da render semplici ed accessibili le informazioni principali riguardanti un esame. Da notare che non vengono mai registrate le informazioni relative al paziente, infatti il nostro primo obbiettivo è quello di fornire un servizio di refertazione che abbia la possibilità di concedere “second opinion”. Tuttavia non viene concesso un servizio di diagnosi, per il quale - 20 - CAP. 2 – PROGETTO MIRO sarebbe necessario un trattamento dei dati sensibili del paziente. Tutto ciò rende molto flessibile la struttura eliminando i problemi riguardanti la privacy. 2.2.2 Laboratorio La seconda figura che viene presa in esame è il laboratorio, con questo termine intendiamo qualsiasi struttura sanitaria abbia necessità di farsi refertare un esame; potrebbe essere un laboratorio radiologico, analisi, epidemiologico oppure un autoambulanza con apposita apparecchiatura, una piattaforma petrolifera (anche essa con i giusti strumenti)ecc…. Noi chiameremo, in tutti i nostri discorsi tutte queste unità con la definizione di laboratorio. Il laboratorio apre un evento cioè una volta effettuato un esame e ottenuto il dato digitale sia in maniera diretta o tramite passaggi di discretizzazione, si collega al web service dell’ ente fornitrice il servizio (nel nostro caso ASUR Marche zona 7) e tramite un opportuna interfaccia inserisce i dati relativi all’ esame fatto nel data base centrale. In questo modo il dato digitale presente nel laboratorio diventa accessibile e pronto per essere refertato da parte degli altri attori di questo sistema, i dottori o meglio il personale specializzato. Una volta generato l’ evento (scrittura nel database centrale), il laboratorio si pone in una posizione di - 21 - CAP. 2 – PROGETTO MIRO monitoraggio, ossia controlla tutte le varie fasi di refertazione che l’ evento da lui generato subisce, fino a che non ritiene soddisfatta la sua richiesta. A questo punto il laboratorio dichiara chiuso l’ evento così termina la fase di refertazione; solo chi ha inoltrato la richiesta può terminare il processo di refertazione, così il servizio è completo. Tutto ciò garantisce e rispetta in pieno uno degli obbiettivi principali del progetto MiRo la grande flessibilità e adattabilità per ogni tipo di struttura dei servizi sanitari. Fig. 2.5 – Flusso di operazioni Lab. vs Rep. 2.2.3 Medico Una volta che il laboratorio apre l’ evento e quindi va ad aggiungere un record al Repository centrale, effettua una richiesta di refertazione all’ altra figura che compare nell’ - 22 - CAP. 2 – PROGETTO MIRO architettura di MiRo: i medici o per meglio dire il personale specializzato. Ogni medico, effettuando l’ accesso tramite un semplice web browser (es Internet Explorer) accede al web service centrale dove tramite opportune interfacce user friendly verranno mostrati, solo gli eventi che ogni medico è in grado di refertare, questo perché in generale noi stiamo parlando di specialisti in particolari discipline mediche. Fig. 2.6 – Flusso di operazioni Doc. vs Rep. Ad ogni dottore viene visualizzata una form dove sono presenti data e ora di inserzione nel database, il laboratorio che l’ ha creata, lo stato dell’ esame( aperto, refertato, chiuso)e infine il download ossia la possibilità di scaricarsi il dato ottenuto in laboratorio sul proprio pc, per poi poter visualizzarlo e conseguentemente refertarlo. Quando il medico decide di refertare gli appare una form dove può scrivere il suo referto e contemporaneamente può leggere i referti emessi dagli altri medici, in questo modo si - 23 - CAP. 2 – PROGETTO MIRO viene a creare un meccanismo definito “second opinion” (uno degli obbiettivi di partenza del nostro lavoro). Un altro aspetto fondamentale che riguarda il medico è la firma digitale. Una volta scritto il referto il medico deve, tramite certificato digitale firmare il referto in modo da garantire a tutti gli operatori del sistema la propria identità e veriticità. Ciò se basta a confermare chi ha redatto il referto non da certezze su quando il referto sia stato fatto e firmato; quindi una volta scritto, il referto firmato deve essere spedito ad un web service esterno chiamato Time Stamp Service il quale sancirà in modo inequivocabile l’ istante temporale in cui il dottore ha sottomesso il referto al sistema. Il web service Time Stamp rimanderà al medico il referto con il Time Stamp e il medico lo spedirà al repository centrale che lo immagazzinerà. Fig. 2.7 – Flusso di operazioni. - 24 - CAP. 2 – PROGETTO MIRO In questo modo si offrono le garanzie necessarie sulla fase di refertazione. Fig. 2.8 – Fase di Refertazione. - 25 - CAP. 2 – PROGETTO MIRO 2.3 SOLUZIONI TECNICHE MiRo è un applicazione “object-oriented” basata su internet che fa uso di database relazionali per memorizzare i dati. Questa applicazione, basata su un modello client server viene eseguita all’interno di un web browser standard utilizzando la Java Virtual Machine, riducendo in maniera significativa i tempi e le difficoltà di configurazione dei client. Questo è un requisito fondamentale specialmente in ambiente remoto. MiRo è un sistema che si basa su di un’architettura orientata ai servizi, implementata attraverso Web Services, i quali stanno diventando uno standard “de facto” per quanto riguarda la distribuzione di servizi all’interno di un ambiente eterogeneo offrono un insieme come Internet. I Web Services di standard di comunicazione che permettono a diverse applicazioni di scambiarsi dati e servizi applicativi. Tutto ciò garantisce la massima accessibilità da parte degli operatori. L’architettura prescelta e’ quella delineata da Java 2 Standard Edition di Sun e gli strumenti di sviluppo sono: • Eclipse 3.1 – come piattaforma di sviluppo • Apache Axis e Java Servlet – per la realizzazione dei web service • Apache Struts e Java Sever Pages – per le interfacce web - 26 - CAP. 2 – PROGETTO MIRO Data la sua architettura così generica il sistema può operare su qualsiasi piattaforma che supporti i web services realizzati in Java. I database supportati sono tutti quelli con interfacce JDBC (nel nostro caso MySQL). I client possono accedere in qualsiasi momento per consultazione o inserimento dati utilizzando un comune Web Browser. Ogni operatore accederà a funzionalità diverse del sistema sulla base del proprio profilo professionale. 2.3.1 COMUNICAZIONE SICURA Un aspetto al quanto fondamentale che è stato oggetto di studio e di progettazione nel nostro lavoro, è rappresentato dalla comunicazione sicura e dal mutuo riconoscimento. Anche se, come detto precedentemente, in questo lavoro non vengono mai trattati i dati sensibili relativi ai pazienti (ossia le informazioni personali), la sicurezza delle comunicazioni all’ interno del sistema svolge un ruolo fondamentale, perché la struttura è stata pensata e sviluppata per applicazioni che si basano su Internet, rete insicura per sua natura, si è reso necessario utilizzare il protocollo di comunicazione https che rappresenta l’ equivalente del classico http, dove le informazioni viaggiano in maniera criptata; le transazioni effettuate vengono - 27 - CAP. 2 – PROGETTO MIRO criptate tramite lo standard SSL con mutuo riconoscimento. Alla base di una comunicazione sicura, è necessario un riconoscimento di entrambi conversazione. Tutto ciò è i stato partecipanti possibile alla attraverso l’utilizzo di certificati digitali, che garantiscano l’ identità di chi si collega al sistema. Il certificato digitale rappresenta una sorta di carta identità elettronica per chi si aggancia al sistema. Tramite l’utilizzo della tecnologia SSL e dei certificati vengono soddisfatti i quattro requisiti fondamentali per la sicurezza: • Autenticazione: garanzia che l’utente sia chi dichiara di essere. Ottenuta grazie al certificato ed agli Username e Password. • Confidenzialità: essere letti garanzia che i esclusivamente dati possono dall’utente autorizzato. Ottenuta grazie ai certificati. • Integrità: vengano garanzia modificate che le informazioni durante la non trasmissione. Ottenuta grazie alla crittografia. • Non-ripudiazione: garanzia che chi utilizza il programma tramite il certificato se ne assuma le responsabilità. - 28 - CAP. 2 – PROGETTO MIRO 2.3.2 Messaggi SOAP La comunicazione all’ interno del sistema, avviene con scambio di messaggi SOAP. I dati si inviano come allegati ai messaggi SOAP, abbiamo scelto questa tecnica per lo scambio di informazione perché avendo come obbiettivo quello di creare una struttura in grado di adattarsi a qualsiasi tipo di esame, era importante avere la possibilità di scambiare messaggi di dimensioni variabili, in questo modo abbiamo raggiunto tale obbiettivo. Ad esempio, un dato proveniente da un laboratorio radiologico produce dei file Dicom di dimensioni che si aggirano attorno ai 40 Mb (dipende dall’ esame), con questo sistema non si sono verificati problemi di trasmissione dei pacchetti. Una cosa importante da notare è che per implementare questa struttura si ha bisogno di una rete a larga banda che consenta le transizioni di file di dimensione variabile. - 29 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE CAPITOLO 3 MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE Analisi degli attori principali dell’architettura, dal laboratorio al medico. 3.1 Laboratorio 3.1.1 Web Service laboratorio 3.1.2 Mutuo Riconoscimento 3.1.3 Interfacce grafiche 3.2Medico 3.2.1 Mutuo Riconoscimento 3.2.2 Interfacce grafiche 3.2.3 Firma del referto - 30 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE 3.1 LABORATORIO Il laboratorio come visto in precedenza rappresenta uno degli attori fondamentali del nostro sistema. E’ qui che viene effettuato l’esame e dove viene memorizzato il dato digitale, ed è qui che si chiude l’intero arco del processo dove termina la fase di refertazione. 3.1.1 Web Service Laboratorio Fig. 3.1 – Struttura del Web Service Laboratory Anche il laboratorio ha bisogno di un web service che mette a disposizione una serie di metodi (quelli in colore giallo) per lo scambio dei messaggi con il repository centrale. - 31 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE 3.1.2 Mutuo Riconoscimento Una volta effettuato l’accesso tramite un semplice web browser( ad esempio internet explorer) il sistema tramite l’utilizzo dei certificati digitali effettua il riconoscimento del laboratorio che ha compiuto l’accesso. Ciò avviene tramite l’ estrapolazione del common name dal certificato il quale viene confrontato con il dato presente nella tabella tm_utenti nel campo utn_cn, se il confronto ha esito positivo il sistema effettuerà un accesso guidato all’ interno del repository, visualizzando tramite opportune interfacce solo i dati relativi agli eventi ad esso connessi. 3.1.3 Interfacce grafiche Il sistema offre al client del laboratorio dell’ interfacce user-friendly di facile utilizzo. Le varie form sono state create tramite tecnologia jsp con l’ausilio del tool struts (entrambi questi prodotti verranno spiegati successivamente). L’interfaccia per l’inserimento dei dati da parte del laboratorio nel repository è la seguente: - 32 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE Fig. 3.2 – Form di visualizzazione degli eventi Lo sfondo blu denota il laboratorio, nella seguente form al client viene proposta una tabella che contiene le informazioni sullo stato degli esami presenti nel repository riferiti a quel particolare laboratorio, i campi visualizzati sono: • Data: “ora e data di emissione del dato nel repository” • Utente: “struttura che ha generato l’evento” • Codice: “codice identificativo dell’esame effettuato” • Tariffa: “tipo do esame effettuato” • Stato: “stato dell’esame aperto,chiuso o refertato” • Link: “possibilità di effettuare il download dell’esame” - 33 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE E’ importante notare che il laboratorio cliccando sul link dello stato può decidere se chiudere il referto o no. Scegliendo l’opzione open dal menù si apre la seguente form: Fig. 3.3 – Form per l’inserimento dei dati In questa form viene offerta al client la possibilità di aprire un evento ossia di generare nel web service repository una riga editando i seguenti campi: • Codice Tariffa: “codice identificativo dell’esame effettuato” • File dell’esame: “si allega il dato digitale prodotto in laboratorio” • Unità esterna: “si indica se è interno o esterno alla struttura attinente” - 34 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE • Richiedente: “si indica chi ha richiesto l’esame” • Impegnativa: “si mette un codice alfa numerico per l’impegnativa” • Note: “campo per editare delle note in merito all’esame” - 35 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE 3.2 MEDICO Il medico è l’ultima figura che prendiamo in esame. E’ qui che viene effettuato il referto ed è qui che si viene a creare il processo di second opinion. 3.2.1 Mutuo Riconoscimento Come avviene per il laboratorio anche il medico può accedere al sistema con l’utilizzo di un semplice web browser (ad esempio internet explorer) collegandosi al sito del web service repository. Al momento dell’accesso, il sistema tramite l’utilizzo dei certificati digitali effettua un muto riconoscimento per il medico e per il web service repository. Il riconoscimento del dottore avviene tramite il common name del proprio certificato digitale (presente nella propria smart card), il quale viene confrontato con il con il dato presente nella tabella tm_utenti (del repository) nel campo utn_cn, se il confronto ha esito positivo il sistema effettuerà un accesso guidato all’ interno del repository, visualizzando tramite opportune interfacce solo i dati relativi agli eventi che esso è in grado di rifertare. - 36 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE In questo modo è garantita l’ affidabilità del sistema da un punto di vista puramente medico. 3.3.2 Interfacce grafiche Nel progettare l’interfaccia utente, come per il laboratorio, lo scopo era creare delle form di tipo userfriendly in modo da facilitare il loro impiego da parte del personale medico. Appena effettuato l’accesso e superata la fase di mutuo riconoscimento, al client, viene presentata la seguente form: Fig. 3.4 – Form di visualizzazione eventi Si può notare, lo sfondo verde associato al dottore viceversa quello del laboratorio erà blu. Si vede subito che - 37 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE l’unica cosa che cambia nella struttura è il menù dove è assente l’opzione open perché quella erà caratteristica del laboratorio l’unico in grado di generare un evento. Il dottore scegliendo l’esame può decidere di scaricare il dato digitale nella propria postazione, premendo download, e quindi visualizzarlo; fatto ciò tramite il link stato si aprirà una nuova form : Fig. 3.5 – Form per effettuare il referto. In questa finestra vengono visualizzati anche i referti precedentemente effettuati da altri medici, se lo stato dell’evento era impostato su aperto o refertato il dottore nell’apposita casella di testo potrà editare il suo referto avvalendosi della possibilità di confrontarsi con i pareri medici precedentemente inseriti. In questo modo si crea ciò che oggigiorno si definisce meccanismo di “second opinion”. Scritto l’esame il medico aggiungerà alla lista degli altri - 38 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE referti il proprio premendo il tasto confirm. E’ qui che si mette in modo il meccanismo di firma digitale. 3.2.2 Firma del referto Fig. 3.6 – Funzione di Hash. Il processo di firma digitale viene effettuato grazie ad una libreria sviluppata da un nostro collega, che si occupa della cifratura di dati binari utilizzando i certificati digitali che risiedono all’interno di una smart-card. L’interfaccia di questa libreria principalmente espone due metodi: uno per firmare il dato e uno per verificarne l’integrità. La firma digitale di un documento è il risultato dell'applicazione di una funzione hash al documento stesso. Per essere utile, però, la funzione hash deve soddisfare a due importanti proprietà. Primo, dev'essere difficile trovare due documenti che possiedono la stessa valore di hash; - 39 - CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE secondo, dato un valore di hash deve essere difficile recuperare il documento che ha prodotto quel valore. Il referto per mezzo di una funzione di hash viene convertito in una sequenza di byte di lunghezza finita, nela caso dell’algoritmo SHA-1 l’array è di 20 byte. Una funzione hash è una funzione da molti a uno che mappa i suoi valori di ingresso in un valore appartenente ad un insieme finito. Tipicamente questo insieme è un intervallo di numeri naturali. Nel nostro caso un flusso di byte di lunghezza variabile, il nostro referto, viene trasformato in una sequenza di lunghezza finita, chiamata digest. È questo nuovo oggetto binario che viene inviato alla procedura di firma digitale che a questo punto utilizzando il digest del referto come input di un’ulteriore funzione di hash eseguita dalla smart-card che cripta i dati utilizzando il certificato digitale. Questo processo è stato realizzato in maniera esterna al nostro sistema, in modo da non soffrire dei problemi di implementazione dovuto alle differenze tra le varie smart-card, inoltre è possibile affinare la tecnica di firma aggiungendo la chiamata ad un servizio di timestamping che possa garantire l’istante di tempo in cui il dato è stato firmato. Un'altra persona può controllare la firma applicando la stessa funzione di hash alla propria copia del documento e confrontando il valore di hash ottenuto con quello del documento originale. Se coincidono, può praticamente certo che i documenti siano identici. - 40 - essere CAP. 3 – MUTUO RICONOSCIMENTO E INTERFACCE GRAFICHE Fig. 3.7 – Generazione e verifica della firma digitale. La verifica della firma viene eseguita dal repository per garantire all’utente che visualizza i referti l’identità della persona che ha scritto il documento. Nel nostro sistema viene richiamata una funzione di libreria che partendo dal documento originale effettua di nuovo il processo che confronta il documento firmato. - 41 - CAP. 4 - TECNOLOGIE SOFTWARE CAPITOLO 4 TECNOLOGIE SOFTWARE Panoramica delle tecnologie software impiegate nella realizzazione del progetto dal lato client con delle web application. 4.1 Java Platform 4.1.1 Java Server Pages 4.2 Apache Tomcat 4.2.1 Comunicazione SSL 4.3 Strumenti di Sviluppo 4.3.1 Apache Struts - 42 - CAP. 4 - TECNOLOGIE SOFTWARE 4.1 JAVA PLATFORM Java appena è uscito è stato accolto con molto entusiasmo dalla comunità mondiale dei progettisti di software e dei provider di servizi Internet, questo perché Java permetteva agli utenti di Internet di utilizzare applicazioni sicure e indipendenti dalla piattaforma, che si possono trovare in qualsiasi punto della rete. Java è quindi nato come linguaggio per la rete, per affiancare l'Hyper Text Markup Language (HTML), il quale non è un linguaggio di programmazione vero e proprio, e per dargli quella sicurezza che l'HTML non ha. Da quando è nato Java sulla rete si è iniziato a poter parlare di numeri di carte di credito e di informazioni sicure, notizia che ha molto affascinato le grosse società mondiali, le quali hanno trasformato la vecchia Internet, rete ad appannaggio delle sole università e centri di ricerca, nell'attuale mezzo di comunicazione aperto a tutti. Il linguaggio di programmazione Java è stato creato verso la metà degli anni novanta, è il più recente tra i suoi cugini, e per questo è ancora in fase evolutiva, tanto che ogni anno circa ne viene rilasciata una nuova relase. Da linguaggio nato solo per la rete è divenuto un vero e proprio linguaggio di programmazione, paragonabile, dal punto di vista delle funzionalità, al più blasonato C++. Java e la maggior parte degli altri linguaggi possono essere - 43 - CAP. 4 - TECNOLOGIE SOFTWARE paragonati solo dal punto di vista delle funzionalità, perché sono fondamentalmente molto diversi, infatti Java compila i sorgenti dei suoi programmi in un codice detto Bytecode, diverso dal linguaggio della macchina su cui è compilato, mentre linguaggi come il C++ compilano i sorgenti dei programmi in un codice che è il codice della macchina ( per macchina intendo computer + sistema operativo ) su cui è eseguito. Quindi per eseguire un programma Java occorre avere uno strumento che è chiamato Java Virtual Machine, la quale interpreta il bytecode generato dal compilatore Java e lo esegue sulla macchina su cui è installato. Grazie alla Java Virtual Machine Java è indipendente dalla piattaforma, infatti il programma compilato Java è legato alla JVM e non al sistema operativo, sarà quindi possibile eseguire lo stesso programma Java, compilato una sola volta su una qualche macchina con un compilatore Java versione X, su una piattaforma Windows e su una piattaforma Linux, per fare questo però c'è bisogno che sia Windows che Linux abbiano installato una Java Virtual Machine che supporti la versione X di Java. Le due JVM installate sulle due piattaforme diverse sono lo stesso programma compilato una volta per Windows ed una volta per Linux, come avveniva con i programmi scritti in linguaggi come il C/C++. Una Java Virtual Machine è implementata anche nei vari Browser (Come Netscape e Explorer) per poter eseguire i programmi Java incontrati nella rete, i cosidetti Applet. - 44 - CAP. 4 - TECNOLOGIE SOFTWARE Questo però, unito al fatto che Java ancora si evolve, causa degli ovvi problemi di incompatibilità: capita sempre che il più moderno Browser supporti una versione precedente di Java rispetto all'ultima versione rilasciata dalla Sun Microsystem, inoltre bisogna tener presente che non tutti gli utenti di Internet navigano usando l'ultima versione di Netscape o di Explorer. Quindi volendo creare un applet ed inserirlo in un nostro documento HTML, dobbiamo tenere presente questi problemi, e cercare di scrivere un programma che sia compatibile con la maggior parte delle JVM inplementate nei vari browser. Un altro problema da affrontare è quello della scelta del compilatore Java da utilizzare, infatti esistono vari ambienti integrati per editare, compilare, debuggare ed eseguire programmi Java, come quelli della Borland, della Microsoft, della Symantec. Tutti questi ambienti offrono dei tool di sviluppo eccellenti, come editori grafici di finestre, debugger molto interessanti, però hanno due problemi, il primo è che si pagano, anche molto, il secondo è sempre lo stesso della compatibilità, infatti essi spesso si trovano indietro alla relase della sun, ed inoltre aggiungono delle classi che poi le JVM implementate nei browser non hanno. Il mio consiglio è quello di usare le JDK ( Java Development Kit ) della Sun, le quali comprendono sia il compilatore che la Java Virtual Machine per eseguire i programmi da noi compilati, inoltre sono freeware (non costano niente) e sono scaricabili dalla rete ed i browser si - 45 - CAP. 4 - TECNOLOGIE SOFTWARE adeguano pian piano a questa versione di Java. Se volete scrivere applet per i vecchi browser dovete scaricarvi la versione 1.2.1 di Java, però questa versione è niente alla versione attualmente più gettonata, ovvero alla 1.5.1. Un ultimo problema che ha Java è la lentezza, infatti, come già detto, esso è interpretato, quindi le istruzioni Java prima di essere eseguite dalla macchina vengono interpretate dalla JVM, ovvero per eseguire ogni istruzione il computer eseguirà un numero di istruzioni macchina che è più del doppio delle istruzioni che eseguirebbe se la stessa istruzione fosse stata scritta in C, quindi avrete bisogno di computer veloci per eseguire bene programmi Java, e di questo vi sarete sicuramente accorti anche navigando sulla rete. Anche la memoria è importante, si compila e si esegue anche con soli 128Mb di RAM, ma per fare le cose velocemente ne occorrono almeno 512. Per finire ritorno al Java Development Kit della Sun Microsystem, con questo è possibile produrre tutto il software che si vuole senza dover pagare diritti di uso del prodotto come avviene con il Borland Jbuilder, il Symantec Cafe, e il Microsoft Visual Java.La principale differenza tra Java e gli altri linguaggi di programmazione ad oggetti è che mentre con questi ultimi è possibile anche porgrammare ad oggetti con Java si deve assolutamente programmare ad oggetti. - 46 - CAP. 4 - TECNOLOGIE SOFTWARE 4.1.1 Java Server Pages JSP (Java Server Pages) è una tecnologia semplice ma potente, che permette di creare Pagine HTML dinamiche lato server. In questo stralcio presentiamo, aiutandoci con esempi, le caratteristiche principali. Introduzione JSP (Java Server Pages) è una tecnologia semplice ma potente, che permette di creare Pagine HTML dinamiche lato server. Questa tecnologia agli occhi del programmatore viene gestita per mezzo di un linguaggio di script che è in grado di mescolare codice HTML, componenti riusabili (JavaBeans), applicazioni remote (Servlet), codice Java e script Java-like. Le Pagine JSP sono un'estensione diretta dei Servlet Java e offrono, rispetto ad altre attuali tecnologie Server-Side, il vantaggio e la possibilità di separare la sezione di gestione delle operazioni e di produzione dei contenuti, da quello di visualizzazione vera e propria. Normalmente una Pagina JSP è composta da: porzioni di codice HTML, JavaScript, CSS e così via, non compilati dal motore Jsp (Jsp Engine) e comunemente definiti blocchi di codice statico; porzioni di codice Java, compilati dal motore Jsp, che prendono il nome di blocchi di codice dinamico. Come lavorano le Pagine JSP - 47 - CAP. 4 - TECNOLOGIE SOFTWARE La prima volta che un Client (di norma un generico browser) effettua la richiesta di una Pagina JSP ad un Web Server (container JSP), questo la compila creando un oggetto Servlet che risiederà in memoria; solo dopo questo passaggio l'output viene inviato al Client che potrà interpretarlo come se fosse una semplice Pagina HTML. Ad ogni richiesta successiva della medesima Pagina JSP, il Web Server controlla se è stata modificata: in caso negativo richiama il Servlet già compilato, altrimenti si occupa di eseguire nuovamente la compilazione e memorizzazione del nuovo Servlet. Conseguenza di questo meccanismo è che la prima volta che si invoca una Pagina JSP il sistema sarà un pò più lento del normale, mentre dalle invocazioni successive si avranno prestazioni di tutto rispetto. Un semplice esempio del codice di una Pagina JSP e dell'output prodotto sono riportati qui di seguito: <%@page language="java"%> <%@page import="java.util.*"%> <html> <head> <title>Java Server Pages: Hello World</title> </head> <body> <% out.println(" Hello World JSP! "); %> <%= new Date() %> </body> - 48 - CAP. 4 - TECNOLOGIE SOFTWARE </html> Se ci si sofferma esclusivamente sulla forma del documento JSP è possibile notare che l'aspetto di codice JSP ricalca quello di una Pagina HTML e non quello di una classe Java. Analizzando successivamente il codice del Servlet generato dal Web Server alla prima invocazione della Pagina JSP, che può variare leggermente in base al motore Jsp utilizzato, si nota che il metodo che effettua il servizio vero e proprio è _jspService() (Per visualizzare il codice Servlet clicca qui). All'interno del metodo _jspService() troviamo, oltre ad una serie di variabili e controlli fondamentali per il funzionamento del Servlet, la traduzione dei tag HTML contenuti nella Pagina JSP in codice Java. Componenti di un Pagina JSP Una Pagina JSP può essere composta da: direttive; script JSP; oggetti impliciti; azioni. Direttive Le direttive sono elementi JSP che forniscono informazioni globali su un'intera Pagina JSP. Un esempio potrebbe essere una direttiva che indichi il linguaggio da utilizzare nella compilazione della Pagina JSP. La sintassi - 49 - CAP. 4 - TECNOLOGIE SOFTWARE delle direttive è la seguente <%@ direttiva {attributo="valore"}%> . Volendo indicare al motore Jsp che il linguaggio da utilizzare nelle Pagine JSP deve essere il Java, è possibile utilizzare la riga di codice seguente: <%@ page language="java"%>. Attualmente nella specifica JSP sono definite tre direttive: page, include, taglib. • Direttiva Page definisce informazioni globali riguardanti l'intera Pagina JSP che la contiene. Le impostazioni che modifica influenzano direttamente la compilazione della pagina. • Direttiva Include serve per inserire testo e codice all'interno di una Pagina JSP al momento della sua traduzione. La sua sintassi è la sequente: <%@include file="specificoURL"%>. • Direttiva Taglib personalizzato permette di tag di creare chiamato tag un library. insieme Questa direttiva dichiara che la pagina intende utilizzare tag personalizzati, indica univocamente la libreria che li definisce e associa loro un prefisso che li distingue da quelli presenti in altre librerie. La sintassi della direttiva taglib è la seguente: uri="URIdellaLibreriadiTag" <%@taglib prefix="prefissoTAG"%> dove : uri identifica univocamente personalizzati; - 50 - un insieme di tag CAP. 4 - TECNOLOGIE SOFTWARE prefix definisce una stringa utilizzata per distinguere una tag personalizzato da uno invece standard. Script JSP Gli script JSP consentono di includere porzioni di codice direttamente all'interno di Pagine HTML, esistono tre elementi coinvolti negli script JSP, ciascuno dei quali ha una posizione ben definita all'interno del Servlet generato: Dichiarazioni, Espressioni e Scriptlet. • Dichiarazioni JSP servono per dichiarare variabili e metodi del linguaggio di script usato nelle Pagine JSP. La sintassi delle dichiarazioni JSP è la seguente: <%! Dichiarazione %>, un esempio <%! String nome=new String("Andrea"); %> nel caso in cui si voglia dichiarare una variabile di tipo String contenente un valore iniziale diverso da null; <%! public String getName() { return name; } %> nel caso in cui si voglia definire un metodo che ritorni una variabile, definita in questo caso nella dichiarazione precedente, di tipo String. • Espressioni JSP sono degli elementi del linguaggio di script che vengono valutati logicamente, letteralmente o matematicamente, il cui risultato viene convertito in una java.lang.String. Le espressioni sono valutate all'atto della richiesta HTTP. La stringa risultante viene inserita al posto dell'espressione nel file .jsp, nel caso l'espressione non può essere convertita, si verifica un errore di - 51 - CAP. 4 - TECNOLOGIE SOFTWARE traduzione. La sintassi di un'espressione JSP è la seguente: <%= Espressione %>, un esempio Ciao <%= getName() %>. • Scriptlet JSP possono contenere qualsiasi tipo di istruzione a patto che siano valide per il linguaggio specificato nella direttiva language. Vengono eseguiti al momento della richiesta e possono far uso di dichiarazioni, espressioni e JavaBeans. La sintassi degli scriptlet JSP è la seguente: <% Sorgente dello Scriptlet %>. Importante sottolineare che il codice contenuto tra i tag <% %> viene inserito nel metodo _jspService() del Servlet al momento della compilazione (Clicca qui per visualizzare un esempio di codice Servlet). Oggetti Impliciti Scrivendo Pagine JSP si ha l'accesso ad alcuni oggetti impliciti appositamente creati per essere utilizzati nei documenti JSP senza dover essere dichiarati anticipatamente. Gli oggetti impliciti più importanti sono: request, response, pageContext, session, application, out, config, page. • request permette il recupero dell'informazioni legate alla richiesta commissionata dal browser dell'utente come i parametri, le intestazioni, i cookie e così via. • response permette l'accesso al canale di output, per la generazione della risposta HTTP; è possibile comunicare - 52 - CAP. 4 - TECNOLOGIE SOFTWARE un errore, inviare una particolare intestazione, settare un cookie e altro. • pageContext è molto generico e rappresenta qualsiasi cosa abbia a che fare con il contesto di esecuzione della pagina; da esso è possibile ricavare gli altri oggetti impliciti. • session permette di associare all'utente che richiede la pagina un'informazione recuperabile in ogni altro documento dell'applicazione. E' possibile memorizzare le scelte effettuate dall'utente in una o più variabili di sessione così da poterne tener traccia. • application fornisce un accesso al contesto di esecuzione dell'applicazione Web, cioè offre la possibilità di ottenere informazioni sul Servlet Engine che esegue l'applicazione, recuperare i parametri di inizializzazione del software, registrare dei messaggi accessibili da ogni pagina appartenente al contesto. • L'oggetto out è di tipo JspWriter; ad esso corrisponde il canale di output instaurato con il browser dell'utente che ha richiesto la pagina. • config non è utilizzato frequentemente dagli sviluppatori poichè il container imposta o rileva automaticamente le informazioni accessibili tramite questo riferimento. • page rappresenta la parola chiave this dell'istanza corrente della Pagina JSP. - 53 - CAP. 4 - TECNOLOGIE SOFTWARE Azioni Le azioni rappresentano un livello di astrazione che può essere utilizzato per incapsulare agevolmente compiti comuni. Generalmente vengono impiegate per la creazione o la manipolazione di oggetti, perlopiù JavaBeans. Alcune azioni standard sono: <jsp:useBean>: associa un'istanza di un JavaBean a un ambito e ad un ID tramite una variabile di script dichiarata con lo stesso nome; se non trova un'istanza "in vita" cerca di crearla. Gli attributi più importanti legati a questa azione sono: id : rappresenta l'identità dell'istanza dell'oggetto all'interno dell'ambito specificato; scope : rappresenta l'ambito dell'oggetto. Possibili scope: page, request, session, application; class : rappresenta il nome di classe che definisce l'implementazione dell'oggetto. Da questa azione principale derivano le seguenti sottoazioni: <jsp:setProperty>: imposta il valore di una proprietà di un bean; <jsp:getProperty>: prende il valore della proprietà del bean referenziato, lo converte in un java.lang.String e lo inserisce nell'oggetto out implicito; Un JavaBeans è un componente Java al 100% che opera all'interno di una qualsiasi macchina virtuale; è una classe - 54 - CAP. 4 - TECNOLOGIE SOFTWARE Java che implementa l'interfaccia java.io.Serializable e utilizza metodi pubblici get/set per esporre le proprietà. <jsp:include>: fornisce un modo per includere risorse aggiuntive, di tipo statico o dinamico, nella Pagina JSP corrente. Gli attributi più importanti legati a questa azione sono: page : rappresenta la URL relativa della risorsa da includere; flush : questo attributo contiene un valore boolean che indica se il buffer debba o meno essere svuotato. <jsp:forward>: consente al motore Jsp l'inoltro, all'atto dell'esecuzione, della risorsa corrente a una risorsa statica, a un Servlet o ad una Pagina JSP. L'unico attributo possibile per questa azione è: page : rappresenta la URL relativa all'oggetto da inoltrare. <jsp:param>: viene impiegata per fornire informazioni a coppie di tag/valore, includendoli come sottoattributi delle azioni <jsp:forward>, <jsp:useBean>, <jsp:include> all'atto dell'esecuzione, della risorsa corrente a una risorsa statica, a un Servlet o ad una Pagina JSP. L'unico attributo è: name : rappresenta il nome del parametro referenziato; value : rappresenta il valore del parametro referenziato. Conclusioni Quelle esposte, anche se in estrema sintesi, sono le caratteristiche più salienti della tecnologia JSP che offre, - 55 - CAP. 4 - TECNOLOGIE SOFTWARE rispetto ad altre attuali tecnologie Server-Side, la possibilità ed il grosso vantaggio di separare le logiche di business dalla loro presentazione. - 56 - CAP. 4 - TECNOLOGIE SOFTWARE 4.2 APACHE TOMCAT Struttura di una applicazione web La nozione di applicazione web è stata formalizzata per la prima volta nella versione 2.2 delle specifiche delle servlet Java. Sebbene il concetto di applicazione web sia piuttosto ampio e dipendente dal contesto in cui viene esaminato, la definizione fornita nelle suddette specifiche risulta di portata sufficientemente generale. Dal punto di vista dello sviluppatore di applicazioni web basate su Java risulta particolarmente importante la standardizzazione della struttura di tali applicazioni e l'introduzione degli archivi WAR come strumento per il deployment delle applicazioni web. Struttura di una applicazione web Una applicazione web è una gerarchia di file e directory disposti secondo uno schema standard. /webapp. Una applicazione web ha una propria directory radice (root). Il nome della cartella corrisponde a ciò che nella terminologia delle servlet viene chiamato context path (nel seguito supporremo che tale nome di tale cartella sia - 57 - CAP. 4 - TECNOLOGIE SOFTWARE webapp). La directory radice contiene tutti gli altri elementi che compongono l'applicazione web. File HTML, JSP, ... La directory /webapp contiene i documenti HTML, le pagine JSP, le immagini e le altre risorse che compongono l'applicazione web. Tali file possono essere strutturati in directory esattamente come si farebbe per un sito web statico. /webapp/WEB-INF. All'interno della cartella root si trova una directory speciale, denominata WEB-INF. La funzione di questa cartella è quella di contenere file riservati, come ad esempio il file di configurazione dell'applicazione, web.xml. Per questo motivo le specifiche impongono che la cartella ed il suo contenuto debbano risultare inaccessibili per gli utenti dell'applicazione. /webapp/WEB-INF/web.xml. Il file web.xml viene comunemente denominato deployment descriptor. Si tratta del file di configurazione dell'applicazione web; in esso, ad esempio, si definiscono gli alias delle servlet, i parametri di inizializzazione, le mappature dei percorsi e così via. /webapp/WEB-INF/classes e /webapp/WEB-INF/lib. Queste due directory sono destinate a contenere le classi e gli archivi Jar di cui la nostra applicazione web necessita. Se dobbiamo utilizzare delle classi Java compilate (file con - 58 - CAP. 4 - TECNOLOGIE SOFTWARE estensione .class andremo a copiarle all'interno di /webapp/WEB-INF/classes, secondo l'usuale struttura di directory Java (così se abbiamo una classe MiaServlet contenuta nel package sito.servlet. il percorso del file compilato sarà /webapp/WEB- INF/classes/sito/servlet/MiaServlet.class). Se dobbiamo utilizzare classi contenute in archivi Jar, invece, sarà sufficiente copiarli in /webapp/WEB-INF/lib. Archivi WAR Una volta sviluppata una applicazione web possiamo "impacchettarla" in un archivio WAR (Web Application Archive). Un archivio WAR non è altro che un archivio Jar, una vecchia conoscenza dei programmatori Java, la cui estensione viene modificata in .war semplicemente per indicare che il contenuto dell'archivio è una applicazione web. Gli archivi WAR hanno una duplice funzione: oltre a quella ovvia di comprimere tutte le componenti di una applicazione in un unico archivio, costituiscono il formato di deployment dell'applicazione stessa. Il termine inglese deployment indica la messa in opera dell'applicazione. Uno scenario tipico è quello in cui l'applicazione viene sviluppata localmente per poi essere trasferita, ad ultimazione, sul server vero e proprio (detto anche di - 59 - CAP. 4 - TECNOLOGIE SOFTWARE produzione). In questo caso è sufficiente creare il file WAR, copiarlo sul server di produzione e seguire le istruzioni per l'installazione del WAR relative al servlet container che si sta utilizzando. Nel caso di Tomcat, l'installazione di un WAR è estremamente semplice: è sufficiente copiare il file nella directory webapps e avviare (o riavviare) il server, che provvederà da solo a decomprimere l'archivio e ad avviare l'applicazione. Un esempio di comando per la creazione di un archivio WAR (da digitare dopo essersi spostati all'interno della cartella webapp) è il seguente: jar cvf webapp.war . E' consigliabile, in ogni caso, fare riferimento alla documentazione ufficiale del tool jar per una spiegazione esaustiva delle opzioni disponibili. In questa breve nota abbiamo preso dimestichezza con la strutturazione di una applicazione web, così come definita nelle specifiche Servlet a partire dalla versione 2.2. Organizzando le nostre applicazioni in tal modo otterremo diversi vantaggi: • aumenteremo la portabilità dell'applicazione, rendendone più semplice la messa in opera; - 60 - CAP. 4 - TECNOLOGIE SOFTWARE • renderemo l'applicazione più comprensibile ad altre persone, in quanto sarà molto più semplice sapere dove si trovano le classi, gli archivi Jar e le altre componenti dell'applicazione stessa; • potremo racchiudere l'intera applicazione in un archivio WAR, rendendone semplice l'installazione su tutti i servlet container conformi alle specifiche Sun. Abbiamo così compiuto il primo passo per la realizzazione di una applicazione web in ambiente Java, che abbiamo imparato a strutturare in modo standard. Il prossimo argomento da affrontare sarà la configurazione dell'applicazione attraverso il già citato file web.xml. L'ambiente di Tomcat Tomcat contiene al suo interno tutte le funzionalità tipiche di un web server, ovvero - in sintesi - ha la capacità di interpretare una richiesta di una risorsa veicolata su protocollo HTTP, indirizzarla ad un opportuno gestore (o prenderla dal filesystem) e restituire poi il risultato (codice HTML o contenuto multimediale che sia). In questo aspetto non differisce molto da altri web server, come Apache, se non che spesso si rivela meno prestante. La caratteristica - 61 - CAP. 4 - TECNOLOGIE SOFTWARE innovativa di Tomcat non è quindi quella di essere un veloce web server, ma piuttosto quella di fornire allo sviluppatore un vero e proprio ambiente nel quale girano applicazioni Java (le servlet). In questo stralcio cercheremo di comprendere meglio come sia strutturato questo ambiente Java nel quale prenderanno vita le nostre applicazioni lato server. Contenitori in Tomcat Come nelle normali applicazioni, anche nella programmazione lato server gli oggetti creati devono avere uno scopo, ovvero essere definiti ed accessibili in un determinato contesto. suddividere lo spazio Utilizzare delle Tomcat risorse equivale in a contenitori sostanzialmente indipendenti nei quali possono essere depositati oggetti Java, secondo una logica orientata al web. Per comprendere appieno questo concetto analizziamo nel dettaglio i singoli contenitori che sono tra loro strutturati in maniera abbastanza gerarchica. Tomcat Chiaramente il primo livello corrisponde al web server stesso che avrà un insieme di variabili ad esso associate. Ad esempio qui dentro vi sarà indicazione del numero di thread attualmente in esecuzione, della versione di Tomcat o altre proprietà globali generalmente di poco interesse nella programmazione. - 62 - CAP. 4 - TECNOLOGIE SOFTWARE Contesto Quando un utente accede al web server accede in realtà ad una sezione specifica del web server (a un certo dominio virtuale o directory, ad esempio) identificata sostanzialmente dalla URL. Nel gergo di Tomcat la sezione viene chiamata contesto (context). All'interno di questo contenitore si troveranno quindi oggetti comuni a tutta la sezione, che saranno gli stessi per tutti gli utenti e tutte le applicazioni generato del contesto. all'avvio indipendentemente di Questo Tomcat dal fatto contenitore e che viene rimane qualcuno attivo lo stia interrogando in quel momento. Questo non vuol dire che rimanga invariato, anzi, il suo contenuto cambierà in funzione delle richieste ricevute ed ogni elemento contribuirà a definire l'ambiente di tutta la sezione. Sessione Il primo contenitore con caratteristiche fortemente orientate al web è quello associato alla sessione (Session). Come dice il nome questo contenitore viene associato ad un utente (o, meglio, ad un client) per tutto il tempo per il quale rimane connesso ad un contesto specifico. Al suo interno verranno memorizzati oggetti che saranno accessibili solo a quell'utente e ne personalizzeranno l'ambiente rispetto ad altri client che utilizzino risorse dello stesso contesto nello stesso momento. - 63 - CAP. 4 - TECNOLOGIE SOFTWARE Richiesta Il contenitore con la vita più breve è la richiesta (Request) che nasce e muore nell'ambito della singola transazione web. Raramente questo contenitore viene riempito da applicazioni anche se svolge un ruolo fondamentale nel flusso dei dati che attraversano Tomcat. Il flusso di dati Abbiamo visto che l'oggetto Context viene creato all'avvio di Tomcat mentre l'oggetto Request nasce muore ad ogni richiesta. Andiamo ad analizzare un po' più in dettaglio il flusso logico che segue Tomcat nel processare le singole richieste web che riceve. Richiesta di una risorsa Supponiamo che un client acceda a una risorsa su di un server su cui gira Tomcat. Innanzi tutto viene instanziato un oggetto Request, Req, in Tomcat nel quale vengono inserite tutte le informazioni ricevute tramite HTTP (dalla url richiesta sino ai cookies) e "al suo fianco" viene instanziato un oggetto risposta (Response, Res) nel quale costruire pian piano la risposta da restituire al client. Leggendo la url all'interno della richiesta Tomcat è quindi in grado di comprendere a quale dei contesti residenti in memoria debbano essere consegnati questi due oggetti Req e Res. - 64 - CAP. 4 - TECNOLOGIE SOFTWARE Contesto e sessione Il contesto invocato da Tomcat provvederà a vedere se esiste già una sessione attiva per quel tipo di client leggendone la "firma" nella richiesta (sotto forma di cookie o nella url, a seconda del metodo usato). Se la sessione, Ses, esiste già tra quelle immagazzinate nel context, il contesto la riprende e la affianca agli oggetti Req e Res. A questo punto l'ambiente è stato completamente definito e il web server può passare il controllo all'applicazione associata alla risorsa in questione. Le Servlet A questo punto del percorso logico, il contesto è in grado di decidere (sulla base del suo file di configurazione) a quale applicazione passare il controllo all'interno dell'ambiente di lavoro definito in tutto il percorso (la più semplice delle applicazioni è chiaramente quella che si occupa di restituire un file presente sul disco del server). L'applicazione Java (la servlet) potrà quindi svolgere tutte le sue funzioni attingendo agli oggetti già presenti nei vari contenitori (nella sessione, ad esempio) o mettendocene di nuovi. Risposta Al termine del proprio lavoro la Servlet deve dare un esito al proprio lavoro tramite la risposta Res. Le uniche - 65 - CAP. 4 - TECNOLOGIE SOFTWARE due modalità per farlo sono o riempirla con dei contenuti che il client sia in grado di comprendere (codice HTML, ad esempio) oppure inserire un ordine per il contesto di redirigere il controllo ad un'altra risorsa (forward). Nel primo caso il contesto ripasserà tutto l'oggetto a Tomcat che si occuperà di generare l'output per il client. Nel secondo caso, se la risorsa è esterna al contesto il controllo verrà restituito a Tomcat che ricomincerà il percorso logico con questa nuova risorsa. Se invece la risorsa è interna al contesto un'operazione di forward equivale a fare un passo indietro nel percorso logico per poi tornare avanti su un altro sentiero o, in altri termini, chiamare la Servlet associata alla risorsa indicata nell'istruzione di forward, passandole nuovamente tutto l'ambiente. - 66 - CAP. 4 - TECNOLOGIE SOFTWARE Tomcat è un Servlet/JSP engine, o meglio, è l'implementazione ufficiale delle specifiche di queste due tecnologie, che permette di eseguire Servlet e pagine JSP su richiesta di un utente. Esso può essere usato da solo (principalmente utile agli sviluppatori per testing e debugging) o in congiunzione con uno tra i più diffusi web server (per le applicazioni real world), tra i quali: • Apache, versione 1.3 o successiva • Microsoft Internet Information Server • Microsoft Personal Web Server • Netscape Enterprise Server Quindi in base alle esigenze di utilizzo si dovrà installare anche un web server con cui Tomcat coopererà: quando arriva una richiesta di esecuzione di una servlet o JSP, il web server la passa a Tomcat che si occupa di esaudirla. Installazione di Apache Tomcat Tomcat richiede per il suo funzionamento un Java Runtime Enviroment JRE 1.1 o successivo, (compresi, naturalmente i sistemi basati sulla piattaforma Java2). - 67 - CAP. 4 - TECNOLOGIE SOFTWARE Scaricare l'archivio nel formato più adatto al proprio sistema operativo da qui (Jakarta, il sito ufficiale di Tomcat). Spacchettare l'archivio in una directory (chiamiamola ad esempio foo) dove verrà creata una subdirectory di nome jakarta-tomcat-5.5.9. Variabili d'ambiente Cambiare directory spostandosi in jakarta-tomcat-5.5.9e settare la variabile d'ambiente TOMCAT_HOME in modo da farla puntare alla directory radice dove Tomcat è stato installato. Su Windows utilizzare set TOMCAT_HOME=C:\foo\jakarta-tomcat-5.5.9 supponendo che Tomcat sia stato installato sul disco C. Su Unix utilizzare per TOMCAT_HOME=/foo/jakarta-tomcat-5.5.9; TOMCAT_HOME per tcsh setenv bash/sh export TOMCAT_HOME /foo/jakarta-tomcat-5.5.9supponendo che Tomcat si stato installato in /foo. Settare la variabile d'ambiente JAVA_HOME in modo che punti alla directory radice dove è installato il JDK e infine - 68 - CAP. 4 - TECNOLOGIE SOFTWARE aggiungere il path dell'interprete java alla variabile d'ambiente PATH. Tomcat è pronto adesso per essere eseguito in modalità stand-alone (da solo senza web server). Avvio e arresto di Tomcat Per avviare Tomcat usare lo script o il file batch che si trovano nella directory TOMCAT_HOME/bin. Su UNIX utilizzare % startup.sh Su Windows utilizzare C:\TOMCAT_HOME\bin>startup Per arrestare Tomcat, ci sono due comandi che si trovano nella stessa directory dei comandi di avvio. Su UNIX utilizzare % shutdown.sh Su Windows utilizzare C:\TOMCAT_HOME\bin>shutdown La struttura delle directory di Tomcat Vediamo come è struturata la gerarchia delle directory di Tomcat a partire dalla radice. Dopo avere unzippato/untarato Tomcat si pùò vedere che all'interno della directory jakarta-tomcat-5.5.9 abbiamo: - 69 - CAP. 4 - TECNOLOGIE SOFTWARE Tabella 1. Struttura delle directory Nome della directory Descrizione bin Contiene gli script/batch per lo startup-shutdownd conf Contiene i file di configurazione tra cui server.xml che è il principale file di configurazione di Tomcat, e web.xml che serve a settare i valori di default per le varie applicazioni gestite da Tomcat doc Contiene vari documenti riguardanti Tomcat lib Contiene diversi file jar usati da Tomcat . Su UNIX ogni file in questa directory viene appeso al classpath. logs Directory utilizzata per i file di log src Sorgenti delle interfacce e delle classi astratte delle API servlet. webapps Contiene esempi di applicazioni web In aggiunta a queste directory si possono avere anche altre due directory: classes creata dall'utente, dove si possono mettere classi che Tomcat aggiungerà, al suo avvio, al classpath; e work generata automaticamente da Tomcat come directory temporanea (ad esempio per contenere le pagine JSP già compilate, prima di inviarle al client). Se si cancella questa directory quando Tomcat è attivo, non si potranno eseguire pagine JSP. Conclusioni - 70 - CAP. 4 - TECNOLOGIE SOFTWARE Se tutto è andato liscio Tomcat dovrebbe funzionare. Provate a testare le applicazioni già installate, ad esempio http://ind_host:8080/examples/jsp/ oppure http://ind_host:8080/examples/servlets/ (dove ind_host è l'indirizzo IP o il nome della macchina dove è installato Tomcat, oppure usa localhost se è la stessa da cui ti colleghi). Da queste due pagine si possono testare diverse pagine JSP e servlet, con la possibilità di vedere dal browser stesso anche il codice degli esempi proposti. 4.2.1 Comunicazione SSL Il protocollo SSL utilizza combinazioni di chiavi pubbliche e simmetriche. Una sessione SSL inizia con uno scambio di messaggi effettuati nella fase detta “Handshake”. Vediamola in dettaglio. Fig. 4.1 – Comunicazione Diretta SSL Il server ha un certificato che descrive le informazioni riguardanti la società/ente/persona. I campi più rilevanti sono le informazioni di cui sopra e la chiave pubblica. Il tutto è regolato dal Certificato X.509. Tale certificato può essere garantito da una Certification Authority, la quale - 71 - CAP. 4 - TECNOLOGIE SOFTWARE assicura l’esatta corrispondenza tra il certificato e colui che lo emette. Anche il client può avere un certificato, ma questo serve solo se esso deve ricevere dati sensibili da parte del server. Generalmente è il contrario. Se la connessione è iniziata dal server, esso invierà al client un messaggio “server hello”. Se è il client ad iniziare la connessione, si avrà in invio un messaggio “hello”. Analizziamo più in dettaglio tale messaggio. È definito dai campi: 1. protocol version: definisce la versione SSl usata; 2. random byte: byte casuali generati dal client; 3. Session identifier: per verificare se si tratta di una nuova sessione o di una aperta in precedenza; 4. Lista delle CipherSuite: è una lista in cui il Client notifica al Server gli algoritmi di crittografia supportati, ordinati in modo decrescente; 5. Lista degli algoritmi di compressione, supportati dal Client. Il Server risponde con un messaggio “server hello” definito nel modo seguente: 1. Protocol version: rappresenta la versione SSl scelta dal Server; 2. Random byte: byte generati in modo casuale; 3. Session identifier: identifica la sessione. Se non è una sessione già aperta, se ne crea una nuova; questo nel caso in cui nel tag “session identifier” del messaggio - 72 - CAP. 4 - TECNOLOGIE SOFTWARE “hello” sino presenti tutti zeri; Altrimenti viene riesumata la sessione indicata dal client, se presente nella cache del server; 4. ChiperSuite: famiglia di algoritmi di crittografia scelta dal server; 5. Compression method: metodo di compressione scelto da server. Dopo questo l’autenticazione scambio tramite lo di messaggi, scambio di un avviene certificato. Supponiamo che debba essere il server ad identificarsi. Questo vuol dire che i dati confidenziali transiteranno dal client al server e NON viceversa, in quanto il Client non è identificato. Tale situazione potrebbe essere, per esempio, una fase di acquisto in rete, in cui il client invia i suoi dati personali al Server, quindi è necessario che il Server sia ben identificato. Il Server dunque invia il certificato al Client, insieme alle preferenze riguardo l’algoritmo di crittografia da usare. A questo punto il client verifica che il certificato ricevuto sia valido analizzando vari aspetti. Per prima cosa la data di validità del certificato. In seguito, si verifica che la CA che garantisce il certificato (ammesso che ce ne sia una), sia una CA affidabile. Se lo è, il client recupera la chiave pubblica dalla sua lista di CA sicure per verificare la firma digitale arrivatagli con il certificato del server. Se l’informazione in tale certificato è cambiata da quando è stata firmata da una CA, o se la chiave pubblica - 73 - CAP. 4 - TECNOLOGIE SOFTWARE nella lista CA non corrisponde alla chiave privata usata dalla CA per firmate il certificato del server, il client non potrà autenticare il server. Infine, il client verifica che il nome del dominio nel certificato server corrisponda allo stesso dominio del server. Questa fase conferma che il server è localizzato nello stesso indirizzo di rete che è specificato nel nome di dominio del suo certificato. Tale controllo previene attacchi del tipo “Man in the Middle”, in quanto se il domain name non corrisponde, molto probabilmente perché il certificato è stato inviato non dal server atteso, ma da una terza persona che si è intromessa. La connessione sarà rifiutata se i due nomi non corrispondono. Se invece tutto è andato a buon fine, il client avrà verificato che il server è affidabile. A questo punto il client genera una “pre master key” e la cripta con la chiave pubblica del server. Opzionalmente, in questa fase, può essere richiesta l’autenticazione del client, che è molto simile a quella vista prima, nel seguente modo: esso deve cifrare alcuni valori casuali condivisi con la sua chiave privata, creando in pratica una firma. In più, il server verifica che il client, anche se autenticato, sia autorizzato ad accedere alle risorse richieste. La chiave pubblica nel certificato del client può facilmente convalidare tale firma se il certificato è autentico, in caso contrario la sessione sarà terminata. Il server riceve il messaggio criptato e, verificata l’attendibiltà del client, lo decripta tramite la sua chiave privata. Genera - 74 - CAP. 4 - TECNOLOGIE SOFTWARE poi una master secret (cosa che fa anche il client, seguendo gli stessi passi) e da questa, insieme, generano la chiave di sessione, una chiave simmetrica che sarà usata per criptare e decriptare i dati che attraversano il tunnel SSL appena creato; inoltre serve per verificare che i dati non vengano alterati nel lasso di tempo che intercorre tra l’invio e la ricezione. A questo punto il client ed il server si inviano un messaggio di notifica di HandShake concluso, e lo scambio di dati può iniziare. Fig. 4.2 – Attacco nel mezzo Installazione e configurazione del supporto SSL su Tomcat 5 1. Creare un keystore eseguendo il seguente comando: Windows: %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA Unix: $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA - 75 - CAP. 4 - TECNOLOGIE SOFTWARE and specify a password value of "changeit". Scommentare l’elemento "SSL HTTP/1.1 Connector" nel file di configurazione di Tomcat $CATALINA_HOME/conf/server.xml e modificarlo come necessario. <-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 --> <!-- <Connector port="8443" minProcessors="5" maxProcessors="75" enableLookups="true" disableUploadTimeout="true" acceptCount="100" debug="0" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" /> --> 2. Usare l’attributo keystoreFile per specificare il percorso del “.keystore”. Attenzione nello specificare un percorso relativo per il file di keystore perchè si parte dalla cartella di installazione del Tomcat mentre il keytool parte dalla cartella base dell’utente inoltre se si usa l’Eclipse è necessario copiare il keystore anche nella cartella dello workspace “.metadata\.plugins\org.eclipse.wst.server.core\tmp - 76 - CAP. 4 - TECNOLOGIE SOFTWARE 0” altrimenenti quando viene lanciato il Tomcat in Eclipse non trova il file (bisogna apportare le stesse modifiche fatte al file di configurazione $CATALINA_HOME/conf/server.xml anche sul file server.xml del Server project di Eclipse). Si può notare che l’elemento Connector stesso è commentato di default, quindi è necessario rimuovere i tag di commento che lo circondano. Poi, è possibile personalizzare gli attributi specificati come necessario. La discussione seguente analizza solo gli attributi di maggiore interesse per impostare la comunicazione SSL, per il resto delle opzioni si può fare riferimento alla guida del Tomcat. L’attributo port (valore di default è 8443) è il numero della porta TCP/IP sulla quale Tomcat si metterà in ascolto per le connessioni sicure. Può essere cambiare con un qualisiasi numero di porta (come con la porta di default per le communicazioni https, che è il 443). Comunque, speciali setup (che non andiamo ad analizzare) necessitano di eseguire Tomcat su numeri di porta minori di 1024 su molti sistemi operativi. If you change the port number here, you should also change the value specified for the redirectPort attribute on the non-SSL connector. This allows Tomcat to automatically redirect - 77 - users who CAP. 4 - TECNOLOGIE SOFTWARE attempt to access a page with a security constraint specifying that SSL is required, as required by the Servlet 2.4 Specification. Ci sono opzioni aggiuntive usate per configurare il protocollo SSL. Potrebbe essere necessario aggiungere o cambiare un valore dei seguenti attributi, a seconda di come è stato precedentemente configurato il keystore: NOME ATTRIBUTO DESCRIZIONE Impostare il valore a true se si vuole che il Tomcat richieda ai client SSL di presentare un Certificato client per poter usare questo clientAuth socket. Impostare il valore want se si vuole che il Tomcat richieda un Certificato client, ma non ritorni errore nel caso non venga presentato. Aggiungere questo attributo se il file del keystore creato non è nel percorso di default che si aspetta Tomcat (un file keystoreFile chiamato .keystore nella cartella user home sotto la quale Tomcat è in esecuzione). Può essere specificato un percorso assoluto, o uno relativo che viene risolto a partire dalla variabile di ambiente - 78 - CAP. 4 - TECNOLOGIE SOFTWARE $CATALINA_BASE. Aggiungere questo elemento se è stata keystorePass usata una password del keystore (e del certificato) differente da quella che si aspetta Tomcat (changeit). Aggiungere questo elemento se si sta keystoreType usando un keystore PKCS12. I valori possibili per questo attributo sono JKS e PKCS12. Il protocollo di crittografia/decrittografia che deve essere usato su questa socket. Non è consigliato cambiare questo valore sslProtocol se si sta utilizzando la JVM di Sun. È stato riportato che nell’implementazione1.4.1 di IBM il protocollo TLS non è compatibile con alcuni diffusi browser. In questo caso, usare il valore SSL. La lista, separata da virgola, di cifre di ciphers crittografia che a questa socket è consentito usare. Di default, qualsiasi cifra disponibile è consentita. L’algoritmo X509 da usare. Il default è impostato all’implementazione Sun algorithm (SunX509). Per le JVM di IBM il valore dovrebbe essere impostato a IbmX509. Per altri fornitori, consultare la - 79 - CAP. 4 - TECNOLOGIE SOFTWARE documentazione JVM per il valore corretto. truststoreFile Il file di TrustStore da usare per validare i certificate dei client. La password per accedere al TrustStore. Il truststorePass default è impostato al valore dell’attributo keystorePass. Dopo aver completato queste modifiche alla configurazione, è necessario riavviare il Tomcat come si fa normalmente, e tutto dovrebbe essere pronto. Dovrebbe essere possibile accedere attraverso SSL a qualsiasi applicazione web supportata da Tomcat. Per esempio, si può provare con: https://localhost:8443 e dovrebbe essere visualizzata la pagina di introduzione del Tomcat (a meno che non sia stato cambiato il ROOT dell’applicazione web). Configurazione della Web Application, file web.xml // Impostazione del keyStore System.setProperty("javax.net.ssl.keyStore","D:/J Telemed/.keystore"); System.setProperty("javax.net.ssl.keyStorePasswor d","changeit"); - 80 - CAP. 4 - TECNOLOGIE SOFTWARE <security-constraint> <web-resource-collection> <web-resource-name>JTelemedSite</web-resourcename> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>CLIENT-CERT</auth-method> <realm-name>JTelemed</realm-name> </login-config> Configurazione del Tomcat, file server.xml - 81 - CAP. 4 - TECNOLOGIE SOFTWARE <!-- Define a SSL HTTP/1.1 Connector on port 8443 --> <Connector keystoreFile=".keystore" keystorePass="changeit" clientAuth="true" acceptCount="100" disableUploadTimeout="true" enableLookups="false" maxHttpHeaderSize="8192" maxSpareThreads="75" maxThreads="150" minSpareThreads="25" port="8443" scheme="https" secure="true" sslProtocol="TLS"/> Configurazione del Web Service, file web.xml <security-constraint> <web-resource-collection> <web-resource-name>JTelemedSite</web-resourcename> <url-pattern>/services/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> - 82 - CAP. 4 - TECNOLOGIE SOFTWARE </user-data-constraint> </security-constraint> <login-config> <auth-method>CLIENT-CERT</auth-method> <realm-name>JTelemed</realm-name> </login-config> - 83 - CAP. 4 - TECNOLOGIE SOFTWARE 4.3 STRUMENTI DI SVILUPPO 4.3.1 Apache Struts Il modello di sviluppo più diffuso per la realizzazione del front-end di applicazioni web mediante il linguaggio Java è sicuramente quello delle JSP (Java Server Pages): con questa tecnologia separazione tra viene implementato interfaccia utente e un pattern generazione di del contenuto. L'obiettivo è permettere al designer Html di cambiare il layout complessivo della pagina a prescindere dal suo contenuto, che verrà generato poi dinamicamente a runtime, tramite accesso ad un Dbms o ad altri meccanismi di persistenza delle informazioni. Questo modello ha da un lato il pregio assoluto della semplicità di comprensione e di utilizzo (la pagina Jsp viene poi compilata in una servlet e gestita dall'application server in modo trasparente al programmatore) ma dall'altro non è sufficientemente attrezzato per affrontare applicazioni che vadano oltre una certa complessità. Mancano infatti logiche sufficientemente sofisticate di ripartizione dei ruoli all'interno dei programmi applicativi da un lato e di controllo complessivo del set delle pagine costruite dall'altro. Si può cercare di superare questa - 84 - CAP. 4 - TECNOLOGIE SOFTWARE difficoltà utilizzando un adattamento all'ambiente web di un pattern ben noto di costruzione di applicazioni interattive: il cosiddetto Model-View-Controller. Secondo questo pattern, i componenti di un'applicazione vengono logicamente classificati a seconda che siano gestori di logiche di business e di accesso ai dati (model), gestori di visualizzazione dati e dell'input dell'utente (viewer o view) e gestori del flusso di controllo complessivo dell'applicazione (controller). Fig. 4.3 – View Model Nel mondo Java, questo pattern viene generalmente indicato come Model2 (dove per Model1 si intende per retrocompatibilità standard di la generalizzazione funzionamento implementazione di delle riferimento si della JSP): ha modalità una in sua Struts, sottoprogetto del Jakarta Project; il progetto della Apache - 85 - CAP. 4 - TECNOLOGIE SOFTWARE software Foundation che raccoglie tecnologie basate su Java per utilizzi specifici web. Struts è uno strato software di controllo flessibile, che utilizza tecnologie standard ResourceBundles) e programmatore un Xml e Java mette (Servlet, a controller Beans, disposizione sofisticato del per l'implementazione pratica del pattern Model2. Il notevole vantaggio di questa astrazione è che Struts è essenzialmente agnostico rispetto alle scelte di chi faccia poi il model ed il view. In pratica vengono utilizzati componenti Ejb oppure Jdbc o plain Jdo per costruire il model, mentre per il view si lavora ancora con le Jsp (a questo punto viste come componenti di un framework più "robusto" e non come costituenti l'intero framework applicativo a disposizione del programmatore) o con altri sistemi di template come Velocity o Xslt. Fig. 4.4 – Struttura di Struts - 86 - CAP. 4 - TECNOLOGIE SOFTWARE In sostanza, un'applicazione web basata su Struts utilizza un deployment descriptor (descrittore dell'impiego dell'applicazione: fisicamente si tratta di un documento Xml) per inizializzare dell'applicazione; ActionForms, l'interzione risorse che che tra includono raccolgono le risorse principalmente l'input dall'utente, ActionMappings che indirizzano poi l'input ad opportune Actions (programmi server-side, senza funzionalità di presentazione) e ActionForwards che selezionano le pagine di output indicate. Anche la validazione dell'input utente può essere definita in fase di deployment attraverso un'altro descrittore Xml utilizzato dalle risorse ActionForms: lo Struts Validator. Abbiamo quindi un esempio di approccio alla realizzazione di sistemi di tipo extremely late binding, dove addirittura alcune scelte sul workflow complessivo del sistema possono essere riconsiderate in fase di deployment: questo tipo di logica garantisce la massima flessibilità nell'utilizzo dei componenti base dell'applicazione e permette di affrontare situazioni dove venga richiesta una notevole complessità nella dell'applicazione. - 87 - presentazione web CAP. 4 - TECNOLOGIE SOFTWARE Fig. 4.5 – Flusso di struts • Request: Richiesta HTTP • Dispatch: Chiama una action passandogli il form bean creato • Update: Crea un form bean con i dati della form HTML • Forward: Chiama una pagina JSP • Extract: Crea un bean con i dati risultanti • Response: Produce una risposta HTTP - 88 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA CAPITOLO 5 CRITTOGRAFIA A CHIAVE PUBBLICA Descrizione delle tecniche di crittografia dei dati digitali e di firma digitale, utilizzo dei certificati. 5.1 Crittografia 5.1.1 Introduzione 5.1.2 Crittografia a chiave simmetrica 5.1.3 Sistemi a crittografia a chiave pubblica 5.2 La firma digitale 5.2.1 Algoritmo RSA 5.2.2 Algoritmo SHA-1 5.3 Certificati elettronici 5.3.1 Standard X.509 per i certificati 5.3.2 Necessità di coppie di chiavi distinte per cifratura e firma 5.4 Infrastruttura a chiave pubblica (PKI) 5.4.1 Requisiti di una PKI 5.5 Gestione dei certificati 5.5.1 Ciclo di vita dei certificati - 89 - CAP. 5 - CRITTOGRAFIA 5.5.2 La gestione delle chiavi 5.5.3 La revoca di un certificato 5.5.4 I cammini di certificazione - 90 - A CHIAVE PUBBLICA CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 5.1 CRITTOGRAFIA 5.1.1 Introduzione La crittografia è lo studio della codifica e della decodifica dei dati. Il termine crittografia viene dalle parole greche kryptos che significa nascosto, e graphia, che significa scrittura. La crittografia riveste un ruolo preminente nell'ambito dei meccanismi di sicurezza, essendo in grado di fornire la maggior parte dei servizi contemplati nell'architettura di sicurezza stabilita dall’ISO (International Organization for Standardization); pertanto i sistemi crittografici rivestono un’importanza fondamentale nella soluzione di molte problematiche di sicurezza connesse con la protezione di informazioni. Per inquadrare il problema è opportuno rifarsi al modello di riferimento proposto dall’ISO che individua cinque classi di funzionalità necessarie per garantire il desiderato livello di sicurezza. Queste sono: 1. confidenzialità: con tale funzionalità si vuole impedire di rilevare informazioni riservate ad entità non autorizzate alla conoscenza di tali informazioni; la confidenzialità è - 91 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA ottenuta tramite tecniche crittografiche di cifratura dei dati. 2. integrità dei dati: i servizi di integrità dei dati proteggono contro gli attacchi attivi finalizzati ad alterare illegittimamente il valore di un dato; l’alterazione di un messaggio può comprendere la cancellazione, la modifica, o il cambiamento dell’ordine dei dati 3. autenticazione: i servizi di autenticazione garantiscono l’accertamento dell’identità; quando a qualcuno (o a qualcosa) si attribuisce una certa identità, i servizi di autenticazione forniscono lo strumento con cui verificare la correttezza di tale affermazione di identità; i servizi di autenticazione si suddividono in: - servizi di autenticazione dell’entità: in questo caso si autentica l’identità presentata ad un entità remota che partecipa ad una sessione di comunicazione (le password sono un esempio tipico di strumenti atti ad ottenere autenticazione dell’entità). - servizi di autenticazione dell’origine dei dati: in questo caso si autentica l’identità che l’originatore di un messaggio rivendica di avere; 4. controllo degli accessi: una volta che l’autenticazione è avvenuta, è possibile eseguire un controllo degli accessi in modo tale da verificare che vengano utilizzate solo quelle risorse o servizi ai quali si è autorizzati. 5. non ripudio: tali servizi hanno la finalità di impedire che un’entità riesca successivamente - 92 - a rinnegare con CAP. 5 - CRITTOGRAFIA successo la partecipazione a A CHIAVE PUBBLICA transazione o comunicazione a cui in realtà ha preso parte; i servizi di non ripudio non prevengono il ripudio di una comunicazione o di una transazione, forniscono, invece, gli elementi per dimostrare in caso di contenzioso l’evidenza dei fatti. Se è vero che autenticazione ed integrità dei dati sono elementi essenziali nell’implementazione di un servizio di non ripudio senza, il non ripudio, tuttavia, va oltre le problematiche di autenticazione ed integrità: è, infatti, lo strumento con cui dimostrare ad una terza parte e dopo l’accadimento del fatto che una comunicazione o transazione è stata originata o avviata da una certa entità o consegnata ad una certa entità. Si pensi ad esempio al caso di una persona, Alice, che invia un ordine di acquisto di beni a Bob; la paternità dell’ordine di acquisto viene attribuita con assoluta certezza ad Alice se si adotta un servizio corretto di non ripudio; in caso di contenzioso Bob può dimostrare con assoluta certezza che l’ordine di acquisto è stato effettuato da Alice. Nei documenti cartacei, quali contratti, ordini, bonifici, è la firma autografa ad essere utilizzata per garantire il servizio di non ripudio, nei documenti elettronici è, invece, la tecnica crittografica di firma digitale. Le principali tecniche crittografiche, cifratura e firma digitale, costituiscono i - 93 - componenti basilari CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA nell’implementazione di tutti i servizi di sicurezza sopra descritti. Alla base delle tecniche crittografiche si colloca l’algoritmo crittografico che fornisce le funzioni di trasformazione di un testo da testo in chiaro a testo cifrato. Gli algoritmi crittografici si suddividono in due classi distinte: algoritmi a chiave simmetrica e algoritmi a chiave pubblica. Fino a poco più di vent’anni fa erano conosciuti solo gli algoritmi a chiave simmetrica; nel 1976 Diffie ed Hellman presentarono un protocollo per lo scambio di una chiave segreta attraverso un canale insicuro; tale meccanismo era stato inteso essenzialmente per risolvere il problema dell’avvio di un normale sistema di cifratura a chiavi simmetriche ma in realtà ha posto le basi della crittografia a chiave pubblica. 5.1.2 Crittografia a chiave simmetrica Gli algoritmi simmetrici utilizzano una singola chiave matematica sia per la cifratura che per la decifratura dei dati. Un messaggio sicuro viene cifrato per il destinatario utilizzando una chiave nota solamente al mittente ed al destinatario. Il principio matematico su cui si basa l’algoritmo è che cifrando un testo in chiaro con una certa chiave si ottiene nuovamente il testo in chiaro (vedi Figura 1): - 94 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Dk (Ek (P )) = P con P = testo in chiaro, E = operazione di cifratura, D = operazione di decifratura, K = chiave Fig. 5.1: Schema di funzionamento di un algoritmo simmetrico Il principale problema implementativo di algoritmi a chiave simmetrica è dovuto alla difficoltà di distribuire le chiavi in modo sicuro e su larga scala. Infatti, l’intera sicurezza dell’algoritmo si basa sulla segretezza della chiave che deve essere conosciuta solo dai due interlocutori e pertanto si pone il problema di come poter distribuire tale chiave in maniera sicura ( non si potranno utilizzare canali intrinsecamente insicuri come, ad esempio,Internet).Inoltre , se si assume che una chiave distinta è richiesta per ogni coppia di interlocutori, il numero di chiavi da distribuire aumenta drasticamente all’aumentare delle parti in gioco; se, infatti , n sono le parti coinvolte , il numero di chiavi da distribuire è pari a n*(n-1)/2. A questo va aggiunto che un protocollo di comunicazione che utilizza crittografia a chiave - 95 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA simmetrica non mette a disposizione che garantiscono l’integrità e l’autenticazione dell’origina dei dati. Infatti , un intrusore che riesce a conoscere la chiave segreta di due interlocutori , può poi , intercettando i messaggi scambiati , alterarne il contenuto , inficiandone cossi l’integrità . Inoltre , il destinatario non è più in grado di distinguere se i messaggi provengono dal mittente originale o dall’ intrusore e quindi non ha più nessuna garanzia di autenticazione. Gli algoritmi simmetrici più diffusi sono CAST , DES , Triple DES, RC2, RC4, RC5, IDEA. 5.1.3 Sistemi a crittografia a chiave pubblica Gli algoritmi a chiave pubblica (altrimenti detti asimmetrici) , sono progettati in modo tale che la chiave di cifratura risulta differente dalla chiave di decifratura. Quello che viene cifrato con la chiave di cifratura, chiave pubblica, può essere decifrato solo con l’altra chiave di decifratura , detta chiave privata. La conoscenza della chiave pubblica non permette di determinare quella privata (si tratta di un problema computazionalmente complesso). Questo è il motivo per cui la chiave di decifratura può essere resa pubblica senza compromettere la sicurezza del sistema. C= E(Kpub , P) P= D(Kpriv ,C) P = testo in chiaro, C = testo cifrato - 96 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA E = operazione di cifratura, Kpub = chiave pubblica del destinatario D = operazione di decifratura, Kpriv = chiave privata del destinatario Fig. 5.2: Schema di funzionamento di un algoritmo a chiave pubblica Con tali algoritmi, a differenza di quelli simmetrici, non è più necessario prevedere un canale sicuro per la trasmissione della chiave, in quanto, essendo la chiave di decifratura distinta da quella di cifratura, è possibile distribuire quest’ultima in maniera non riservata tramite dei server pubblici. Se, poi, n sono gli utenti coinvolti, n è anche il numero di chiavi da distribuire e non n*(n-1)/2 come nel caso degli algoritmi simmetrici. Gli algoritmi asimmetrici garantiscono la confidenzialità nella comunicazione. Infatti, un messaggio cifrato con la chiave pubblica del destinatario fa sì che solo quest’ultimo sia in grado di decifrare tale messaggio, in quanto è l’unico che possiede la corrispondente chiave privata. Inoltre - 97 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA invertendo l’utilizzo delle chiavi, ossia cifrando con la chiave privata del mittente e decifrando con la chiave pubblica del mittente, è possibile garantire l’autenticazione. È su tale principio che si basa la firma digitale. - 98 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 5.2 LA FIRMA DIGITALE Fino a pochissimo tempo fa la firma autografa era considerata l’unico strumento adeguato per attribuire certezza la paternità di un messaggio: un documento originale, stampato su carta e firmato in maniera autografa, è ritenuto, infatti, legalmente valido. Tale validità legale è attribuita ritenendo che: • Sia possibile rilevare alterazioni o abrasioni della carta che ne indicono una contraffazione. • Sia possibile ricondurre con certezza (quantomeno “legale”) la firma apposta in calce ad un documento alla persona manifestazione la cui della firma sua è apposta, volontà di e alla sottoscrivere accettare o richiedere quanto esposto nel documento stesso. La certezza dell’autenticità della firma, come è ben evidente a chiunque abbia falsificato la firma di un conoscente, è assai lontana dalla realtà. Nonostante questo, la legge tutela questa forma di manifestazione della volontà e la considera talmente valida da richiedere che certi documenti, di cui si ipotizza una eventuale futura contestazione, debbano essere conservati per lunghi periodi di tempo. - 99 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA La firma digitale (digital signature), invece, è, oggi, la tecnologia con cui possono essere effettivamente soddisfatti tutti i requisiti richiesti per dare validità legale ad un documento elettronico firmato digitalmente; garantisce i servizi di integrità, autenticazione e non ripudio. Vediamo quali sono le proprietà richieste al processo di apposizione di una firma digitale (proprietà che nel caso della firma autografa si assume siano garantite): • non falsificabili: la firma è la prova che solo il firmatario nessun altro ha apposto la firma sul documento. • non riusabilità: la firma fa parte integrante del documento non deve essere utilizzabile su un altro documento. • non alterabilità: una volta firmato, il documento non deve poter essere alterato. • non contestabilità: il firmatario non può rinnegare con successo la paternità dei documenti firmati; la firma attesta la volontà del firmatario di sottoscrivere quanto contenuto nel documento. La firma crittografiche digitale a viene chiave realizzata pubblica tramite insieme tecniche all’utilizzo di particolari funzioni matematiche, chiamate funzioni hash unidirezionali. Il processo di firma digitale passa attraverso tre fasi: - 100 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 1. Generazione dell’impronta digitale 2. Generazione della firma 3. Apposizione della firma Nella prima fase viene applicata al documento in chiaro una funzione di hash appositamente studiata che produce una stringa binaria di lunghezza costante e piccola, normalmente 128 o 160 bit, chiamata digest message o digital fingerprint ossia impronta digitale. Queste funzioni devono avere due proprietà: • unidirezionalità, ossia dato x è facile calcolare f(x), ma data f(x) è computazionalmente difficile risalire a x. • prive di collisioni (collision-free), ossia a due testi diversi deve essere computazionalmente impossibile che corrisponda la medesima impronta. ( x ≠ y allora f(x) ≠ f(y) ) - 101 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Figura 1.3: Schema di funzionamento di una funzione hash L’utilità dell’uso delle funzioni hash consente di evitare che per la generazione della firma sia necessario applicare l’algoritmo di cifratura, che è intrinsecamente inefficiente, all’intero testo che può essere molto lungo. Inoltre consente l’autenticazione, da parte di una terza parte fidata, della sottoscrizione di un documento senza che questa venga a conoscenza del suo contenuto1. La seconda fase, la generazione della firma, consiste semplicemente nella cifratura con la propria chiave privata dell’impronta digitale generata il precedenza. In questo modo la firma risulta legata, da un lato (attraverso la chiave privata usata per la generazione) al soggetto sottoscrittore, e dall’altro (per il tramite dell’impronta) al testo sottoscritto. In realtà l’operazione di cifratura viene effettuata, anziché sulla sola impronta, su una struttura di dati che la contiene insieme - 102 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA con altre informazioni utili, quali ad esempio l’indicazione della funzione hash usata per la sua generazione. Sebbene tali informazioni possano essere fornite separatamente rispetto alla firma, la loro inclusione nell’operazione di codifica ne garantisce l’autenticità. Nell’ultima fase, la firma digitale generata precedentemente viene aggiunta in una posizione predefinita, normalmente alla fine, al testo del documento. Di solito, insieme con la firma vera e propria, viene allegato al documento anche il valore dell’impronta digitale ed eventualmente il certificato2 da cui è possibile recuperare il valore della chiave pubblica. La firma digitale così generata viene aggiunta in una posizione predefinita, normalmente alla fine, al testo del documento. Normalmente, insieme con la firma vera e propria,viene allegato al documento anche il valore dell’impronta digitale ed eventualmente il certificato da cui è possibile recuperare il valore della chiave pubblica. L’operazione di verifica della firma digitale viene effettuata ricalcolando, con la medesima funzione di hash usata nella fase di firma, il valore dell’impronta digitale (hash “fresco”); poi si decifra con la chiave pubblica del firmatario la firma (hash firmato) e si controllando che il valore così ottenuto coincida con hash “fresco”. Gli algoritmi per la firma digitale più utilizzati sono RSA, mentre per le funzioni hash abbiamo SHA-1, RIPEMD-160 - 103 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 5.2.1 Algoritmo RSA Proposto nel 1978 da Rivest, Shamir e Adleman [RSA78], da cui il nome, è il primo sistema di crittografia a chiavi pubbliche basato sui concetti proposti da Diffie ed Hellman ed è anche quello attualmente più diffuso ed utilizzato. Il metodo si basa sulla fattorizzazione di interi di grandi dimensioni e per utilizzarlociascun interlocutore deve compiere le seguenti azioni: Scegliere due grandi interi p e q che siano primi; il loro prodotto corrisponde al valore di N chiamato modulo, in quanto utilizzato nel calcolo del modulo nelle operazioni di codifica e decodifica. Scegliere un intero e, che sia primo rispetto a T = (p-1) * (q-1) detta funzione di Eulero, ossia tale che tra T ed e non ci siano fattori comuni eccetto 1. Si ottiene così la chiave pubblica di codifica Kp = (e,N). Calcolare l’intero d per il quale risulta e * d mod T = 1, ossia trovare l'intero d per cui (e*d-1) sia divisibile per T. Si ottiene così la chiave segreta di decodifica Ks = (d,N). - 104 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Il messaggio cifrato X corrispondente ad un messaggio M si ottiene dalla relazione: X=Me mod N mentre la decodifica avviene secondo la relazione: Xd mod N = (Me mod N)d mod N = Med mod N = M. La sicurezza dello RSA è affidata alla difficoltà di determinare i fattori primi di un intero quando questo è molto grande, difficoltà che aumenta in modo esponenziale al crescere del numero di bit usati per la chiave. Infatti se un intrusore riuscisse a sapere che N è fattorizzato in p e q, allora potrebbe calcolare T e conoscendo d risalire a c tramite l'equazione e*d mod T = 1. Va osservato che tale algoritmo può essere utilizzato sia per cifrare un documento sia per firmarlo digitalmente. Per far ciò è sufficiente invertire la sequenza di utilizzo delle chiavi: il mittente firma il documento con la propria chiave privata e poi il destinatario verifica la tale firma con la chiave pubblica del mittente. Ciò è possibile in virtù della seguente proprietà dell’aritmetica modulare: (Me mod N)d mod N = (Md mod N)e mod N = Med mod N = M. - 105 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA La lunghezza della chiave dipende dal grado di sicurezza richiesto; tipici valori sono 512, 1024 o 2048 bit. Esempio: p=3 e q=11 N=p*q=33 T=(p-1)*(q-1)=20 e=7 e*d mod T = 1 bisogna trovare un q intero tale che T*x+1 sia un multiplo di e x=1 d = (T*x+1)/e = 3 chiave privata = (7 , 33) chiave pubblica = (3 , 33) M = 15 X = (15)^3 mod 33 = 9 (9)^ 7mod 33 = 15 = M 5.2.2 Algoritmo SHA-1 La funzione hash SHA (Secure Hash Algorithm) produce un digest message di 160 bit per ogni messaggio di lunghezza inferiore a 2^64 bit. Vediamo le principali fasi algoritmiche [S96]: Il messaggio viene completato con dei bit per ottenere multipli di 512 bit (padding): all'inizio del messaggio si mette un 1 seguito da tanti zeri quanti sono necessari affinché il messaggio sia più corto di 64 bit di un multiplo di - 106 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 512 bit, mentre alla fine del messaggio si mettono 64 bit che danno la lunghezza del messaggio prima del padding. Vengono inizializzate 5 variabili di 32 bit l'una (A, B, C, D, E). L'algoritmo processa blocchi di 512 bit alla volta; ogni blocco viene processato attraverso 4 funzioni non lineari, ognuna applicata 20 volte. Senza entrare nei dettagli implementativi di queste funzioni che fondamentalmente eseguono operazioni 18 logiche (and, or, not, xor) e operazioni di shifting, abbiamo come risultato finale che ogni blocco di 512 bit processato modifica le cinque variabili iniziali. Una volta processati tutti i blocchi che costituiscono il messaggio, concatenando tali variabili otteniamo un hash a 5 x 32 = 160 bit del messaggio. Tuttora non esistono attacchi crittografici contro SHA-1; inoltre, producendo un hash a 160 bit, risulta più robusto ad attacchi di forza - bruta rispetto a MD4 e MD5 che producono un message digest di 128 bit. - 107 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 5.3 CERTIFICATI ELETTRONICI Nella tecnologia di crittografia a chiave pubblica sia in fase di cifratura sia in fase di verifica di una firma digitale occorre ritrovare la chiave pubblica o del destinatario di un messaggio o del firmatario del messaggio firmato. In entrambi i casi il valore delle chiavi pubbliche non è confidenziale; la criticità del reperimento delle chiavi sta nel garantire non la confidenzialità, ma l’autenticità delle chiave pubbliche, ossia sta nell’assicurare che una certa chiave pubblica appartenga effettivamente all’interlocutore per cui si vuole cifrare o di cui si deve verificare la firma. Se, infatti, una terza parte prelevasse la chiave pubblica del destinatario sostituendola con la propria, il contenuto dei messaggi cifrati sarebbe disvelato e le firme digitali potrebbero essere falsificate. La distribuzione delle chiavi pubbliche è, pertanto, il problema cruciale della tecnologia a chiave pubblica. In un dominio con un numero limitato di utenti si potrebbe anche ricorrere ad un meccanismo manuale di distribuzione delle chiavi: due interlocutori che abbiano una relazione di conoscenza già stabilita, potrebbero, ad esempio, scambiarsi reciprocamente le chiavi attraverso floppy disk. Meccanismi di distribuzione manuale diventano, tuttavia, assolutamente inadeguati ed impraticabili - 108 - in dominio CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA scalabile dove non c’è alcuna diretta conoscenza prestabilita tra gli interlocutori. Il problema della distribuzione delle chiavi pubbliche è risolto tramite l’impiego dei certificati elettronici. I certificati a chiave pubblica costituiscono, infatti, lo strumento affidabile e sicuro attraverso cui rispondere ad esigenze di scalabilità; attraverso i certificati elettronici le chiavi pubbliche vengono distribuite e rese note agli utenti finali con garanzia di autenticità e integrità. L’utilizzo dei certificati elettronici presuppone, tuttavia, l’esistenza di una Autorità di Certificazione (Certification Authority o CA) che li emetta e li gestisca. Ogni certificato è una struttura dati costituita da una parte dati contenente al minimo: • informazioni che identificano univocamente il possessore di una chiave pubblica (ad esempio il nome); • il valore della chiave pubblica; • il periodo di validità temporale del certificato; • la firma digitale della autorità di certificazione con cui si assicura autenticità della chiave ed integrità delle informazioni contenute nel certificato; La semplicità del meccanismo di distribuzione delle chiavi è diretta conseguenza delle caratteristiche stesse dei certificati: i certificati, infatti, possono essere distribuiti senza dover necessariamente ricorrere ai tipici servizi di sicurezza di confidenzialità, integrità, e comunicazioni. - 109 - autenticazione delle CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Per le proprietà della crittografia a chiave pubblica non c'è infatti alcun bisogno di garantire la riservatezza del valore della chiave pubblica; durante il processo di distribuzione, poi, non ci sono requisiti di autenticazione ed integrità dal momento che il certificato è per sua costituzione una struttura già protetta (la firma digitale dell'autorità di certificazione sul certificato fornisce, infatti, sia autenticazione sia integrità). Se, quindi, un intrusore tentasse, durante la pubblicazione del certificato, di alterarne il contenuto, la manomissione sarebbe immediatamente rilevata in fase di verifica della firma sul certificato; il processo di verifica fallirebbe e l’utente finale sarebbe avvertito della non integrità della chiave pubblica contenuta nel certificato. Le caratteristiche stesse, quindi, del certificato permettono di distribuire i certificati a chiave pubblica anche mediante canali non sicuri (file server insicuri o sistemi di directory o protocolli di comunicazione intrinsecamente insicuri). 5.3.1 Standard X.509 per i certificati Lo standard ormai diffusamente riconosciuto di definizione del formato dei certificati è quello descritto nello standard X.509 ISO/IEC/ITU (Visa e MasterCard hanno ad - 110 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA esempio adottato le specifiche X.509 come base per la definizione dello standard per il commercio elettronico SET, Secure Electronic Transaction). Tale standard è giunto alla terza versione (X.509v3), dopo che le precedenti due versioni si erano dimostrate insufficienti a risolvere certe problematiche. Vediamo da quali campi è composto un certificato: • Version: indica la versione del formato del certificato (1, 2 o 3) • Serial number: è un codice numerico che identifica il certificato tra tutti i certificati emessi dall’Autorità di Certificazione • Signature Algorithm: specifica l’algoritmo utilizzato dalla CA per firmare il certificato; è data dalla coppia funzione hash – algoritmo a chiave pubblica • Issuer X.500 Name: è il nome della CA; è fornito secondo lo standard di naming X.500 • Validity Period: specifica la data e l’ora di inizio validità e di fine validità del certificato • Subject X.500 Name: nome del possessore del certificato • Subject Public Key Information: contiene il valore della chiave pubblica del possessore del certificato e l’algoritmo con cui tale chiave viene usata - 111 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA • Issuer Unique Identifier: è una stringa di bit aggiuntivi, opzionale, usata nel caso in cui nella struttura ad albero ci siano due CA • Subject Unique Identifier: è una stringa di bit aggiuntivi, opzionale, usata nel caso di omonimia di due membri in una stessa CA • CA Signature: è la firma digitale della CA sul certificato Nella aggiunto questo versione un 3, rispetto alle ulteriore campo è campo precedenti, (standard suddiviso in tre è stato extention); sottoinsiemi: l’identificatore del tipo di estensione, un indicatore di criticità e il valore effettivo dell’estensione; l’indicatore di criticità facilita l’interoperabilità tra sistemi che non utilizzano certificati con determinate estensioni e sistemi che, invece, interpretano tutte le estensioni definite a livello di standard; l’indicatore di criticità è semplicemente un flag che stabilisce se l’estensione è critica o non critica; se l’estensione non è critica, significa che il sistema che deve elaborare il certificato può eventualmente ignorare l’estensione in questione se non è in grado di interpretarla. Le estensioni standard definite nei certificati X.509v3 si suddividono in quattro gruppi: - 112 - CAP. 5 - CRITTOGRAFIA • estensioni contenenti informazioni A CHIAVE PUBBLICA sulla chiave pubblica che specificano il tipo di utilizzo per cui è stata emessa una certa chiave. • estensioni contenenti informazioni aggiuntive relative all’Autorità di Certificazione e all’utente possessore del certificato (ad esempio nomi alternativi per la CA e per l’utente). • estensioni contenenti informazioni sulle politiche di emissione e sulle finalità di utilizzo dei certificati (le estensioni “certificate policy3” e “policy mapping4”). • estensioni contenenti informazioni sui vincoli di spazio dei nomi o ritrovamento di politica o la da verifica imporre di un durante il certificato appartenente ad un dominio di fiducia esterno. 5.3.2 Necessità di coppie di chiavi distinte per cifratura e firma L’algoritmo a chiave pubblica più utilizzato, RSA, ha la proprietà di permettere sia operazioni di cifratura sia operazioni di firma digitale con la stessa coppia di chiavi crittografiche; tuttavia un uso doppio della coppia di chiavi è sconsigliato. I motivi sono deducibili dall’esame dei requisiti di gestione che sono differenti nel caso in cui le chiavi siano utilizzate per la cifratura o per la firma digitale. - 113 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Per la coppia di chiavi di firma digitale si devono, infatti, rispettare i seguenti requisiti: • la chiave privata di firma deve essere custodita durante l’intero periodo di validità in modo tale che solo il suo legittimo possessore possa averne accesso; tale requisito è un presupposto fondamentale per fornire supporto a servizi di non ripudio (vedremo nel terzo capitolo come la legge italiana imponga che la chiave di firma sia custodita all’interno dello stesso dispositivo in cui sarà utilizzata.) • una chiave privata di firma non deve essere sottoposta a backup per proteggersi contro perdite accidentali della chiave privata; se la chiave di firma, infatti, viene accidentalmente persa, una nuova coppia di chiavi di firma può sottoporre essere a facilmente backup la generata, chiave privata mentre di firma contrasterebbe con il primo requisito • una chiave privata di firma non necessita di essere archiviata; l’archiviazione contrasterebbe con il primo requisito; una chiave privata di firma deve essere distrutta in modo sicuro allo scadere della sua validità; se, infatti, si potesse risalire al suo valore anche dopo la cessazione del suo utilizzo, tale chiave privata potrebbe ugualmente essere utilizzata per falsificare la firma su vecchi documenti (per evitare ciò, si può ricorrere a servizi di validazione temporale.) - 114 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA • una chiave pubblica di firma necessita di essere archiviata; tale chiave potrebbe essere utilizzata per la verifica di firme apposte su documenti in tempi successivi alla scadenza della validità della coppia di chiavi di firma. Per la coppia di chiavi di cifratura, invece, si devono rispettare i seguenti requisiti, in contrasto con i precedenti: • la chiave privata, utilizzata nelle operazioni di decifratura, necessita di essere sottoposta a backup o archiviata; infatti, se una chiave privata venisse persa (ad esempio a causa della corruzione del dispositivo di protezione della chiave o per dimenticanza da parte del legittimo possessore del segreto utilizzato per sbloccare l’accesso al dispositivo di protezione) e non ci fossero meccanismi di recupero della chiave (key recovery), sarebbe inaccettabile perdere irrimediabilmente l’accesso ai dati cifrati con quella chiave. • la chiave pubblica utilizzata per la cifratura dei dati non necessita generalmente di archiviazione; l’algoritmo utilizzato determina se un backup della chiave pubblica è necessario o meno (se si utilizza un meccanismo di trasporto delle chiavi basato su RSA, il backup non è necessario, può diventarlo se si utilizza, - 115 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA invece, l’algoritmo Diffie-Hellmann per lo scambio delle chiavi). • la chiave privata non deve essere distrutta allo scadere della sua validità di impiego per poter continuare a decifrare in futuro i documenti cifrati col la corrispondente chiave pubblica. Alla luce delle considerazioni elencate è evidente, quindi, che, se si impiegasse un’unica coppia di chiavi sia per la cifratura sia per la firma, sarebbe impossibile garantire il soddisfacimento contemporaneo di tutti i requisiti. Inoltre si tenga presente che: • le realizzazioni a chiave pubblica utilizzate a supporto della cifratura sono tipicamente soggette a limiti di esportazioni molto più restrittivi rispetto a quelli previsti per la firma digitale • le chiavi di firma e di cifratura possono avere validità temporalidifferenti per motivi di politica organizzativi/gestionali e di sicurezza. • non tutti gli algoritmi a chiave pubblica hanno la proprietà dell’algoritmo RSA di effettuare sia cifratura sia firma con la stessa coppia di chiavi. - 116 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 5.4 INFRASTRUTTURA A CHIAVE PUBBLICA Le infrastrutture a (PKI) chiave pubblica (Public Key Infrastructure) forniscono il supporto necessario affinché la tecnologia di crittografia a chiave pubblica sia utilizzabile su larga scala. Le infrastrutture offrono servizi relativi alla gestione delle chiavi e dei certificati e delle politiche di sicurezza. Le autorità di certificazione e la problematica di gestione dei certificati elettronici costituiscono, infatti, il cuore delle infrastrutture a chiave pubblica. Una infrastruttura a chiave pubblica introduce il concetto di third-party trust, ossia di quella situazione che si verifica quando due generiche entità si fidano implicitamente l’una dell’altra senza che abbiano precedentemente stabilito una personale relazione di fiducia. Questo è possibile perché entrambe le entità condividono una relazione di fiducia con una terza parte comune. - 117 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Fig. 5.3: Third-party trust Third-party qualsiasi trust è un implementazione requisito su larga fondamentale scala che per utilizzi crittografia a chiave pubblica e in una PKI viene realizzata attraverso l’Autorità di Certificazione. Vediamo in dettaglio quali sono i componenti fondamentali di una PKI. Autorità di Registrazione (RA) L’accertamento dell’identità dell’utente richiedente un certificato elettronico, deve precedere l’effettiva emissione del certificato; è indispensabile procedere a tale verifica dato che con l’emissione di un certificato elettronico si rende pubblicamente valida l’associazione tra una certa chiave pubblica e una certa entità. Una volta attestata la validità dell’identità dell’utente attraverso una serie di - 118 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA procedure definite nell’ambito di una precisa politica di sicurezza (ad esempio, il controllo della carta di identità), l’autorità di registrazione ha il compito di abilitare l’utente come appartenente ad uno specifico dominio di fiducia ; la funzionalità di autorità di registrazione può essere espletata dall’autorità di certificazione stessa oppure delegata ad altre entità. Autorità di Certificazione (CA) Costituisce il cuore di una PKI; la sua principale funzione consiste nel creare i certificati elettronici per quegli utenti precedentemente abilitati nella fase di registrazione al dominio di fiducia di cui la CA è garante; un’Autorità di Certificazione non si deve limitare esclusivamente alla generazione dei certificati, ma deve poterne gestire l’intero ciclo di vita. Il ciclo di vita comprende le fasi di generazione, aggiornamento (nel caso in cui il certificato stia per perdere validità temporale), sostituzione (nel caso di scadenza della validità temporale) e revoca nel caso in cui le condizioni di emissione del certificato non siano più valide. Un ulteriore compito della CA è stabilire relazioni di fiducia con altre CA. Sistema di Directory Contiene i certificati a chiave pubblica, reperibili dagli utenti qualora sia necessario, e le liste dei certificati revocati (CRL). Tale sistema costituisce - 119 - un elemento CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA fondamentale per la distribuzione su larga scala delle chiavi pubbliche utilizzate nella cifratura e nella firma dei dati. Il sistema di Directory si basa su un particolare protocollo di comunicazione LDAP (Lightweight Directory Access Protocol) che utilizza il TCP/IP. PKI Database Oltre alla Directory, di solito, c’è un’altra entità dedicata alla memorizzazione delle chiavi: è il database gestito esclusivamente dalla CA nel quale viene fatto un backup delle chiavi e vengono archiviate le chiavi scadute. Questo database, a differenza della Directory, è privato ed è accessibile solo dalla CA. Utenti finali Gli utenti finali (end-entity) sono dotati del software in grado di interagire con la PKI in tutte le fasi in cui sia richiesta un’interazione tra le applicazioni client e la CA o la directory (ad esempio un’interazione fondamentale interviene nella fase di inizializzazione dell’utente, fase nella quale vengono creati i relativi certificati di cifratura e di firma). - 120 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Figura 1 Architettura di una PKI. 5.4.1 Requisiti di una PKI L’implementazione di una infrastruttura a chiave pubblica deve tener conto di una serie di requisiti progettuali che si possono sintetizzare nei seguenti: • scalabilità. • supporto per applicazioni multiple: a beneficio degli utenti finali in termini di convenienza, sicurezza ed economia, una stessa infrastruttura deve garantire il supporto per molteplici applicazioni (posta elettronica sicura, applicazioni Web, trasferimento di file sicuro); pertanto, il modello di gestione della sicurezza deve essere consistente e uniforme per tutte le applicazioni. - 121 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA • interoperabilità tra infrastrutture differenti: non è certo una soluzione praticabile quella di realizzare un’unica infrastruttura rispondente alle necessità di sicurezza di un dominio di utenti su scala globale; è, quindi, evidente che si debba ricorrere a domini di sicurezza distinti, ognuno • amministrato da un’infrastruttura specifica. Tuttavia, l’interoperabilità di tali infrastrutture distinte deve essere assicurata ed è richiesta per garantire il raggiungimento di un buon livello di scalabilità. • supporto per una molteplicità di politiche: cammini di certificazione considerati appropriati per un’applicazione, possono non essere considerati altrettanto validi per un’altra applicazione; è, quindi, necessario implementare meccanismi che permettano da un lato di attribuire politiche differenti ai vari cammini di certificazione dall’altro di associare ad ogni applicazione una politica di sicurezza specifica. • conformità agli standard: una vera interoperabilità tra PKI distinte è ottenibile soltanto con l’adozione di standard che definiscono i protocolli funzionali e di comunicazione relativi ai componenti costitutivi di una infrastruttura a chiave pubblica. - 122 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 5.5 GESTIONE DEI CERTIFICATI 5.5.1 Ciclo di vita dei certificati L’emissione effettiva di un certificato elettronico da parte di un’Autorità di Certificazione a favore di un utente finale deve essere preceduta da una fase di registrazione dell’utente richiedente il certificato elettronico. Attraverso il processo di registrazione l’utente richiedente un servizio di certificazione si identifica presso l’autorità preposta al servizio di registrazione; le credenziali che in questa fase l’utente deve dipendono sottoporre fortemente registrazione definite all’Autorità dalle modalità nell’ambito di di Registrazione e procedure una politica di di sicurezza. Il processo di registrazione stabilisce una relazione iniziale tra utente finale ed Autorità di Certificazione; l’utente finale, una volta attestata l’autenticità della sua identità, viene registrato nel dominio di fiducia gestito dalla CA. L’obiettivo, di primaria importanza del processo di registrazione è, quindi, quello di garantire che la chiave pubblica, di certificazione, cui un certo appartenga utente finale effettivamente richiedente. - 123 - al richiede nome la del CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA Terminata la fase di registrazione, l’utente può richiedere l’emissione di un certificato elettronico. La procedura di generazione di un certificato elettronico consiste dei seguenti passi: • l’utente finale sottopone all’autorità di certificazione le informazioni da certificare. • l’Autorità di Certificazione può verificare l’accuratezza delle informazioni presentate in accordo a politiche e standard applicabili. • l’Autorità di Certificazione firma le informazioni generando il certificato e lo pubblica sul sistema di Directory; di solito, la CA archivia una copia del certificato sul suo database privato; ogni operazione di generazione di certificati elettronici viene registrata su un archivio di registrazione dati. Ogni certificato elettronico generato ha una validità temporale limitata al cui termine va sostituito; il periodo di validità di un certificato, in assenza di compromissioni o usi illeciti, garantisce l’utente che deve utilizzare tale certificato che la chiave pubblica possa essere utilizzata per lo scopo per cui è stata generata e che l’associazione tra la chiave pubblica e le altre informazioni contenute nel certificato sia ancora valida. - 124 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA 5.5.2 La gestione delle chiavi Alla gestione del ciclo di vita dei certificati è strettamente collegata la gestione delle coppie di chiavi (key management); tale gestione comprende le funzioni di generazione, di backup, recupero ed aggiornamento delle chiavi. In generale, le chiavi sono generate dagli stessi utenti finali che ne hanno bisogno (key generation); successivamente, gli utenti possono chiedere alla CA la certificazione di tali chiavi. La generazione e la successiva certificazione costituiscono la fase di inizializzazione dell’utente. La necessità di mantenere copie delle chiavi (key backup) nasce dal fatto che l’eventuale perdita delle chiavi comporta l’impossibilità di decifrare i messaggi cifrati e di verificare i messaggi firmati. Spesso le organizzazioni richiedono un backup delle chiavi dei loro membri per evitare che l’onere di mantenimento delle copie sia esclusivamente di questi ultimi. Tali organizzazioni possono avere la necessità di verificare documenti digitalmente firmati da loro membri anche oltre la validità temporale di tali chiavi e non possono fidarsi unicamente alle eventuali copie di backup fatte dal possessore della chiave. Inoltre c’è sempre la possibilità che un utente si dimentichi la password di protezione delle sue chiavi; - 125 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA pertanto deve essere possibile recuperare le proprie chiavi e di impostare una nuova password (key recovery). Un altro problema nella gestione delle chiavi è l’aggiornamento (key update). Spesso le organizzazioni impongono ai loro membri un aggiornamento a intervalli regolari delle loro chiavi. Questo può comportare una gran mole di lavoro, in particolar modo per organizzazione con un gran numero di partecipanti. Infatti, l’aggiornamento delle chiavi comporta una riemissione del certificato elettronico corrispondente. A questo si aggiunga il fatto che, non raramente, capita che le chiavi dell’intera organizzazione scadono lo stesso giorno. 5.5.3 La revoca di un certificato L’operazione di revoca di un certificato costituisce una fase della gestione del ciclo di vita dei certificati di alta criticità. Il certificato elettronico deve essere revocato in presenza delle seguenti condizioni: - compromissione rilevata o semplicemente sospettata della chiave privata corrispondente alla chiave pubblica contenuta nel certificato. - cambiamento di una qualsiasi delle informazioni contenute nel certificato elettronico o delle condizioni iniziali di registrazione. - 126 - CAP. 5 - CRITTOGRAFIA La revoca del certificato A elettronico CHIAVE PUBBLICA è effettuata dall’Autorità di Certificazione e generalmente viene avviata su richiesta dello stesso utente finale; in questo caso, data la criticità e le implicazioni dell’operazione di revoca, è indispensabile predisporre, un sistema di autenticazione della richiesta di revoca. La difficoltà legata al processo di revoca sta nel garantire una corretta notifica su larga scala dell’avvenuta revoca di un particolare certificato; ogniqualvolta un utente finale intende utilizzare un certificato o per cifrare dati o per verificare una firma, deve essere informato sullo stato di quel certificato. Il meccanismo più comunemente utilizzato per la notifica su larga scala di avvenute revoche fa uso delle cosiddette liste di revoca dei certificati (Certificate Revocation List o CRL); la gestione di tali liste è delegata alla CA; nell’ambito del dominio amministrato, ogni CA pubblica periodicamente una struttura dati contenente l’elenco dei certificati revocati, ossia una lista, firmata digitalmente dall’Autorità di Certificazione, che riporta i certificati revocati, la data temporale in cui è avvenuta la revoca ed eventualmente il motivo della revoca; i motivi per cui anche la CRL deve essere firmata digitalmente sono analoghi a quelli descritti nel caso dei certificati elettronici. La CRL, al pari dei certificati, deve essere pubblicata in modo che sia consultabile da qualunque utente. La frequenza di pubblicazione di una CRL dipende fortemente - 127 - CAP. 5 - CRITTOGRAFIA dalla politica di dell’organizzazione; sicurezza una A CHIAVE PUBBLICA definita determinata all’interno politica potrebbe richiedere la pubblicazione di una nuova CRL ogniqualvolta si richiede tuttavia, una molto revoca; tale dispendiosa procedura dal risulterebbe, punto di vista amministrativo. Generalmente, la CA rilascia liste di revoca dei certificati su base periodica, con intervalli di periodicità definibili a livello di politica di sicurezza. Le liste di revoca giocano un ruolo di fondamentale importanza in tutti i processi crittografici, in quanto devono essere consultate sia in fase di cifratura sia in fase di verifica di una firma. Infatti, prima di cifrare un messaggio, va verificato che la chiave pubblica che si deve usare per la cifratura appartenga ad un certificato valido. Analogo discorso vale per la verifica della firma, poiché una firma può essere ritenuta valida solo se il certificato del firmatario, oltre ad essere autentico e integro, non è stato revocato. La dimensione di una CRL è di fondamentale importanza nelle prestazioni di una PKI. Per ovviare ad un possibile overhead dovuto alle eccessive dimensione della lista di revoca sono stati introdotti i cosiddetti punti di distribuzione delle CRL che consentono di partizionare in modo arbitrario l’intera CRL: ogni partizione è associata ad un punto di distribuzione. In questo modo la massima dimensione che - 128 - CAP. 5 - CRITTOGRAFIA può raggiungere una singola partizione A CHIAVE PUBBLICA è controllata dall’Autorità di Certificazione. Il metodo di distribuzione della notifica di revoca attraverso CRL distribuite periodicamente dalla CA è detto di tipo pull; il nome del metodo deriva dal fatto che, quando necessario, sono i sistemi degli utenti finali a reperire le CRL da un sistema di distribuzione. Il problema, tuttavia, legato al metodo di distribuzione delle notifiche delle revoche tramite CRL, è quello della latenza introdotta. Esistono transazioni in cui è necessario garantire revoca in tempo reale e che non possono tollerare finestre temporali in cui le CRL non sono perfettamente allineate; il metodo sopra descritto presenta proprio la limitazione che la latenza introdotte nelle notifiche delle revoche dipende dalla periodicità di emissione delle CRL. Una possibile soluzione potrebbe essere l’emissione forzata di una CRL fuori dalla sequenza degli intervalli temporali prestabilita da parte della CA; ma se viene avviato un attacco attivo finalizzato ad impedire la pubblicazione di tale lista di revoca fuori sequenza, non c’è un meccanismo affidabile di distribuzione delle CRL che permetta di rilevare con assoluta certezza la mancata pubblicazione di tale CRL. Esistono metodi alternativi di distribuzione delle CRL mirati a risolvere il problema della revoca in tempo reale. Un primo approccio è quello che utilizza un metodo di tipo push in cui è l’Autorità di Certificazione ad inviare in - 129 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA modalità “broadcast” le CRL non appena sopravviene una nuova revoca. Se, da un lato, questo metodo garantisce tempi di distribuzione delle CRL estremamente veloci, tuttavia, dall’altro lato presenta alcuni svantaggi che lo rendono non diffusamente impiegato. Innanzitutto tale metodo richiede una modalità di distribuzione sicura che garantisca che le CRL effettivamente raggiungano i sistemi degli utenti finali previsti; in secondo luogo, esso può dar luogo ad un incremento considerevole di traffico sulla rete; in terzo luogo tale metodo non presenta né uno standard, né una proposta di definizione che ne permetta un’implementazione diffusa. Un altro metodo di notifica delle revoche, alternativo a quelli precedenti, consiste nell’effettuare una transazione online di verifica dello stato del certificato (tale protocollo, definito a livello di Internet draft, è conosciuto con il nome di OCSP, “Online Certificate Status Protocol”); il sistema PKI mette a disposizione un server online dedicato a questo servizio che garantisca disponibilità ed accessibilità continua del servizio. 5.5.4 I cammini di certificazione Se si potesse Certificazione su disporre scala di globale, un’unica il Autorità problema di della distribuzione, del reperimento e della verifica della validità - 130 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA delle chiavi pubbliche non sussisterebbe; tuttavia una tale soluzione non è praticabile per motivi di scalabilità, flessibilità e sicurezza. Diventa, quindi, inevitabile ricorrere ad un modello costituito da Autorità di Certificazione multiple tra loro concatenate secondo differenti modelli organizzativi, detti anche modelli di fiducia. In uno scenario costituito da una molteplicità di Autorità di Certificazione su larga scala, strutturate secondo un certo modello organizzativo, non è pensabile che ogni utente abbia diretta conoscenza delle chiavi pubbliche di ogni potenziale interlocutore, sotto forma di certificato elettronico, o delle chiavi pubbliche delle corrispondenti autorità di certificazione competenti. Occorre, quindi, disporre di un meccanismo corretto di ritrovamento dei certificati elettronici degli interlocutori appartenenti a domini di sicurezza esterni. Il modello generale su cui si basano tutti i sistemi di distribuzione su larga scala delle chiavi pubbliche sotto forma di certificati elettronici, utilizza le cosiddette catene di certificazione, altrimenti conosciute come cammini di certificazione. Il problema del ritrovamento di un cammino di certificazione consiste sostanzialmente nel trovare, se esiste, un cammino di certificazione che permetta di verificare l’autenticità del certificato elettronico di uno specifico utente remoto a partire da un insieme di chiavi pubbliche, assunte come radici del cammino, di cui si ha diretta e sicura conoscenza; la risoluzione del problema, - 131 - CAP. 5 - CRITTOGRAFIA A CHIAVE PUBBLICA quindi, assume per date certe condizioni iniziali: tali condizioni iniziali si identificano nelle chiavi di specifiche autorità di certificazione. - 132 - CAP. 6 - CONCLUSIONI CONCLUSIONI Lo scopo del progetto, era quello di creare una struttura software flessibile che offrisse servizi di teleconsulto e teleassistenza distinguendo in modo inequivocabile la fase di effettuazione dell’ esame dalla fase di refertazione. Il mio compito era quello di progettare le applicazioni dal lato client, ossia dal lato del laboratorio e dal lato del medico, che rispettassero i canoni di sicurezza nello scambio delle informazioni sensibili; altro punto focale erà quello di creare interfacce user-friendly per i client in modo da rendere semplice l’utilizzo delle stesse. Tutto ciò è stato conseguito attraverso l’impiego delle tecnologie precedentemente descritte e con la gentile collaborazione dell’ASUR Marche zona 7 per quanto concerne la parte medico informativa. In prospettiva futura con l’ avvento del documento di identità elettronico è auspicabile, che i servizi che ora sono disponibili solo a livello di personale medico o paramedico, vengano messi a disposizione direttamente per l’ utente finale : il cittadino. Il prossimo passo sarà sfruttare la grande flessibilità con cui è stato progettato MiRo , espandendo le sue funzionalità in modo da rendere il servizio sanitario efficiente e capillare. - 133 - CAP. 6 - CONCLUSIONI Il progetto MiRo è stato presentato alla 2nd Conferenza Internazionale EMMIT 2006: EURO-MEDITERRANEAN MEDICAL INFORMATICS TELEMEDICINE. MiRo: A Virtual Private Network For Telehealth Telehealth Services ROBERTO DI ROSA, MIRCO STURARI, ALDO FRANCO DRAGONI*, GIUSEPPE GIAMPIERI** *DEIT, Dipartimento di Elettronica Intelligenza Artificiale e Telecomunicazioni – Università Politecnica delle Marche, Ancona **ASUR Zona 7, Azienda Sanitaria Locale delle Marche Ancona 1 MiRo Project • CONTEXT software-design to organize telemedicine services • MOTIVATIONS necessity to integrate sanitary services in a wide and delocalized environment • SCOPE to provide a flexible architecture for teleconsulting and telereporting 2 - 134 - and CAP. 6 - CONCLUSIONI Telemedicine represents the erogation of sanitary service at distances through informatics and telecommunications technologies 3 Telemedicine ensures high quality digital information even for complex health services 4 - 135 - CAP. 6 - CONCLUSIONI Telemedicine reaches better results if coordinated with the other applications 5 MiRo - Architecture is configured as a substrate to integrate sanitary systems 6 - 136 - CAP. 6 - CONCLUSIONI MiRo - Finalities • provides a flexible software framework for teleconsulting and services development • integrates with the existing health information system to facilitate and rationalize information interchange • improves and facilitates TCP/IP-based teleconsulting services in Intra/Internet environment • provides a report and “second opinion” service 7 Abstract Data-Structure Data Data-Structure • Exam-data: generic digital data produced by an examination • Report: text-file and digital signature • Event: database record containing – the date-time of the insertion in the database – the examination type – the laboratory who produced it - 137 - 8 CAP. 6 - CONCLUSIONI MiRo - Architecture DATA-STRUCTURE • Data: digital data produced by an examination • Event: database record that links to the exam SYSTEM-ENTITIES • Repository: archives event • Laboratory: generates event • Phisician: reports examination • Report: text-file containing report 9 Repository • Allows laboratories to generate and manage events • Allows phisicians to control and report examination • Stores reports allowing teleconsultation ¾Acts as a sophisticated DBMS for event-records 10 - 138 - CAP. 6 - CONCLUSIONI Laboratory • Generates clinical event • Provides digital data of examination • Monitors the workflow of reporting phase • Terminates reporting phase 11 Phisician • Consults published examination • Consults reports written by other phisicians • Compiles and signs report • Send this report to a timestamp service and then to the repository 12 - 139 - CAP. 6 - CONCLUSIONI From Exam to Report 1. Laboratory executes an examination that produces digital data 2. Laboratory pubblicates exam through the creation of an event 3. Phisician compiles and signs a report and sends it to the timestamp service 4. Timestamp service appends a certificated datetime to the report and forwards this back to the phisician 5. Phisician sends this composite-data to the Repository 6. The Laboratory decides when to terminate the refertation process 13 Event-Generation Process Event Event-Generation 1. Laboratory executes an examination that produces digital data 2. Laboratory pubblicates exam through the creation of an event - 140 - 14 CAP. 6 - CONCLUSIONI Event-Generation Process Event Event-Generation 3. Repository records event informations about exam 4. The exam is reachable through the MiRo Network from each authorized client 15 Report-Generation Process Report Report-Generation 1. Phisician, as client, connects to Repository and can see a list of events 2. Phisician chooses an event and retrieves linked examination data 16 - 141 - CAP. 6 - CONCLUSIONI Report-Generation Process Report Report-Generation 3. Phisician sees reports written by other associates 4. Phisician compiles a report and sends it to the Repository 17 MiRo - Technologies • Service-Oriented Architecture based on common standards widely applied in different sanitary contexts • implemented through Web Services (SOAP, XML, …), which are becoming a standard to distribute services inside an heterogeneous environment such as Internet • Client can enter in any moment to consult and to insert event or report data using a common Web Browser • transactions are encrypted through standard SSL X509.v3, using standard Digital Certificates 18 - 142 - CAP. 6 - CONCLUSIONI MiRo – Development Environment • Java 2 Platform Standard Edition • development tools: – Eclipse 3.1 – as development environment – Apache Axis and Java Servlet – to realize web services – Apache Struts and Java Sever Pages – to design web interfaces • Java web services technology – JDBC interface (in our case MySQL). • Clients adopt just Web Browsers 19 MiRo - Security Layer • Secure Socket Layer(SSL) – Confidentiality achieved through cryptography, guarantees that data can exclusively be read from the authorized user. • Digital Sign – Integrity and no-repudiation, achieved through the digital sign, guarantees that information are not modified during the transmission and that those who use the system assume the responsibilities of what they are doing. – Authentication achieved through digital certificate, Username and Password, guarantees that the user is exactly the person he declares to be. 20 - 143 - CAP. 6 - CONCLUSIONI Conclusions and future works • MiRo is a software architecture for TCP/IP-based telereporting activities and “second opinion” services • thanks to its flexibility, can be exploited in many different operational realities and is able to incorporate different Intra/Internet-based services. • In future perspective, we foresee the possibility to implement and to provide new services and to reach citizens. 21 Greetings • ALDO FRANCO DRAGONI DEIT, Dipartimento di Elettronica Intelligenza Artificiale e Telecomunicazioni – Università Politecnica delle Marche, Ancona • GIUSEPPE GIAMPIERI ASUR Zona 7, Azienda Sanitaria Locale delle Marche Ancona THANK FOR YOUR ATTENTION 22 - 144 - CAP. 6 - CONCLUSIONI Si ringrazia per la gentile collaborazione e per la pazienza espressa : tutto il personale dell’ASUR Marche zona 7, Fabio Tonucci, Giuseppe Giampieri, Sergio Piersantelli, Ebe Tartufo. Un ringraziamento speciale al prof. Aldo Franco Dragoni per il sostegno, l’appoggio e per la collaborazione fornita nello sviluppo dell’intero progetto. Una dedica particolare a tutte le persone che mi sono state vicine in questi anni: al mio amico e collega Mirco, tutti gli amici in particolare : Carlo, Federico, Giorgio, Luca, Stefano, Mauro, Paolo, Pietro, Cristian, Daniele, Francesco, Simone, Matteo…. La mia fidanzata, i parenti, mia madre Nera e in modo particolare mio padre Bruno. Roberto Di Rosa - 145 - APPENDICE - CODICE SORGENTE APPENDICE CODICE SORGENTE Codice Java scritto per l’implementazione dell’applicazione Web del lato client. A.1 Client Application A.1.1 EventCloseAction.java A.1.2 EventCloseBean.java A.1.3 EventItem.java A.1.4 EventList.java A.1.5 EventListAction.java A.1.6 EventListBean.java A.1.7 EventOpenAction.java A.1.8 EventOpenBean.java A.1.9 EventViewAction.java A.1.10 EventViewBean.java A.1.11 ReportItem.java A.1.12 ReportList.java A.1.13 ReportUploadAction.java A.1.14 ReportUploadBean.java A.1.15 ReportViewAction.java A.1.16 ReportViewBean.java - 146 - APPENDICE - CODICE SORGENTE A.2 Web Service Client A.2.1 WSLaboratory.java A.2.2 WSLaboratoryService.java A.2.3 WSLaboratoryServiceLocator.java A.2.4 WSLaboratorySoapBindingStub.java A.2.5 WSRepository.java A.2.6 WSRepositoryService.java A.2.7 WSRepositoryServiceLocator.java A.2.8 WSRepositorySoapBindingStub.java A.3 Common Library A.3.1 Certificate.java A.3.2 Client.java A.3.3 DataTime.java A.3.4 FileUtil.java - 147 - APPENDICE - CODICE SORGENTE A.1 CLIENT APPLICATION A.1.1 EventCloseAction.java package it.jtelemed.events; import javax.servlet.http.*; import org.apache.struts.action.*; public class EventCloseAction extends org.apache.struts.action.Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { EventCloseBean closeform = (EventCloseBean)form; if (closeform.closeEvent()) { return mapping.findForward("success"); } else { return mapping.findForward("failure"); } } } A.1.2 EventCloseBean.java package it.jtelemed.events; import it.jtelemed.services.*; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import org.apache.axis.AxisFault; import org.apache.axis.client.Service; - 148 - APPENDICE - CODICE SORGENTE public class EventCloseBean extends org.apache.struts.action.ActionForm { public static final long serialVersionUID = 1; private int rep_id = 0; public EventCloseBean() { rep_id = 0; } public void setRep_id(int value) { rep_id = value; } public int getRep_id() { return rep_id; } /** * chiude un evento * * @param String rep_id * * @return vero se l'evento è stato scaricato chiuso */ public boolean closeEvent() { boolean ret = false; try { WSRepositorySoapBindingStub ws_client = new WSRepositorySoapBindingStub(new URL(new WSRepositoryServiceLocator().getWSRepositoryAddress()), new Service()); // Leggo l'allegato, se presente ret = ws_client.closeevent(rep_id); } catch (AxisFault ex) { ret = false; } catch (MalformedURLException ex) { ret = false; } catch (RemoteException ex) { ret = false; } return ret; } } - 149 - APPENDICE - CODICE SORGENTE A.1.3 EventItem.java package it.jtelemed.events; public class EventItem { public static final long serialVersionUID = 1; private private private private private private int rep_id = 0; String rep_dataevento = ""; String rep_utente = ""; String rep_token = ""; String rep_wslink = ""; String rep_stato = ""; private String rep_codicetarif = ""; private String rep_descrtarif = ""; private String rep_resourcelink = ""; private String rep_descrstato = ""; private String rep_iconfile = ""; public EventItem() { rep_id = 0; rep_dataevento = ""; rep_utente = ""; rep_token = ""; rep_wslink = ""; rep_stato = ""; rep_codicetarif = ""; rep_descrtarif = ""; rep_resourcelink = ""; rep_descrstato = ""; rep_iconfile = ""; } public EventItem(int rep_id, String rep_dataevento, String rep_utente, String rep_token, String rep_wslink, String rep_stato, String rep_codicetarif, String rep_descrtarif) { this.rep_id = rep_id; this.rep_dataevento = rep_dataevento; this.rep_utente = rep_utente; - 150 - APPENDICE - CODICE SORGENTE this.rep_token = rep_token; this.rep_wslink = rep_wslink; this.rep_stato = rep_stato; this.rep_codicetarif = rep_codicetarif; this.rep_descrtarif = rep_descrtarif; this.rep_resourcelink = rep_wslink + "?method=getfile&token=" + rep_token; switch (Integer.parseInt(rep_stato)) { case 0: this.rep_descrstato = "Aperto"; this.rep_iconfile = "icn_eventopenG.png"; break; case 1: this.rep_descrstato = "Refertato"; this.rep_iconfile = "icn_eventrefG.png"; break; default: this.rep_descrstato = "Chiuso"; this.rep_iconfile = "icn_eventcloseG.png"; break; } } public void setRep_id(int value) { rep_id = value; } public int getRep_id() { return rep_id; } public void setRep_dataevento(String value) { rep_dataevento = value; } public String getRep_dataevento() { return rep_dataevento; } public void setRep_utente(String value) { rep_utente = value; } public String getRep_utente() { return rep_utente; } public void setRep_token(String value) { - 151 - APPENDICE - CODICE SORGENTE rep_token = value; } public String getRep_token() { return rep_token; } public void setRep_wslink(String value) { rep_wslink = value; } public String getRep_wslink() { return rep_wslink; } public void setRep_stato(String value) { rep_stato = value; } public String getRep_stato() { return rep_stato; } public void setRep_codicetarif(String value) { rep_codicetarif = value; } public String getRep_codicetarif() { return rep_codicetarif; } public void setRep_descrtarif(String value) { rep_descrtarif = value; } public String getRep_descrtarif() { return rep_descrtarif; } public void setRep_resourcelink(String value) { rep_resourcelink = value; } public String getRep_resourcelink() { return rep_resourcelink; } public void setRep_descrstato(String value) { rep_descrstato = value; } - 152 - APPENDICE - CODICE SORGENTE public String getRep_descrstato() { return rep_descrstato; } public void setRep_iconfile(String value) { rep_iconfile = value; } public String getRep_iconfile() { return rep_iconfile; } } A.1.4 EventList.java package it.jtelemed.events; import java.util.*; public class EventList implements java.util.Collection { private ArrayList list = new ArrayList(); public int size() { return list.size(); } public boolean isEmpty() { return list.isEmpty(); } public boolean contains(Object arg0) { return list.contains(arg0); } public Iterator iterator() { return list.iterator(); } public Object[] toArray() { return list.toArray(); } public Object[] toArray(Object[] arg0) { return list.toArray(arg0); - 153 - APPENDICE - CODICE SORGENTE } public boolean add(Object arg0) { return list.add(arg0); } public boolean remove(Object arg0) { return list.remove(arg0); } public boolean containsAll(Collection arg0) { return list.containsAll(arg0); } public boolean addAll(Collection arg0) { return list.addAll(arg0); } public boolean removeAll(Collection arg0) { return list.removeAll(arg0); } public boolean retainAll(Collection arg0) { return list.retainAll(arg0); } public void clear() { list.clear(); } } A.1.5 EventListAction.java package it.jtelemed.events; import it.jtelemed.common.*; import javax.servlet.http.*; import org.apache.struts.action.*; public class EventListAction extends org.apache.struts.action.Action { public ActionForward execute(ActionMapping mapping, ActionForm form, - 154 - APPENDICE - CODICE SORGENTE HttpServletRequest request, HttpServletResponse response) throws Exception { EventListBean bean = (EventListBean)form; Client cli = Client.create(request.getSession()); bean.setClient(cli); if (bean.populate()) { request.setAttribute("eventListBean", bean); return mapping.findForward("success"); } else return mapping.findForward("listevents"); } } A.1.6 EventListBean.java package it.jtelemed.events; import it.jtelemed.common.Client; import it.jtelemed.services.*; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import org.apache.axis.AxisFault; import org.apache.axis.client.Service; public class EventListBean extends org.apache.struts.action.ActionForm { public static final long serialVersionUID = 1; private EventList list = null; public EventListBean() { list = new EventList(); } public EventList getList() - 155 - { APPENDICE - CODICE SORGENTE return list; } public void setList(EventList lst) { list = lst; } private Client client = null; public void setClient(Client value) { client = value; } public boolean populate() { boolean populated = false; try { String rep_list = ""; EventItem eventItem; //System.setProperty("javax.net.ssl.trustStore","C:/Pr ogram Files/Java/jre1.5.0_05/lib/security/cacerts"); //System.setProperty("javax.net.ssl.trustStorePassword ","changeit"); WSRepositorySoapBindingStub ws_client = new WSRepositorySoapBindingStub(new URL(new WSRepositoryServiceLocator().getWSRepositoryAddress()), new Service()); rep_list = ws_client.listevents(client.user_id); if (rep_list != null && rep_list.length() > 0) { String[] rep_events = rep_list.split("§"); String[] rep_event; if (rep_events != null && rep_events.length > 0) { for (int i = 0; i < rep_events.length; ++i) { rep_event = rep_events[i].split(";"); if (rep_event != null && rep_event.length == 8) { eventItem = new EventItem(new Integer(rep_event[0]).intValue(), - 156 - APPENDICE - CODICE SORGENTE rep_event[1], rep_event[2], rep_event[3], rep_event[4], rep_event[5], rep_event[6], rep_event[7]); list.add(eventItem); } } } } populated = true; } catch (AxisFault ex) { populated = false; } catch (MalformedURLException ex) { populated = false; } catch (RemoteException ex) { populated = false; } return populated; } } A.1.7 EventOpenAction.java package it.jtelemed.events; import it.jtelemed.common.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import import org.apache.struts.action.ActionForm; org.apache.struts.action.ActionForward; org.apache.struts.action.ActionMapping; org.apache.struts.action.ActionMessage; org.apache.struts.action.ActionMessages; import org.apache.struts.action.*; public class EventOpenAction extends Action { private Client client = null; public void setClient(Client value) { client = value; } - 157 - APPENDICE - CODICE SORGENTE public EventOpenAction() { } public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // create a new EventInsertBean with form data EventOpenBean bean = (EventOpenBean)form; // Parametri che vengono caricati bean.setEvt_token(FileUtil.extractFileName(bean.getEvt _filename())); bean.setEvt_wslink(client.user_labwslink); //bean.setEvt_wslink(this.getServlet().getServletConte xt().getInitParameter("wsLink")); // check to see if this user/password combination are valid if(bean.validateEvent()) { request.setAttribute("evt_trf_codice",bean.getEvt_trf_ codice()); request.setAttribute("evt_esterno",bean.getEvt_esterno ()); request.setAttribute("evt_token",bean.getEvt_token()); request.setAttribute("evt_wslink",bean.getEvt_wslink() ); request.setAttribute("evt_richiedente",bean.getEvt_ric hiedente()); request.setAttribute("evt_impegnativa",bean.getEvt_imp egnativa()); request.setAttribute("evt_note",bean.getEvt_note()); request.setAttribute("evt_filename",bean.getEvt_filena me()); //return (mapping.findForward("success")); - 158 - APPENDICE - CODICE SORGENTE if(bean.insertEvent()) { return (mapping.findForward("success")); } else { // create ActionMessage and save in the request ActionMessages errors = new ActionMessages(); ActionMessage error = new ActionMessage("error.openevent.invalid"); errors.add("openevent",error); this.saveErrors(request, errors); return (mapping.findForward("failure")); } } else // data not validated { if (!bean.isEmpty()) { // create ActionMessage and save in the request ActionMessages errors = new ActionMessages(); ActionMessage error = new ActionMessage("error.openevent.invalid"); errors.add("openevent",error); this.saveErrors(request, errors); } return (mapping.findForward("failure")); } } } A.1.8 EventOpenBean.java package it.jtelemed.events; import it.jtelemed.common.*; import it.jtelemed.services.*; - 159 - APPENDICE - CODICE SORGENTE import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import javax.servlet.http.HttpServletRequest; import import import import import org.apache.axis.AxisFault; org.apache.axis.client.Service; org.apache.struts.action.ActionErrors; org.apache.struts.action.ActionMapping; org.apache.struts.action.ActionMessage; public class EventOpenBean extends org.apache.struts.action.ActionForm { public static final long serialVersionUID = 1; private private private private private private private private private int evt_id = -1; String evt_trf_codice = ""; Boolean evt_esterno = new Boolean(false); String evt_token = ""; String evt_wslink = ""; String evt_richiedente = ""; String evt_impegnativa = ""; String evt_note = ""; String evt_filename = ""; private String subjectCN = ""; public EventOpenBean() { // Richiamo il metodo di reset che mi inizializza i valori reset(null, null); } public void reset(ActionMapping actionMapping, HttpServletRequest request) { evt_trf_codice = ""; evt_esterno = new Boolean(false); evt_token = ""; evt_wslink = ""; evt_richiedente = ""; evt_impegnativa = ""; evt_note = ""; evt_filename = ""; if (client != null) subjectCN = client.user_name; else - 160 - APPENDICE - CODICE SORGENTE subjectCN = ""; } public ActionErrors validate(ActionMapping actionMapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if((evt_trf_codice == null) || (evt_trf_codice.length() < 1)) errors.add("evt_trf_codice", new ActionMessage("error.trf_codice.required")); return errors; } public String getEvt_trf_codice() { return evt_trf_codice; } public Boolean getEvt_esterno() { return evt_esterno; } public String getEvt_token() { return evt_token; } public String getEvt_wslink() { return evt_wslink; } public String getEvt_richiedente() { return evt_richiedente; } public String getEvt_impegnativa() { return evt_impegnativa; } public String getEvt_note() { return evt_note; } public String getEvt_filename() { return evt_filename; } public void setEvt_trf_codice(String string) { evt_trf_codice = string; } - 161 - APPENDICE - CODICE SORGENTE public void setEvt_esterno(Boolean bool) { evt_esterno = bool; } public void setEvt_token(String string) { evt_token = string; } public void setEvt_wslink(String string) { evt_wslink = string; } public void setEvt_richiedente(String string) { evt_richiedente = string; } public void setEvt_impegnativa(String string) { evt_impegnativa = string; } public void setEvt_note(String string) { evt_note = string; } public void setEvt_filename(String string) { evt_filename = string; } public String getSubjectCN() { return subjectCN; } public void setSubjectCN(String string) { subjectCN = string; } public boolean isEmpty() { return ((evt_trf_codice == null) || (evt_trf_codice.length() < 1)); } private Client client = null; public void setClient(Client value) { client = value; subjectCN = client.user_name; } /** - 162 - APPENDICE - CODICE SORGENTE * @return boolean true if valid, false otherwise */ public boolean validateEvent() { boolean validated = true; if((evt_trf_codice == null) || (evt_trf_codice.length() < 1)) validated = false; return validated; } /** * inserisce l'evento. * * @return boolean true if valid, false otherwise */ public boolean insertEvent() { javax.activation.DataHandler dh; try { // Creo il FileDataSource per leggere il file javax.activation.FileDataSource fds = new javax.activation.FileDataSource(evt_filename); // Creo il DataHandler per inviare il file dh = new javax.activation.DataHandler(fds); } catch (Exception ex) { dh = null; } try { WSRepositorySoapBindingStub ws_client = new WSRepositorySoapBindingStub(new URL(new WSRepositoryServiceLocator().getWSRepositoryAddress()), new Service()); // Allego il file se presente if (dh != null) { ws_client.addAttachment(dh); } evt_id = ws_client.openevent(client.user_id, evt_trf_codice, evt_esterno.booleanValue(), evt_token, evt_wslink, evt_richiedente, evt_impegnativa, evt_note); return (evt_id > 0); } catch (AxisFault ex) { return false; } catch (MalformedURLException ex) { return false; - 163 - APPENDICE - CODICE SORGENTE } catch (RemoteException ex) { return false; } } } A.1.9 EventViewAction.java package it.jtelemed.events; import javax.servlet.http.*; import org.apache.struts.action.*; public class EventViewAction extends org.apache.struts.action.Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { EventViewBean bean = (EventViewBean)form; // Aggiungo il fileNAME request.setAttribute("fileNAME",bean.getTokenName()); // Aggiungo il fileHREF request.setAttribute("fileHREF",bean.getTokenTypeHref( )); // Aggiungo il fileTYPE request.setAttribute("fileTYPE",bean.getTokenTypeDescr ()); if (bean.viewEvent()) { return mapping.findForward("success"); } else { return mapping.findForward("failure"); } - 164 - APPENDICE - CODICE SORGENTE } } A.1.10 EventViewBean.java package it.jtelemed.events; import it.jtelemed.services.*; import it.jtelemed.common.*; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import org.apache.axis.AxisFault; import org.apache.axis.client.Service; public class EventViewBean extends org.apache.struts.action.ActionForm { public static final long serialVersionUID = 1; private int rep_id = 0; private String rep_token = ""; private String rep_wslink = ""; public EventViewBean() { rep_id = 0; rep_token = ""; rep_wslink = ""; } public void setRep_id(int value) { rep_id = value; } public int getRep_id() { return rep_id; } public void setRep_token(String value) { rep_token = value; } public String getRep_token() { - 165 - APPENDICE - CODICE SORGENTE return rep_token; } public void setRep_wslink(String value) { rep_wslink = value; } public String getRep_wslink() { return rep_wslink; } public String getTokenName() { String ext = FileUtil.extractFileExt(rep_token).toLowerCase(); String ret = "object-" + StringUtil.padString(Integer.toString(rep_id),12,'0',true) + "." + ext; return ret; } public String getTokenType() { String ext = FileUtil.extractFileExt(rep_token).toLowerCase(); String ret = ext; if (ext.compareTo("dcm") == 0) ret = "dcm"; return ret; } public String getTokenTypeHref() { //String ext = getTokenType(); String ret = ServletUtil.getInitParameter(this.getServlet(), "fileLink"); //if (ext.compareTo("dcm") == 0) // ret = "http://localhost/dicom/view.html?file="; return ret; } public String getTokenTypeDescr() { String ret = ""; String ext = getTokenType(); if (ext.compareTo("dcm") == 0) ret = "Dicom"; - 166 - APPENDICE - CODICE SORGENTE else ret = StringUtil.capitalizeString(ext); return ret; } /** * scarica un allegato * * @param String rep_id * @param String rep_token * @param String rep_wslink * * @return vero se è stato scaricato un allegato */ public boolean viewEvent() { boolean hasAtt = false; String rep_filename = FileUtil.parsePathName(ServletUtil.getInitParameter(this.ge tServlet(), "filePath")) + FileUtil.getEventFileName("object", FileUtil.extractFileExt(rep_token), rep_id); FileUtil.logRequest("C:\\cclab.log", "viewEvent?file=" + rep_filename); try { WSLaboratorySoapBindingStub ws_client = new WSLaboratorySoapBindingStub(new URL(rep_wslink), new Service()); // Leggo l'allegato, se presente if (ws_client.getevent(rep_token)) { javax.xml.soap.AttachmentPart att = (javax.xml.soap.AttachmentPart)ws_client.getAttachments()[0 ]; if (att != null) { // Salva l'allegato hasAtt = Attachment.saveAttachment(att, rep_filename); FileUtil.logRequest("C:\\cclab.log", "viewEvent:" + new Boolean(hasAtt).toString()); } } } catch (AxisFault ex) { hasAtt = false; - 167 - APPENDICE - CODICE SORGENTE } catch (MalformedURLException ex) { hasAtt = false; } catch (RemoteException ex) { hasAtt = false; } return hasAtt; } } A.1.11 ReportItem.java package it.jtelemed.reports; public class ReportItem { public static final long serialVersionUID = 1; private private private private private private int ref_ind = 0; String ref_filename = ""; String ref_timestamp = ""; String ref_digest = ""; String ref_notvalid = ""; String ref_author = ""; private String ref_content = ""; public ReportItem() { ref_ind = 0; ref_filename = ""; ref_timestamp = ""; ref_digest = ""; ref_notvalid = ""; ref_author = ""; ref_content = ""; } public ReportItem(int ref_ind, String ref_filename, String ref_timestamp, String ref_notvalid, String ref_author, String ref_content) { this.ref_ind = ref_ind; this.ref_filename = ref_filename; this.ref_timestamp = ref_timestamp; this.ref_notvalid = ref_notvalid; this.ref_author = ref_author; - 168 - APPENDICE - CODICE SORGENTE this.ref_content = ref_content; } public void setRef_ind(int value) { ref_ind = value; } public int getRef_ind() { return ref_ind; } public void setRef_filename(String value) { ref_filename = value; } public String getRef_filename() { return ref_filename; } public void setRef_timestamp(String value) { ref_timestamp = value; } public String getRef_timestamp() { return ref_timestamp; } public void setRef_digest(String value) { ref_digest = value; } public String getRef_digest() { return ref_digest; } public void setRef_notvalid(String value) { ref_notvalid = value; } public String getRef_notvalid() { return ref_notvalid; } public void setRef_author(String value) { ref_author = value; } public String getRef_author() { return ref_author; - 169 - APPENDICE - CODICE SORGENTE } public void setRef_content(String value) { ref_content = value; } public String getRef_content() { return ref_content; } } A.1.12 ReportList.java package it.jtelemed.reports; import java.util.*; public class ReportList implements java.util.Collection { private ArrayList list = new ArrayList(); public int size() { return list.size(); } public boolean isEmpty() { return list.isEmpty(); } public boolean contains(Object arg0) { return list.contains(arg0); } public Iterator iterator() { return list.iterator(); } public Object[] toArray() { return list.toArray(); } public Object[] toArray(Object[] arg0) { return list.toArray(arg0); } public boolean add(Object arg0) { - 170 - APPENDICE - CODICE SORGENTE return list.add(arg0); } public boolean remove(Object arg0) { return list.remove(arg0); } public boolean containsAll(Collection arg0) { return list.containsAll(arg0); } public boolean addAll(Collection arg0) { return list.addAll(arg0); } public boolean removeAll(Collection arg0) { return list.removeAll(arg0); } public boolean retainAll(Collection arg0) { return list.retainAll(arg0); } public void clear() { list.clear(); } } A.1.13 ReportUploadAction.java package it.jtelemed.reports; import javax.servlet.http.*; import it.jtelemed.common.*; import org.apache.struts.action.*; public class ReportUploadAction extends org.apache.struts.action.Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { - 171 - APPENDICE - CODICE SORGENTE ReportUploadBean bean = (ReportUploadBean)form; Client cli = Client.create(request.getSession()); bean.setClient(cli); if (bean.upload()) { request.setAttribute("reportUploadBean", bean); return mapping.findForward("success"); } else return mapping.findForward("failure"); } } A.1.14 ReportUploadBean.java package it.jtelemed.reports; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import org.apache.axis.AxisFault; import org.apache.axis.client.Service; import it.jtelemed.services.*; import it.jtelemed.common.*; public class ReportUploadBean extends org.apache.struts.action.ActionForm { public static final long serialVersionUID = 1; private int rep_id = 0; private String report_content = ""; public ReportUploadBean() { rep_id = 0; report_content = ""; } public void setRep_id(int value) { rep_id = value; } - 172 - APPENDICE - CODICE SORGENTE public int getRep_id() { return rep_id; } public void setReport_content(String value) { report_content = value; } public String getReport_content() { return report_content; } private Client client = null; public void setClient(Client value) { client = value; } public boolean upload() { boolean uploaded = false; String filename = ""; // Nome del file digest firmato e serializzato filename = FileUtil.parsePathName(client.user_archivepath) + "digest-" + DateTime.getTimeStamp(false) + ".bin"; // Operazione di firma del digest e serializzazione if (!FileUtil.createSignedFile(filename, report_content.getBytes())) return false; try { WSRepositorySoapBindingStub ws_client = new WSRepositorySoapBindingStub(new URL(new WSRepositoryServiceLocator().getWSRepositoryAddress()), new Service()); // Allego il digest firmato Attachment.addAttachmentPart(ws_client, filename); // Richiamo il metodo per caricare il report sul server if (ws_client.uploadreport(rep_id, client.user_id, report_content)) { - 173 - APPENDICE - CODICE SORGENTE uploaded = true; } } catch (AxisFault ex) { return false; } catch (MalformedURLException ex) { return false; } catch (RemoteException ex) { return false; } return uploaded; } } A.1.15 ReportViewAction.java package it.jtelemed.reports; import javax.servlet.http.*; import org.apache.struts.action.*; public class ReportViewAction extends org.apache.struts.action.Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ReportViewBean bean = (ReportViewBean)form; if (bean.populate()) { request.setAttribute("reportViewBean", bean); return mapping.findForward("success"); } else return mapping.findForward("failure"); } } - 174 - APPENDICE - CODICE SORGENTE A.1.16 ReportViewBean.java package it.jtelemed.reports; import it.jtelemed.services.*; import it.jtelemed.common.*; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import org.apache.axis.AxisFault; import org.apache.axis.client.Service; public class ReportViewBean extends org.apache.struts.action.ActionForm { public static final long serialVersionUID = 1; private int rep_id = 0; private ReportList list = null; private String ref_content = ""; public ReportViewBean() { rep_id = 0; list = new ReportList(); ref_content = ""; } public void setRep_id(int value) { rep_id = value; } public int getRep_id() { return rep_id; } public ReportList getList() return list; } { public void setList(ReportList lst) { list = lst; } public void setRef_content(String value) { ref_content = value; } - 175 - APPENDICE - CODICE SORGENTE public String getRef_content() { return ref_content; } public boolean populate() { boolean populated = false; try { String ref_list = ""; Object[] atts = null; javax.xml.soap.AttachmentPart ref_attach = null; String ref_content = ""; ReportItem repItem; WSRepositorySoapBindingStub ws_client = new WSRepositorySoapBindingStub(new URL(new WSRepositoryServiceLocator().getWSRepositoryAddress()), new Service()); ref_list = ws_client.getreports(rep_id); if (ref_list != null && ref_list.length() > 0) { atts = Attachment.getAttachments(ws_client); String[] ref_items = ref_list.split("§"); String[] ref_item; if (ref_items != null && ref_items.length > 0) { for (int i = 0; i < ref_items.length; ++i) { ref_item = ref_items[i].split(";"); if (ref_item != null && ref_item.length == 5) { ref_content = ""; ref_attach = (javax.xml.soap.AttachmentPart)atts[i]; if (ref_attach != null) { byte[] buff = FileUtil.readFileBuffer(ref_attach); if (buff != null && buff.length > 0) ref_content = new String(buff); } - 176 - APPENDICE - CODICE SORGENTE repItem = new ReportItem(new Integer(ref_item[0]).intValue(), ref_item[1], ref_item[2], ref_item[3], ref_item[4], ref_content); list.add(repItem); } } } } populated = true; } catch (AxisFault ex) { populated = false; } catch (MalformedURLException ex) { populated = false; } catch (RemoteException ex) { populated = false; } return populated; } } - 177 - APPENDICE - CODICE SORGENTE A.2 WEB SERVICE CLIENT A.2.1 WSLaboratory.java /** * WSLaboratory.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ package it.jtelemed.services; public interface WSLaboratory extends java.rmi.Remote { public java.lang.String echo(java.lang.String value) throws java.rmi.RemoteException; public boolean getevent(java.lang.String file) throws java.rmi.RemoteException; } A.2.2 WSLaboratoryService.java /** * WSLaboratoryService.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ package it.jtelemed.services; public interface WSLaboratoryService extends javax.xml.rpc.Service { public java.lang.String getWSLaboratoryAddress(); public it.jtelemed.services.WSLaboratory getWSLaboratory() throws javax.xml.rpc.ServiceException; - 178 - APPENDICE - CODICE SORGENTE public it.jtelemed.services.WSLaboratory getWSLaboratory(java.net.URL portAddress) throws javax.xml.rpc.ServiceException; } A.2.3 WSLaboratoryServiceLocator.java /** * WSLaboratoryServiceLocator.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ package it.jtelemed.services; public class WSLaboratoryServiceLocator extends org.apache.axis.client.Service implements it.jtelemed.services.WSLaboratoryService { public static final long serialVersionUID = 1; public WSLaboratoryServiceLocator() { } public WSLaboratoryServiceLocator(org.apache.axis.EngineConfigurat ion config) { super(config); } public WSLaboratoryServiceLocator(java.lang.String wsdlLoc, javax.xml.namespace.QName sName) throws javax.xml.rpc.ServiceException { super(wsdlLoc, sName); } // Use to get a proxy class for WSLaboratory private java.lang.String WSLaboratory_address = "http://cityhunter:9090/jtelemed_wslab/services/WSLaborator y"; public java.lang.String getWSLaboratoryAddress() { return WSLaboratory_address; - 179 - APPENDICE - CODICE SORGENTE } // The WSDD service name defaults to the port name. private java.lang.String WSLaboratoryWSDDServiceName = "WSLaboratory"; public java.lang.String getWSLaboratoryWSDDServiceName() { return WSLaboratoryWSDDServiceName; } public void setWSLaboratoryWSDDServiceName(java.lang.String name) { WSLaboratoryWSDDServiceName = name; } public it.jtelemed.services.WSLaboratory getWSLaboratory() throws javax.xml.rpc.ServiceException { java.net.URL endpoint; try { endpoint = new java.net.URL(WSLaboratory_address); } catch (java.net.MalformedURLException e) { throw new javax.xml.rpc.ServiceException(e); } return getWSLaboratory(endpoint); } public it.jtelemed.services.WSLaboratory getWSLaboratory(java.net.URL portAddress) throws javax.xml.rpc.ServiceException { try { it.jtelemed.services.WSLaboratorySoapBindingStub _stub = new it.jtelemed.services.WSLaboratorySoapBindingStub(portAddres s, this); _stub.setPortName(getWSLaboratoryWSDDServiceName()); return _stub; } catch (org.apache.axis.AxisFault e) { return null; } } public void setWSLaboratoryEndpointAddress(java.lang.String address) { WSLaboratory_address = address; - 180 - APPENDICE - CODICE SORGENTE } /** * For the given interface, get the stub implementation. * If this service has no port for the given interface, * then ServiceException is thrown. */ public java.rmi.Remote getPort(Class serviceEndpointInterface) throws javax.xml.rpc.ServiceException { try { if (it.jtelemed.services.WSLaboratory.class.isAssignableFrom(s erviceEndpointInterface)) { it.jtelemed.services.WSLaboratorySoapBindingStub _stub = new it.jtelemed.services.WSLaboratorySoapBindingStub(new java.net.URL(WSLaboratory_address), this); _stub.setPortName(getWSLaboratoryWSDDServiceName()); return _stub; } } catch (java.lang.Throwable t) { throw new javax.xml.rpc.ServiceException(t); } throw new javax.xml.rpc.ServiceException("There is no stub implementation for the interface: " + (serviceEndpointInterface == null ? "null" : serviceEndpointInterface.getName())); } /** * For the given interface, get the stub implementation. * If this service has no port for the given interface, * then ServiceException is thrown. */ public java.rmi.Remote getPort(javax.xml.namespace.QName portName, Class serviceEndpointInterface) throws javax.xml.rpc.ServiceException { if (portName == null) { return getPort(serviceEndpointInterface); } java.lang.String inputPortName = portName.getLocalPart(); if ("WSLaboratory".equals(inputPortName)) { return getWSLaboratory(); - 181 - APPENDICE - CODICE SORGENTE } else { java.rmi.Remote _stub = getPort(serviceEndpointInterface); ((org.apache.axis.client.Stub) _stub).setPortName(portName); return _stub; } } public javax.xml.namespace.QName getServiceName() { return new javax.xml.namespace.QName("http://services.jtelemed.it", "WSLaboratoryService"); } private java.util.HashSet ports = null; public java.util.Iterator getPorts() { if (ports == null) { ports = new java.util.HashSet(); ports.add(new javax.xml.namespace.QName("http://services.jtelemed.it", "WSLaboratory")); } return ports.iterator(); } /** * Set the endpoint address for the specified port name. */ public void setEndpointAddress(java.lang.String portName, java.lang.String address) throws javax.xml.rpc.ServiceException { if ("WSLaboratory".equals(portName)) { setWSLaboratoryEndpointAddress(address); } else { // Unknown Port Name throw new javax.xml.rpc.ServiceException(" Cannot set Endpoint Address for Unknown Port" + portName); } } /** * Set the endpoint address for the specified port name. */ public void setEndpointAddress(javax.xml.namespace.QName portName, - 182 - APPENDICE - CODICE SORGENTE java.lang.String address) throws javax.xml.rpc.ServiceException { setEndpointAddress(portName.getLocalPart(), address); } } A.2.4 WSLaboratorySoapBindingStub.java /** * WSLaboratorySoapBindingStub.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ package it.jtelemed.services; public class WSLaboratorySoapBindingStub extends org.apache.axis.client.Stub implements it.jtelemed.services.WSLaboratory { /* private java.util.Vector cachedSerClasses = new java.util.Vector(); private java.util.Vector cachedSerQNames = new java.util.Vector(); private java.util.Vector cachedSerFactories = new java.util.Vector(); private java.util.Vector cachedDeserFactories = new java.util.Vector(); */ static org.apache.axis.description.OperationDesc [] _operations; static { _operations = new org.apache.axis.description.OperationDesc[2]; _initOperationDesc1(); } private static void _initOperationDesc1(){ org.apache.axis.description.OperationDesc oper; org.apache.axis.description.ParameterDesc param; - 183 - APPENDICE - CODICE SORGENTE oper = new org.apache.axis.description.OperationDesc(); oper.setName("echo"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "value"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string")); oper.setReturnClass(java.lang.String.class); oper.setReturnQName(new javax.xml.namespace.QName("", "echoReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[0] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("getevent"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "file"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "boolean")); oper.setReturnClass(boolean.class); oper.setReturnQName(new javax.xml.namespace.QName("", "geteventReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[1] = oper; } public WSLaboratorySoapBindingStub() throws org.apache.axis.AxisFault { this(null); } public WSLaboratorySoapBindingStub(java.net.URL endpointURL, javax.xml.rpc.Service service) throws org.apache.axis.AxisFault { - 184 - APPENDICE - CODICE SORGENTE this(service); super.cachedEndpoint = endpointURL; } public WSLaboratorySoapBindingStub(javax.xml.rpc.Service service) throws org.apache.axis.AxisFault { if (service == null) { super.service = new org.apache.axis.client.Service(); } else { super.service = service; } ((org.apache.axis.client.Service)super.service).setTypeMapp ingVersion("1.2"); } protected org.apache.axis.client.Call createCall() throws java.rmi.RemoteException { try { org.apache.axis.client.Call _call = super._createCall(); if (super.maintainSessionSet) { _call.setMaintainSession(super.maintainSession); } if (super.cachedUsername != null) { _call.setUsername(super.cachedUsername); } if (super.cachedPassword != null) { _call.setPassword(super.cachedPassword); } if (super.cachedEndpoint != null) { _call.setTargetEndpointAddress(super.cachedEndpoint); } if (super.cachedTimeout != null) { _call.setTimeout(super.cachedTimeout); } if (super.cachedPortName != null) { _call.setPortName(super.cachedPortName); } java.util.Enumeration keys = super.cachedProperties.keys(); while (keys.hasMoreElements()) { java.lang.String key = (java.lang.String) keys.nextElement(); _call.setProperty(key, super.cachedProperties.get(key)); - 185 - APPENDICE - CODICE SORGENTE } return _call; } catch (java.lang.Throwable _t) { throw new org.apache.axis.AxisFault("Failure trying to get the Call object", _t); } } public java.lang.String echo(java.lang.String value) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[0]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "echo")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {value}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return (java.lang.String) _resp; } catch (java.lang.Exception _exception) { return (java.lang.String) org.apache.axis.utils.JavaUtils.convert(_resp, java.lang.String.class); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } - 186 - APPENDICE - CODICE SORGENTE public boolean getevent(java.lang.String file) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[1]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "getevent")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {file}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return ((java.lang.Boolean) _resp).booleanValue(); } catch (java.lang.Exception _exception) { return ((java.lang.Boolean) org.apache.axis.utils.JavaUtils.convert(_resp, boolean.class)).booleanValue(); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } } - 187 - APPENDICE - CODICE SORGENTE A.2.5 WSRepository.java /** * WSRepository.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ package it.jtelemed.services; public interface WSRepository extends java.rmi.Remote { public java.lang.String echo(java.lang.String value) throws java.rmi.RemoteException; public java.lang.String userinfo(java.lang.String utn_cn) throws java.rmi.RemoteException; public java.lang.String listevents(int utn_id) throws java.rmi.RemoteException; public int openevent(int utn_id, java.lang.String evt_codice, boolean evt_esterno, java.lang.String evt_token, java.lang.String evt_wslink, java.lang.String evt_richiedente, java.lang.String evt_impegnativa, java.lang.String evt_note) throws java.rmi.RemoteException; public boolean closeevent(int evt_id) throws java.rmi.RemoteException; public java.lang.String getreports(int evt_id) throws java.rmi.RemoteException; public boolean uploadreport(int evt_id, int utn_id, java.lang.String evt_content) throws java.rmi.RemoteException; public boolean invalidatereport(int evt_id, int ref_ind) throws java.rmi.RemoteException; } A.2.6 WSRepositoryService.java /** * WSRepositoryService.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ - 188 - APPENDICE - CODICE SORGENTE package it.jtelemed.services; public interface WSRepositoryService extends javax.xml.rpc.Service { public java.lang.String getWSRepositoryAddress(); public it.jtelemed.services.WSRepository getWSRepository() throws javax.xml.rpc.ServiceException; public it.jtelemed.services.WSRepository getWSRepository(java.net.URL portAddress) throws javax.xml.rpc.ServiceException; } A.2.7 WSRepositoryServiceLocator.java /** * WSRepositoryServiceLocator.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ package it.jtelemed.services; public class WSRepositoryServiceLocator extends org.apache.axis.client.Service implements it.jtelemed.services.WSRepositoryService { public static final long serialVersionUID = 1; public WSRepositoryServiceLocator() { } public WSRepositoryServiceLocator(org.apache.axis.EngineConfigurat ion config) { super(config); } public WSRepositoryServiceLocator(java.lang.String wsdlLoc, javax.xml.namespace.QName sName) throws javax.xml.rpc.ServiceException { super(wsdlLoc, sName); - 189 - APPENDICE - CODICE SORGENTE } // Use to get a proxy class for WSRepository private java.lang.String WSRepository_address = "http://cityhunter:9090/jtelemed/services/WSRepository"; public java.lang.String getWSRepositoryAddress() { return WSRepository_address; } // The WSDD service name defaults to the port name. private java.lang.String WSRepositoryWSDDServiceName = "WSRepository"; public java.lang.String getWSRepositoryWSDDServiceName() { return WSRepositoryWSDDServiceName; } public void setWSRepositoryWSDDServiceName(java.lang.String name) { WSRepositoryWSDDServiceName = name; } public it.jtelemed.services.WSRepository getWSRepository() throws javax.xml.rpc.ServiceException { java.net.URL endpoint; try { endpoint = new java.net.URL(WSRepository_address); } catch (java.net.MalformedURLException e) { throw new javax.xml.rpc.ServiceException(e); } return getWSRepository(endpoint); } public it.jtelemed.services.WSRepository getWSRepository(java.net.URL portAddress) throws javax.xml.rpc.ServiceException { try { it.jtelemed.services.WSRepositorySoapBindingStub _stub = new it.jtelemed.services.WSRepositorySoapBindingStub(portAddres s, this); _stub.setPortName(getWSRepositoryWSDDServiceName()); return _stub; } - 190 - APPENDICE - CODICE SORGENTE catch (org.apache.axis.AxisFault e) { return null; } } public void setWSRepositoryEndpointAddress(java.lang.String address) { WSRepository_address = address; } /** * For the given interface, get the stub implementation. * If this service has no port for the given interface, * then ServiceException is thrown. */ public java.rmi.Remote getPort(Class serviceEndpointInterface) throws javax.xml.rpc.ServiceException { try { if (it.jtelemed.services.WSRepository.class.isAssignableFrom(s erviceEndpointInterface)) { it.jtelemed.services.WSRepositorySoapBindingStub _stub = new it.jtelemed.services.WSRepositorySoapBindingStub(new java.net.URL(WSRepository_address), this); _stub.setPortName(getWSRepositoryWSDDServiceName()); return _stub; } } catch (java.lang.Throwable t) { throw new javax.xml.rpc.ServiceException(t); } throw new javax.xml.rpc.ServiceException("There is no stub implementation for the interface: " + (serviceEndpointInterface == null ? "null" : serviceEndpointInterface.getName())); } /** * For the given interface, get the stub implementation. * If this service has no port for the given interface, * then ServiceException is thrown. */ public java.rmi.Remote getPort(javax.xml.namespace.QName portName, Class - 191 - APPENDICE - CODICE SORGENTE serviceEndpointInterface) throws javax.xml.rpc.ServiceException { if (portName == null) { return getPort(serviceEndpointInterface); } java.lang.String inputPortName = portName.getLocalPart(); if ("WSRepository".equals(inputPortName)) { return getWSRepository(); } else { java.rmi.Remote _stub = getPort(serviceEndpointInterface); ((org.apache.axis.client.Stub) _stub).setPortName(portName); return _stub; } } public javax.xml.namespace.QName getServiceName() { return new javax.xml.namespace.QName("http://services.jtelemed.it", "WSRepositoryService"); } private java.util.HashSet ports = null; public java.util.Iterator getPorts() { if (ports == null) { ports = new java.util.HashSet(); ports.add(new javax.xml.namespace.QName("http://services.jtelemed.it", "WSRepository")); } return ports.iterator(); } /** * Set the endpoint address for the specified port name. */ public void setEndpointAddress(java.lang.String portName, java.lang.String address) throws javax.xml.rpc.ServiceException { if ("WSRepository".equals(portName)) { setWSRepositoryEndpointAddress(address); } else { // Unknown Port Name - 192 - APPENDICE - CODICE SORGENTE throw new javax.xml.rpc.ServiceException(" Cannot set Endpoint Address for Unknown Port" + portName); } } /** * Set the endpoint address for the specified port name. */ public void setEndpointAddress(javax.xml.namespace.QName portName, java.lang.String address) throws javax.xml.rpc.ServiceException { setEndpointAddress(portName.getLocalPart(), address); } } A.2.8 WSRepositorySoapBindingStub.java /** * WSRepositorySoapBindingStub.java * * This file was auto-generated from WSDL * by the Apache Axis 1.2.1 Jun 14, 2005 (09:15:57 EDT) WSDL2Java emitter. */ package it.jtelemed.services; public class WSRepositorySoapBindingStub extends org.apache.axis.client.Stub implements it.jtelemed.services.WSRepository { /* private java.util.Vector cachedSerClasses = new java.util.Vector(); private java.util.Vector cachedSerQNames = new java.util.Vector(); private java.util.Vector cachedSerFactories = new java.util.Vector(); private java.util.Vector cachedDeserFactories = new java.util.Vector(); */ - 193 - APPENDICE - CODICE SORGENTE static org.apache.axis.description.OperationDesc [] _operations; static { _operations = new org.apache.axis.description.OperationDesc[8]; _initOperationDesc1(); } private static void _initOperationDesc1(){ org.apache.axis.description.OperationDesc oper; org.apache.axis.description.ParameterDesc param; oper = new org.apache.axis.description.OperationDesc(); oper.setName("echo"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "value"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string")); oper.setReturnClass(java.lang.String.class); oper.setReturnQName(new javax.xml.namespace.QName("", "echoReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[0] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("userinfo"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "utn_cn"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string")); oper.setReturnClass(java.lang.String.class); oper.setReturnQName(new javax.xml.namespace.QName("", "userinfoReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); - 194 - APPENDICE - CODICE SORGENTE _operations[1] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("listevents"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "utn_id"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string")); oper.setReturnClass(java.lang.String.class); oper.setReturnQName(new javax.xml.namespace.QName("", "listeventsReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[2] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("openevent"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "utn_id"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_codice"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_esterno"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "boolean"), boolean.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_token"), org.apache.axis.description.ParameterDesc.IN, new - 195 - APPENDICE - CODICE SORGENTE javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_wslink"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_richiedente"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_impegnativa"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_note"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int")); oper.setReturnClass(int.class); oper.setReturnQName(new javax.xml.namespace.QName("", "openeventReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[3] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("closeevent"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_id"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); - 196 - APPENDICE - CODICE SORGENTE oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "boolean")); oper.setReturnClass(boolean.class); oper.setReturnQName(new javax.xml.namespace.QName("", "closeeventReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[4] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("getreports"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_id"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string")); oper.setReturnClass(java.lang.String.class); oper.setReturnQName(new javax.xml.namespace.QName("", "getreportsReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[5] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("uploadreport"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_id"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "utn_id"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new - 197 - APPENDICE - CODICE SORGENTE javax.xml.namespace.QName("", "evt_content"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "string"), java.lang.String.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "boolean")); oper.setReturnClass(boolean.class); oper.setReturnQName(new javax.xml.namespace.QName("", "uploadreportReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[6] = oper; oper = new org.apache.axis.description.OperationDesc(); oper.setName("invalidatereport"); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "evt_id"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); oper.addParameter(param); param = new org.apache.axis.description.ParameterDesc(new javax.xml.namespace.QName("", "ref_ind"), org.apache.axis.description.ParameterDesc.IN, new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "int"), int.class, false, false); oper.addParameter(param); oper.setReturnType(new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema ", "boolean")); oper.setReturnClass(boolean.class); oper.setReturnQName(new javax.xml.namespace.QName("", "invalidatereportReturn")); oper.setStyle(org.apache.axis.constants.Style.RPC); oper.setUse(org.apache.axis.constants.Use.ENCODED); _operations[7] = oper; } public WSRepositorySoapBindingStub() throws org.apache.axis.AxisFault { this(null); } - 198 - APPENDICE - CODICE SORGENTE public WSRepositorySoapBindingStub(java.net.URL endpointURL, javax.xml.rpc.Service service) throws org.apache.axis.AxisFault { this(service); super.cachedEndpoint = endpointURL; } public WSRepositorySoapBindingStub(javax.xml.rpc.Service service) throws org.apache.axis.AxisFault { if (service == null) { super.service = new org.apache.axis.client.Service(); } else { super.service = service; } ((org.apache.axis.client.Service)super.service).setTypeMapp ingVersion("1.2"); } protected org.apache.axis.client.Call createCall() throws java.rmi.RemoteException { try { org.apache.axis.client.Call _call = super._createCall(); if (super.maintainSessionSet) { _call.setMaintainSession(super.maintainSession); } if (super.cachedUsername != null) { _call.setUsername(super.cachedUsername); } if (super.cachedPassword != null) { _call.setPassword(super.cachedPassword); } if (super.cachedEndpoint != null) { _call.setTargetEndpointAddress(super.cachedEndpoint); } if (super.cachedTimeout != null) { _call.setTimeout(super.cachedTimeout); } if (super.cachedPortName != null) { _call.setPortName(super.cachedPortName); } java.util.Enumeration keys = super.cachedProperties.keys(); while (keys.hasMoreElements()) { - 199 - APPENDICE - CODICE SORGENTE java.lang.String key = (java.lang.String) keys.nextElement(); _call.setProperty(key, super.cachedProperties.get(key)); } return _call; } catch (java.lang.Throwable _t) { throw new org.apache.axis.AxisFault("Failure trying to get the Call object", _t); } } public java.lang.String echo(java.lang.String value) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[0]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "echo")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {value}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return (java.lang.String) _resp; } catch (java.lang.Exception _exception) { return (java.lang.String) org.apache.axis.utils.JavaUtils.convert(_resp, java.lang.String.class); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; - 200 - APPENDICE - CODICE SORGENTE } } public java.lang.String userinfo(java.lang.String utn_cn) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[1]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "userinfo")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {utn_cn}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return (java.lang.String) _resp; } catch (java.lang.Exception _exception) { return (java.lang.String) org.apache.axis.utils.JavaUtils.convert(_resp, java.lang.String.class); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } public java.lang.String listevents(int utn_id) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); - 201 - APPENDICE - CODICE SORGENTE _call.setOperation(_operations[2]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "listevents")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {new java.lang.Integer(utn_id)}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return (java.lang.String) _resp; } catch (java.lang.Exception _exception) { return (java.lang.String) org.apache.axis.utils.JavaUtils.convert(_resp, java.lang.String.class); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } public int openevent(int utn_id, java.lang.String evt_codice, boolean evt_esterno, java.lang.String evt_token, java.lang.String evt_wslink, java.lang.String evt_richiedente, java.lang.String evt_impegnativa, java.lang.String evt_note) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[3]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); - 202 - APPENDICE - CODICE SORGENTE _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "openevent")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {new java.lang.Integer(utn_id), evt_codice, new java.lang.Boolean(evt_esterno), evt_token, evt_wslink, evt_richiedente, evt_impegnativa, evt_note}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return ((java.lang.Integer) _resp).intValue(); } catch (java.lang.Exception _exception) { return ((java.lang.Integer) org.apache.axis.utils.JavaUtils.convert(_resp, int.class)).intValue(); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } public boolean closeevent(int evt_id) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[4]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "closeevent")); setRequestHeaders(_call); setAttachments(_call); - 203 - APPENDICE - CODICE SORGENTE try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {new java.lang.Integer(evt_id)}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return ((java.lang.Boolean) _resp).booleanValue(); } catch (java.lang.Exception _exception) { return ((java.lang.Boolean) org.apache.axis.utils.JavaUtils.convert(_resp, boolean.class)).booleanValue(); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } public java.lang.String getreports(int evt_id) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[5]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "getreports")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {new java.lang.Integer(evt_id)}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { - 204 - APPENDICE - CODICE SORGENTE return (java.lang.String) _resp; } catch (java.lang.Exception _exception) { return (java.lang.String) org.apache.axis.utils.JavaUtils.convert(_resp, java.lang.String.class); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } public boolean uploadreport(int evt_id, int utn_id, java.lang.String evt_content) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[6]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "uploadreport")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {new java.lang.Integer(evt_id), new java.lang.Integer(utn_id), evt_content}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return ((java.lang.Boolean) _resp).booleanValue(); } catch (java.lang.Exception _exception) { return ((java.lang.Boolean) org.apache.axis.utils.JavaUtils.convert(_resp, boolean.class)).booleanValue(); } } - 205 - APPENDICE - CODICE SORGENTE } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } public boolean invalidatereport(int evt_id, int ref_ind) throws java.rmi.RemoteException { if (super.cachedEndpoint == null) { throw new org.apache.axis.NoEndPointException(); } org.apache.axis.client.Call _call = createCall(); _call.setOperation(_operations[7]); _call.setUseSOAPAction(true); _call.setSOAPActionURI(""); _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOA P11_CONSTANTS); _call.setOperationName(new javax.xml.namespace.QName("http://services.jtelemed.it", "invalidatereport")); setRequestHeaders(_call); setAttachments(_call); try { java.lang.Object _resp = _call.invoke(new java.lang.Object[] {new java.lang.Integer(evt_id), new java.lang.Integer(ref_ind)}); if (_resp instanceof java.rmi.RemoteException) { throw (java.rmi.RemoteException)_resp; } else { extractAttachments(_call); try { return ((java.lang.Boolean) _resp).booleanValue(); } catch (java.lang.Exception _exception) { return ((java.lang.Boolean) org.apache.axis.utils.JavaUtils.convert(_resp, boolean.class)).booleanValue(); } } } catch (org.apache.axis.AxisFault axisFaultException) { throw axisFaultException; } } } - 206 - APPENDICE - CODICE SORGENTE A.3 COMMON LIBRARY A.3.1 Certificate.java package it.jtelemed.common; import java.security.cert.X509Certificate; import javax.servlet.http.HttpServletRequest; public final class Certificate { /** * Ritorna l'elenco dei certificati X509 nella request * * @param request oggetto richiesta della Servlet * * @return elenco dei certificati X509 */ public static X509Certificate[] getX509Certificates(HttpServletRequest request) { X509Certificate[] certs = null; try { if (request.isSecure()) { if (request.getAttribute("javax.servlet.request.X509Certificat e") != null) { // Leggo dagli attributi dell'oggetto request // l'elenco dei certificati certs = (java.security.cert.X509Certificate[])request.getAttribute( "javax.servlet.request.X509Certificate"); } } } catch (Exception ex) { certs = null; } return certs; } /** * Ritorna il certificato X509 i-esimo nella request * * @param request oggetto richiesta della Servlet * - 207 - APPENDICE - CODICE SORGENTE * @return certificato X509 i-esimo */ public static X509Certificate getX509Certificate(HttpServletRequest request, int index) { X509Certificate[] certs = null; try { certs = getX509Certificates(request); if (certs != null && index > -1 && index < certs.length) return certs[index]; } catch (Exception ex) { } return null; } /** * Ritorna il SubjectDN del 1° certificato X509 nella request * * @param request oggetto richiesta della Servlet * * @return elenco dei certificati X509 */ public static java.security.Principal getSubjectDN(HttpServletRequest request) { java.security.Principal subjDN = null; try { X509Certificate[] certs = getX509Certificates(request); if (certs != null && certs.length > 0) { subjDN = certs[0].getSubjectDN(); } } catch (Exception ex) { subjDN = null; } return subjDN; } /** * Estrae un campo dal un DistinguishName, ad es. CN * * @param certDN DistinguishName * @param fieldDN Nome del Campo * - 208 - APPENDICE - CODICE SORGENTE * @return Valore del Campo * */ public static String extractDNField(java.security.Principal certDN, String fieldDN) { // dn_string contiene il DistinguishName String dn_string = certDN.toString(); // dn_field_value conterrà il Valore del Campo, ad es. Mirco Sturari String dn_field_value = ""; // dn_fields[] contiene il DistinguishName spezzato dalle virgole String[] dn_fields = dn_string.split(","); // dn_fields conterrà uno degli elementi di dn_fields[] String dn_field = ""; // dn_token conterrà il token dell'elemento dn_field String dn_token = ""; // se dn_fields[] non è null e contiene almeno un elemento if (dn_fields != null && dn_fields.length > 0) { // Ciclo sugli elementi dell'array dn_fields[] for (int i = 0; i < dn_fields.length; ++i) { // Leggo ciascuna stringa senza spazi a destra e sinistra dn_field = dn_fields[i].trim(); // Estraggo il Nome del Campo dn_token = dn_field.substring(0, dn_field.indexOf("=")).trim(); // Controllo che il Nome del Campo si lo stesso cercato if (dn_token.compareToIgnoreCase(fieldDN) == 0) { // Estraggo il Valore del Campo dn_field_value = dn_field.substring(dn_field.indexOf("=")+1); // Esco dal ciclo break; } } } // Ritorno il Valore del Campo return dn_field_value; } - 209 - APPENDICE - CODICE SORGENTE /** * Estrae il campo CountryCode(C) dal DistinguishName * * @param certDN DistinguishName * * @return CountryCode */ public static String extractCountryCode(java.security.Principal certDN) { // Estraggo il campo CountryCode return extractDNField(certDN, "C"); } /** * Estrae il campo CommonName(CN) dal DistinguishName * * @param certDN DistinguishName * * @return CommonName */ public static String extractCommonName(java.security.Principal certDN) { // Estraggo il campo CommonName return extractDNField(certDN, "CN"); } /** * Estrae il campo Email(E) dal DistinguishName * * @param certDN DistinguishName * * @return Email */ public static String extractEmail(java.security.Principal certDN) { // Estraggo il campo Email return extractDNField(certDN, "EMAILADDRESS"); } /** * Estrae il campo Organization(O) dal DistinguishName * * @param certDN DistinguishName * * @return Organization */ public static String extractOrganization(java.security.Principal certDN) { // Estraggo il campo Organization return extractDNField(certDN, "O"); - 210 - APPENDICE - CODICE SORGENTE } /** * Estrae il campo OrganizationUnit(OU) dal DistinguishName * * @param certDN DistinguishName * * @return OrganizationUnit */ public static String extractOrganizationUnit(java.security.Principal certDN) { // Estraggo il campo OrganizationUnit return extractDNField(certDN, "OU"); } /** * Un digest cifrato è quello in cui si usa * una chiave segreta per creare il digest. * Si possono usare chiavi diverse per creare * differenti digest per lo stesso buffer di byte. * * @param algorithm algoritmo da usare per il digest * @param buffer buffer di byte * @param key chiave segreta per la cifratura * * @return digest */ public static byte[] getKeyedDigest(String algorithm, byte[] buffer, byte[] key) { try { java.security.MessageDigest msgdg = java.security.MessageDigest.getInstance(algorithm); msgdg.update(buffer); return msgdg.digest(key); } catch (java.security.NoSuchAlgorithmException e) { } return null; } /** * Un digest cifrato è quello in cui si usa * una chiave segreta per creare il digest. * Si possono usare chiavi diverse per creare * differenti digest per lo stesso file. * * @param algorithm algoritmo da usare per il digest * @param path nome del file * @param key chiave segreta per la cifratura - 211 - APPENDICE - CODICE SORGENTE * * @return digest */ public static byte[] getKeyedDigest(String algorithm, String path, byte[] key) { java.io.FileInputStream in; try { in = new java.io.FileInputStream(path); int bytes = in.available(); byte[] buf = new byte[bytes]; in.read(buf, 0, buf.length); in.close(); return getKeyedDigest(algorithm, buf, key); } catch (Exception ex) { } return null; } /** * Crea il digest a partire da un buffer. * * @param algorithm algoritmo da usare per il digest * @param buffer buffer di byte * * @return digest */ public static byte[] getDigest(String algorithm, byte[] buffer) { try { java.security.MessageDigest msgdg = java.security.MessageDigest.getInstance(algorithm); msgdg.update(buffer); return msgdg.digest(); } catch (java.security.NoSuchAlgorithmException e) { } return null; } /** * Crea il digest a partire da un file. * * @param algorithm algoritmo da usare per il digest * @param path nome del file * * @return digest */ public static byte[] getDigest(String algorithm, String path) { - 212 - APPENDICE - CODICE SORGENTE java.io.FileInputStream in; try { in = new java.io.FileInputStream(path); int bytes = in.available(); byte[] buf = new byte[bytes]; in.read(buf, 0, buf.length); in.close(); return getDigest(algorithm, buf); } catch (Exception ex) { } return null; } } A.3.2 Client.java package it.jtelemed.common; import javax.servlet.http.*; public class Client { public String user_name = "-"; public String user_kind = "-"; public int user_id = -1; public String user_labwslink = ""; public String user_archivepath = ""; /** * Costruttore pubblico che inizializza le proprietà * */ public Client() { user_id = -1; user_name = "-"; user_kind = "-"; user_labwslink = ""; user_archivepath = ""; } /** * Metodo statico per la creazione di una istanza della * classe Client a partire da un oggetto di sessione HTTP * * @param session oggetto di sessione HTTP - 213 - APPENDICE - CODICE SORGENTE * * @return istanza della classe Client */ public static Client create(HttpSession session) { Client cli = new Client(); cli.user_id = new Integer((String)session.getAttribute("USER_ID")).intValue() ; cli.user_name = (String)session.getAttribute("USER_NAME"); cli.user_kind = (String)session.getAttribute("USER_KIND"); cli.user_labwslink = (String)session.getAttribute("USER_LABWSLINK"); cli.user_archivepath = (String)session.getAttribute("USER_ARCHIVEPATH"); return cli; } /** * Metodo statico per il salvataggio di una classe * Client all'interno di un oggetto di sessione HTTP * * @param cli istanza della classe Client * @param session oggetto di sessione HTTP * */ public static void save(Client cli, HttpSession session) { session.setAttribute("USER_ID",new Integer(cli.user_id).toString()); session.setAttribute("USER_NAME",cli.user_name); session.setAttribute("USER_KIND",cli.user_kind); session.setAttribute("USER_LABWSLINK",cli.user_labwsli nk); session.setAttribute("USER_ARCHIVEPATH",cli.user_archi vepath); } } - 214 - APPENDICE - CODICE SORGENTE A.3.3 DateTime.java package it.jtelemed.common; import it.jtelemed.common.StringUtil; public class DateTime { /** * Ritorna la data corrente * * @return restituisce la stringa contenente la data corrente nel formato 'YYYY-MM-DD' */ public static String getCurrentDate() { // Prendo un'istanza della classe Calendar java.util.Calendar c = java.util.Calendar.getInstance(); // Compongo la stringa contenente la data String s = c.get(java.util.Calendar.YEAR) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.MONTH)+1).toString(),2,'0' ,true) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.DATE)).toString(),2,'0',tr ue); // Ritorno la stringa return s; } /** * Ritorna la data e l'ora corrente * * @return restituisce la stringa contenente la data e l'ora corrente nel formato 'YYYY-MM-DD HH:MM:SS' */ public static String getCurrentDateTime() { // Prendo un'istanza della classe Calendar java.util.Calendar c = java.util.Calendar.getInstance(); // Compongo la stringa contenente la data String s = c.get(java.util.Calendar.YEAR) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.MONTH)+1).toString(),2,'0' ,true) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.DATE)).toString(),2,'0',tr ue) + " " + - 215 - APPENDICE - CODICE SORGENTE StringUtil.padString(new Integer(c.get(java.util.Calendar.HOUR_OF_DAY)).toString(),2 ,'0',true) + ":" + StringUtil.padString(new Integer(c.get(java.util.Calendar.MINUTE)).toString(),2,'0', true) + ":" + StringUtil.padString(new Integer(c.get(java.util.Calendar.SECOND)).toString(),2,'0', true); // Ritorno la stringa return s; } /** * Ritorna il timestamp * * @param with_separator specifica se la stringa deve contenere dei separatori * * @return restituisce la stringa contenente la data e l'ora corrente nel formato 'YYYY-MM-DD HH:MM:SS MMMM' */ public static byte[] getTimeStamp(boolean with_separator) { // Prendo un'istanza della classe Calendar java.util.Calendar c = java.util.Calendar.getInstance(); StringBuilder sb = new StringBuilder(); // Compongo la stringa contenente la data sb.append(c.get(java.util.Calendar.YEAR)); if (with_separator) sb.append("-"); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.MONTH)+1).toString(),2,'0' ,true)); if (with_separator) sb.append("-"); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.DATE)).toString(),2,'0',tr ue)); if (with_separator) sb.append(" "); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.HOUR)).toString(),2,'0',tr ue)); if (with_separator) sb.append(":"); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.MINUTE)).toString(),2,'0', true)); if (with_separator) sb.append(":"); - 216 - APPENDICE - CODICE SORGENTE sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.SECOND)).toString(),2,'0', true)); if (with_separator) sb.append(" "); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.MILLISECOND)).toString(),2 ,'4',true)); // Ritorno la stringa return sb.toString().getBytes(); } } A.3.4 File Util.java package it.jtelemed.common; public class FileUtil { /** * Ritorna il nome del file senza il percorso * * @param path percorso completo del file * * @return nome del file senza il percorso */ public static synchronized String extractFileName(String path) { String s = ""; // Spezzo la stringa a partire dall'ultimo \ if (path.lastIndexOf("\\") > -1) s = path.substring(path.lastIndexOf("\\")+1); // Spezzo la stringa a partire dall'ultimo / else if (path.lastIndexOf("/") > -1) s = path.substring(path.lastIndexOf("/")+1); else s = path; // Ritorno la stringa return s; } /** * Ritorna l'estensione del file * * @param path percorso completo del file - 217 - APPENDICE - CODICE SORGENTE * * @return estensione del file */ public static synchronized String extractFileExt(String path) { String f = extractFileName(path); String s = ""; // Spezzo la stringa a partire dall'ultimo . if (f.lastIndexOf(".") > -1) s = f.substring(f.lastIndexOf(".")+1); return s; } /** * Ritorna il nome del percorso con lo slash finale * * @param path percorso * * @return nome del percorso con lo slash finale */ public static synchronized String parsePathName(String path) { String s = ""; // Controllo che il percorso contenga \ if (path.lastIndexOf("\\") > -1) { // Controllo che il percorso non finisca con \ if (!path.endsWith("\\")) s = path + "\\"; else s = path; } // Controllo che il percorso contenga / else if (path.lastIndexOf("/") > -1) { // Controllo che il percorso non finisca con / if (!path.endsWith("/")) s = path + "/"; else s = path; } else s = path + "/"; // Ritorno la stringa return s; } /** * Estrae un buffer contenente il file allegato * - 218 - APPENDICE - CODICE SORGENTE * @param att allegato al messaggio SOAP * * @return ritorna un buffer che contiene i byte letti */ public static byte[] readFileBuffer(javax.xml.soap.AttachmentPart att) { byte[] buffer = null; try { javax.activation.DataHandler dh = att.getDataHandler(); if (dh != null) { buffer = readFileBuffer(dh.getInputStream()); } } catch (Exception ex) { buffer = null; } return buffer; } /** * Estrae un buffer contenente il file allegato * * @param path nome del file * * @return ritorna un buffer che contiene i byte letti */ public static byte[] readFileBuffer(String path) { byte[] buffer = null; try { buffer = readFileBuffer(new java.io.FileInputStream(path)); } catch (Exception ex) { buffer = null; } return buffer; } /** * Estrae un buffer contenente il file allegato * * @param in file input strema * * @return ritorna un buffer che contiene i byte letti */ - 219 - APPENDICE - CODICE SORGENTE public static byte[] readFileBuffer(java.io.InputStream in) { byte[] buffer = null; int bytes = 0; try { bytes = in.available(); if (bytes > 0) { buffer = new byte[bytes]; in.read(buffer, 0, buffer.length); } } catch (Exception ex) { bytes = -1; } return buffer; } /** * Scrive il buffer nel file * * @param path nome del file da salvare * @param buf byte da scrivere * * @return true se il file è stato salvato */ public static synchronized boolean writeFileBuffer(String path, byte[] buf) { boolean saved = false; try { if (buf != null) { int bytes = buf.length; if (bytes > 0) { java.io.FileOutputStream out = new java.io.FileOutputStream(path); out.write(buf, 0, buf.length); out.flush(); out.close(); saved = true; } } } catch (Exception ex) { saved = false; } return saved; } /** - 220 - APPENDICE - CODICE SORGENTE * Scrive il log nel file * * @param path nome del file da salvare * @param req richiesta da scrivere * * @return true se il file è stato salvato */ public static synchronized boolean logRequest(String path, String req) { boolean saved = false; String row = DateTime.getCurrentDateTime() + "\t" + req + "\r"; try { java.io.FileWriter out = new java.io.FileWriter(path, true); out.write(row); out.flush(); out.close(); saved = true; } catch (Exception ex) { saved = false; } return saved; } /** * Crea un file firmando un buffer di byte * * @param signed_name nome del file digest firmato e serializzato * @param uncript_buffer byte da firmare * * @return vero se il file firmato è stato creato */ public static synchronized boolean createSignedFile(String signed_name, byte[] uncript_buffer) { try { // Creazione del digest byte[]dig_buf = Certificate.getDigest("SHA1", uncript_buffer); // Operazione di firma del digest e serializzazione it.jtelemed.smartcard.Firma firma = new it.jtelemed.smartcard.Firma(); - 221 - APPENDICE - CODICE SORGENTE return firma.cifra(signed_name, dig_buf); } catch (Exception ex) { return false; } } /** * Verifica la firma del file * * @param report_name nome del file da verificare * @param digest_name nome del digest con cui verificare * * @return vero se la firma del file è verificata */ public static synchronized boolean verifySignedFile(String report_name, String digest_name) { try { // Operazione di firma del digest e serializzazione it.jtelemed.smartcard.Firma firma = new it.jtelemed.smartcard.Firma(); byte[] file_buff = FileUtil.readFileBuffer(report_name); byte[] digest_buff = Certificate.getDigest("SHA-1", file_buff); return firma.decifra(digest_name, digest_buff); } catch (Exception ex) { return false; } } /** * Ritorna il nome di un file di evento * * @param prefix Prefisso del nome del file * @param suffix Suffisso del nome del file * @param evt_id Identificativo dell'evento * * @return (prefix)-(evt_id)(12).(suffix) */ public static synchronized String getEventFileName(String prefix, String suffix, int evt_id) { - 222 - APPENDICE - CODICE SORGENTE return prefix + "-" + StringUtil.padString(Integer.toString(evt_id),12,'0',true) + "." + suffix; } /** * Ritorna il nome di un file di referto * * @param prefix Prefisso del nome del file * @param suffix Suffisso del nome del file * @param evt_id Identificativo dell'evento * @param rep_ind Indice del referto * * @return (prefix)-(evt_id)(12)-(rep_ind)(3).(suffix) */ public static synchronized String getReportFileName(String prefix, String suffix, int evt_id, int rep_ind) { return prefix + "-" + StringUtil.padString(Integer.toString(evt_id),12,'0',true) + "-" + StringUtil.padString(new Integer(rep_ind).toString(),3,'0',true) + "." + suffix; } } - 223 - BIBLIOGRAFIA BIBLIOGRAFIA Struttura di una applicazione web - Luca Balzerani 18/04/2002 L'ambiente di Tomcat - Massimo Vassalli 26/02/2002 Installazione di Apache Tomcat - Giuseppe Capodieci 13/03/2001 Java Server Pages, un'introduzione - Andrea De Paoli 10/07/2002 http://www.itportal.it/ http://www.weekit.it/ http://programmazione.html.it/ - 224 - BIBLIOGRAFIA [WSCA10] Heather Kreger, 2001, IBM, Web Services Conceptual Architecture 1.0 . [SOAP11] W3C, Simple Object Access Protocol (SOAP) 1.1. [WSDL11] W3C, Web Services Description Language (WSDL) 1.1 . [UDDI-TWP] Uddi.org, UDDI Technical White Paper . [FFP00] J.Feghhi, J.Fegghi, P.Williams, “Digital Certificates Applied Internet Security”, Addison Wesley, April 2000 [FJ03] J.Falkner, K.Jones, “Servlets and JavaServer Pages : The J2EE Technology Web Tier, Addison Wesley, Sptember 2003 [C05] C.Galasso, “Introduzione e … Jakarta Struts”, Logica Informatica S.r.l., gennaio 2005 - 225 - BIBLIOGRAFIA [CBEGL04] V.Chopra, A.Bakore, J.Eaves, B. Galbraith, S.Li, C.Wiggers, “Professional APACHE TOMCAT 5”, Wrox, May 2004 [C04] C.Cavaness, “Programming Jakarta Struts, 2nd Edition”, O’REILLY, April 2004 [CMV02] P.Chandra, M.Messier, J.Viega, “Network Security with OpenSSL”, O’REILLY, June 2002 [F04] N.Ford, “Art of Java Web Development”, Manning, July 2004 [RSA98] “Frequently asked questions about today’s cryptography”, RSA Laboratories, 1998. [S90] C.P.Schnorr, signatures “Efficient for smart identification cards”, Advances and in Cryptology, 1990 [RSA78] R.L.Rivest, A.Shamir, L.Adleman, “A Method for Obtaining Digital Signatures and Public-Key Cryptosystem”, in “Communications of the ACM” - 226 - BIBLIOGRAFIA [RFC99] C.Adams, S.Farrell, “Internet X.509 Public Key Infrastructure – Certificate Management Protocols”, Marzo 1999 [RFAQ99] “PCKS #11 Frequently Asked Question”, RSA Laboratories, http://www.rsa.com [MAM99] M.Myers, R.Ankney, A.Malpani, S. Galperin, C.Adams, “X.509 Infrastructure Internet Online Public Certificate Key Status Protocol – OCPS”, PKIX Working Group Draft, Marzo 1999 [ITSEC] Information Tecnology Security Evaluation Criteria, http://www.itsec.gov.uk [V05] I.Venuti, “Guida Pratica WEB SERVICES”, Edizione Master, Novembre 2005 Java 2 Platform Standard Edition 5.0 Development Kit (JDK) Update 5 jdk-1_5_0_05-windows-i586-p.exe from http://java.sun.com/j2se/1.5.0/download.jsp - 227 - BIBLIOGRAFIA Java 2 Platform Standard Edition 5.0 Documentation From http://java.sun.com/j2se/1.5.0/download.jsp Apache Tomcat 5.5.9 Binary jakarta-tomcat-5.5.9.exe from http://jakarta.apache.org/site/downloads/ Lomboz 3.1 Release Candidate 2 lomboz-eclipse-emf-gef from http://forge.objectweb.org/projects/lomboz MySQL 4.1.14 Database Server mysql-4.1.14-win32.zip from http://dev.mysql.com/downloads/mysql/4.1.html MySQL Java Connector 3.0.17 mysql-connector-java- 3.0.17-ga.zip from http://dev.mysql.com/downloads/connector/j/3.0.html Exadel Studio 3.0.4 ExadelStudio-3.0.4.exe from http://www.exadel.com/products_exadelstudio.htm OpenSSL 0.9.8a Toolkit openssl-0.9.8a.tar.gz http://www.openssl.org/source/ - 228 - from