In questo articolo ci “spostiamo a valle” del layer di business e parleremo delle principali tipologie di accesso sicuro al layer di presentation. Si mostrerà come accedere via HTTP e HTTPS.
Introduzione
Negli articoli dedicati all‘integrazione di applicazioni Enterprise (vedere [MOKA_INT]) si era visto come permettere la comunicazione con EJB sfruttando l‘interoperabilità del protocollo IIOP. In [MOKA_SSLIOP] si è parlato di come esporre (e accedere), in modo sicuro, oggetti remoti attraverso il protocollo IIOP over SSL.
In questo articolo ci “spostiamo a valle” del layer di business e parleremo delle principali tipologie di accesso sicuro al layer di presentation.
Si mostrerà come accedere via HTTP (via proxy e mediante Basic Authentication) e HTTPS (Basic Authenticational e mediante certificati) in modo programmatico al layer di presentation, usando le classi HTTP Client di Jakarta [JAKARTA_HTTPCLIENT] e il servlet container Tomcat [TOMCAT].
Le API HttpClient di Jakarta
Per invocare un URL mediante le API HttpClient di Jakarta basta effettuare i seguenti passi. Creare un‘istanza della classe HttpClient
HttpClient httpclient = new HttpClient();
Creare un‘istanza del metodo che si vuole invocare: GetMethod per invocazioni HTTP GET
GetMethod httpget = new GetMethod(<>);
e PostMethod per invocazioni HTTP POST
PostMethod httpPost = new PostMethod(<>);
Invocare il metodo executeMethod() passando come argomento l‘HttpMethod:
int statusCode = httpclient.executeMethod(httpget);
Impostare gli eventuali parametri d‘input
httpget.setQueryString(queryString); httpPost.setRequestEntity(new StringRequestEntity( << DATI >>);
Leggere e gestire la risposta
byte[] responseBody = httpget.getResponseBody(); String response=new String(responseBody); System.out.println("Risposta:" + response);
Rilasciare la connessione
httpMethod.releaseConnection();
Di seguito si riporta un semplice esempio di http GET
GetMethod httpget = null; try { // Creazione HttpClient HttpClient httpclient = new HttpClient(); // Creazione GET method httpget = new GetMethod("http://www.MokaByte.it"); // invocazione int statusCode = httpclient.executeMethod(httpget); // lettura della risposta byte[] responseBody = httpget.getResponseBody(); String response=new String(responseBody); System.out.println(method + " Response ["+ response+"]..."); } catch (Exception e) { e.printStackTrace(); } finally{ httpget.releaseConnection(); }
Da notare che di default l‘HTTPClient effettua automaticamente tre tentativi di riconnessione a fronte di IOException. Tale numero può essere modificato mediante la classe DefaultHttpMethodRetryHandler.
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler( 2 , false));
Proxy
Nel caso in cui per collegarsi alla rete si è in presenza di un Proxy bisogna effettuare l‘opportuna autenticazione. Per autenticarsi sul Proxy bisogna fornire all‘HttpClient le seguenti informazioni:
- indirizzo IP del Proxy mediante il metodo setProxy()
- porta TCP del Proxy mediante il metodo setProxy()
- username per il Proxy mediante il metodo setProxyCredentials()
- password per il Proxy mediante il metodo setProxyCredentials()
Di seguito si riporta un esempio di codice:
HttpClient httpclient = new HttpClient(); // Proxy Authentication (senza questa istruzione avrei un HTTP 401) httpclient.getHostConfiguration().setProxy("<>", <>); httpclient.getState().setProxyCredentials( AuthScope.ANY, new UsernamePasswordCredentials("<>", "<>"));
HTTP Basic Authentication
Le risorse Web possono essere soggette a diverse tipologie di Sicurezza. La protezione base è la Basic Authentication. Questo tipo di autenticazione prevede una username e una password per identificare chi richiede di accedere alle risorse protette. La password viene trasmessa in chiaro e codificata in base-64. In assenza quindi di altre forme di protezione (ad esempio prevedendo quantomeno una crittografia mediante certificato pubblico come vedremo più avanti con l‘HTTPS), l‘HTTP basic authentication non consente di difendersi dall‘”attacco dell‘uomo nel mezzo” [MITM].
Configurazione di Tomcat per HTTP BA
Per mettere sotto Basic Authentication le risorse in un Servlet Container bisogna indicare nel file Web.xml che il metodo di autenticazione è BASIC.
BASIC
Le risorse da proteggere devono invece essere specificate mediante i tag e . Ad esempio, per mettere sotto protezione
/mine/*
In questo modo Tomcat richiederà automaticamente al Web browser di inserire la username e password per accedere alle risorse protette. L‘esempio che segue mette sotto protezione di Basic Authentication le risorse contenute nelle directory mine della nostra WebApp di test.
Entire Application /mine/* <> BASIC My Rolename-only Area
Bisogna configurare in modo coerente il file
<>/conf/tomcat-users.xml
aggiungendo le utenze ed i relativi ruoli mediante i tag and .
. . . >"/> . . . >"/>
In questo modo se si richiede di accedere all‘URL
http://<>:8080/kokhttps/mine/my_private_page.jsp
compare la popup di autenticazione. Solo inserendo la username e password precedentemente configurate nel file tomcat_users.xml sarà possibile accedere alla risorsa my_privarte_page.jsp.
Accesso programmatico via HTTP BA
A livello programmatico l‘accesso mediante Basic Authentication (BA) richiede di specificare userid e password mediante l‘oggetto Credentials da settare sull‘HttpClient:
httpclient.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(<>, <>));
Una volta passate l‘utenza e la password per la basic authentication, il resto della comunicazione avviene usando le API dell‘HttpClient già viste.
HTTPS
In molti casi si ha la necessità da un lato di proteggere i dati scambiati usando la crittografia, dall‘altro di accertare l‘identità del server con cui si vuole comunicare. L‘HTTPS soddisfa entrambe le esigenze. Garantisce, infatti, l‘identità del server attraverso l‘uso dei certificati e allo stesso tempo fornisce un canale di comunicazione protetto.
HTTPS di fatto combinal‘interazione del protocollo HTTP attraverso un meccanismo di crittografia di tipo Secure Sockets Layer (SSL). In pratica viene creato un canale di comunicazione criptato tra il client e il server attraverso lo scambio di certificati; una volta stabilito questo canale al suo interno viene utilizzato il protocollo HTTP per la comunicazione. Questa tecnica assicura una buona protezione contro attacchi del tipo “Man In The Middle”.
Per impostare un server in modo che accetti connessioni di tipo HTTPS, l‘amministratore deve creare un certificato digitale, ovvero un documento elettronico che associa l‘identità di una persona a una chiave pubblica. Questi certificati devono essere rilasciati da un Certificate Authority o comunque da un sistema che accerta la validità dello stesso in modo da definire la vera identità del possessore (i browser web sono in grado di verificare la validità dei certificati).
Il formato dei certificato utilizzato da SSL è quello dettato dalla specifica X.509 [RFC_X509]. Per casi di prova e/o di test di applicazioni che devono comunicare con SSL, si può scegliere di utilizzare direttamente i certificati generati dal keytool (come nei nostri esempi) o simulare una CA e autofirmare la richiesta di certificato con l‘ausilio di tools PKI come OpenSSL. Nei casi reali è però opportuno ottenere un certificato rilasciato da una Certification Authority.
Il keytool
Per creare la coppia di chiavi pubblica e privata necessarie a garantire l‘autenticità della controparte e la riservatezza della comunicazione tra client è server è possibile utilizzare il programma keytool del JDK. La chiave pubblica viene inclusa in un certificato, per cui di seguito per comodità si parlerà semplicemente di chiave privata e certificato.
Il keytool memorizza le chiavi e i certificati in un keystore, che generalmente è un file con estensione .jks, proteggendo le chiavi private con password. Un file di chiavi contenente solo certificati (e non anche la chiave privata, come avviene per il keystore) viene invece chiamato trustStore.
Generazione delle chiavi
Di seguito verrà mostrata la procedura di generazione di una chiave privata del server e della sua corrispondente chiave pubblica. Per generare una nuova chiave privata basta lanciare il comando keytool con l‘opzione ?genkey: il keytool crea una chiave privata e la sua corrispettiva chiave pubblica in un file criptato denominato keystore, che viene generato nella directory del server dalla quale è stato lanciato il comando.
Nel nostro caso si provvede a creare un keystore di nome mokaserverstore.ks, identificato dall‘alias mokaserveralias e “protetto” da una password ‘tds1977‘ che permette la verifica dell‘integrità del keystore. Con l‘opzione ?dname si specificano i dati di identificazione dell‘intestatario del certificato in formato Distinguished Name X.500 (lo stesso usato, ad esempio, nei server LDAP).
Lanciando il comando
keytool -genkey -alias mokaserveralias -keystore mokaserverkeystore.ks ?dname "CN=Amedeo, OU=Redazione, O=MokaByte, L=BO, S=IT, C=IT" -storepass tds1977
si ottiene il file
mokaserverkeystore.ks
Tale comando richiede di immettere la password della chiave privata: premendo invio quest‘ultima coincide con quella del keystore che nel nostro esempio sarebbe tds1977
C:MokaBytesample_httpscert>D:installedjdk1.5.0_06 inkeytool -genkey -alias mokaserveralias -keystore mokaserverkeystore.ks -dname "CN=Amedeo, OU=Redazione, O=MokaByte, L=BO, S=IT, C=IT" -storepass tds1977 Immettere la password della chiave per (INVIO se corrisponde alla password del keystore): [INVIO]
Export del certificato
Per avere un certificato pubblico valido da poter usare nelle nostre connessioni HTTPS si può usare il certificato self signed all‘interno del keystore del server. Un certificato self-signed è semplicemente quello in cui issuer e subject sono la stessa entità e può essere estratto dal precedente file mokaserverkeystore.ks con il comando sottostante:
keytool -export -keystore mokaserverkeystore.ks -alias mokaserveralias -file mokaserver.cer -storepass tds1977
Il file mokaserver.cer così ricavato è già pronto per essere distribuito ad un client.
Nella realtà , però, per creare un certificato bisogna produrre una certificate request (anche detta, da alcune autorità di certificazione, “certificate signing request”, in quanto il compito di queste ultime consiste appunto nell‘apporre una firma). Una richiesta di certificato viene generata nel seguente modo:
keytool -certreq -alias "mokaserveralias" -file mokacertreq.csr -keystore mokaserverkeystore.ks ?storepass tds77
La richiesta di certificato viene mandata a una certification authority che la firma e restituisce al mittente il certificato prodotto secondo le sue policy.
Import del certificato
Per quanto riguarda il client, alcune applicazioni (come i browser) sono in grado di gestire direttamente le chiavi pubbliche attraverso i file .cer. Viceversa, per poter utilizzare una chiave pubblica in modo programmatico attraverso le API Java (come è il nostro caso) dovremo importare il certificato in un file di formato JKS (lo stesso formato del keystore precedentemente generato), che come accennato in precedenza viene chiamato truststore.
Per importare il certificato precedentemente esportato in un trustStore sul client è possibile utilizzare l‘utility di import, specificando il .cer che si vuole importare e il nome del trustStore che si vuole creare.
keytool -import -keystore mokaclienttruststore.ks -alias mokaserveralias -file mokaserver.cer -storepass tds1982
A questo punto, ottenuto il file mokaclienttruststore.ks , abbiamo tutti gli elementi per poter comunicare via HTTPS tra client e server.
Per lo scenario HTTPS con mutua autenticazione (che verrà descritto successivamente) i passi sopra mostrati vanno ripetuti anche sul client allo scopo di ottenere un keystore del client, contenete chiave privata e certificato, ed un truststore del server contenente il certificato pubblico del client.
HTTPS Basic Authentication
Come accennato in precedenza, l‘HTTPS porta con sè un meccanismo di trust in del server. Di fatto in questo scenario viene verificata soltanto l‘identità del server. Le chiavi generate saranno quindi la chiave privata del server e la sua corrispondente chiave pubblica. Il server userà la propria chiave privata per asserire la propria identità ed il client userà il certificato corrispondente (contenente la chiave pubblica) per verificarla. Pur avendosi una sola coppia di chiavi, la cifratura della comunicazione avviene comunque in entrambi i sensi, perché le chiavi asimmetriche verranno usate per scambiarsi una chiave segreta temporanea che verrà impiegata per cifrare i dati scambiati durante la sessione di comunicazione.
Riassumendo, per il trust in server si richiedono:
- Lato Server: keystore con chiave privata e certificato server
- Lato Client: trustStore con certificato server
Figura 5 – Https Basic Auhtentication (Trust in Server)
L‘HTTPS basic authentication combina i vantaggi del protocollo HTTPS, ossia comunicazione criptata e trust del server, all‘identificazione del client mediante username e password. La differenza rispetto alla basic authentication via HTTP consiste quindi nel fatto che la password viene trasmessa in modo criptato.
Per configurare l‘HTTPS su Tomcat si deve editare il file
${tomcat.server.home.dir}/conf /server.xml e configurare il connector SSL/TLS:
I parametri da configurare sono i seguenti:
- Connector Port: specificare il numero di porta che si vuole utilizzare per le connessioni https (il default è 8443)
- keystoreFile: impostare il path del file del keystore
- keystorePass: indicare la password per aprire il keystore
Per esempio:
maxThreads="150" scheme="https" secure="true" clientAuth="false" keystoreFile ="J:/Programmi/tomcat-6.0.13/conf/tempArti/mokaserverkeystore.ks" keystorePass="tds1977" sslProtocol="TLS" />
A questo punto è possibile lanciare Tomcat e provare la connessione sulla porta 8443, o quella indicata in Connector Port, in HTTPS. Riprendendo l‘esempio precedente, per mettere sotto protezione HTTPS Basic Authentication la pagina JSP /mine/my_private_page.jsp basta modificare il file Web.xml indicando l‘authentication mode come DIGEST.
A questo punto è possibile lanciare Tomcat e provare la connessione HTTPS sull‘opportuna porta configurata. In questo modo se si richiede di accedere all‘URL https://<>:8443/kokhttps/mine/my_private_page.jsp compare dapprima la popup del certificato e successivamente la popup di autenticazione.
Solo inserendo la username e password precedentemente configurate nel file tomcat_users.xml sarà possibile accedere alla risorsa my_privarte_page.jsp. Rispetto alla HTTP BA, in questo caso la password viene trasmessa in modo criptato.
A livello programmatico basta esplicitare, mediante le proprietà javax.net
- la locazione del truststore
- la storepass
- la tipologia del truststore
sysprops.put("java.protocol.handler.pkgs","javax.net.ssl.internal.www.protocol"); sysprops.put("javax.net.debug", "ssl,handshake,data,trustmanager"); sysprops.put("javax.net.ssl.trustStore", <>); sysprops.put("javax.net.ssl.trustStorePassword", <>); sysprops.put("javax.net.ssl.trustStoreType", "JKS"); HttpClient httpclient = new HttpClient(); httpget = new GetMethod("https://localhost:8443/kokhttps/mine/my_private_page.jsp); httpclient.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(<>, <>)); int statusCode = httpclient.executeMethod(httpget);
HTTPS con Mutua autenticazione
Per accertarsi che anche il client che richiede un certo servizio da un Server remoto sia riconosciuto da questo come un‘entità autorizzata a fruirne, oltre all‘HTTPS basic authentication si ha a disposizione la mutua autenticazione HTTPS. Con questa tecnica l‘autenticazione avviene in entrambi i sensi attraverso i certificati:
- da un lato il server con la propria chiave privata dà prova della sua identità , mentre il client verifica quest‘ultima con il certificato pubblico del server
- dall‘altro il client risponde codificando con la sua chiave privata ed il server lo autentica decodificando con il certificato pubblico del client.
Per questo, in aggiunta alle configurazioni del trust in server, è necessario indicare all‘applicazione dove potrà trovare la propria chiave privata, il proprio certificato, e come verificare l‘identità del server. A tale scopo sono necessari un keystore, contenente il certificato e la chiave privata del client, ed un truststore, contenente il certificato del server con il quale comunicare, che consentirà quindi un riconoscimento univoco e mirato al quel particolare servizio/sistema.
Riepilogando, nell‘autenticazione mutua sono previste le seguenti configurazioni:
- Lato Server: Keystore con chiave privata e certificato server; Trustore con certificato client
- Lato Client: Keystore con chiave privata e certificato clien; Trustore con certificato server
Per configurare l‘HTTPS per la mutua autenticazione su Tomcat si deve editare il file ${tomcat.server.home.dir}/conf /server.xml, impostando a “true” l‘opzione clientAuth :
Questo però non è ancora sufficiente. così configurato Tomcat non farà accedere alcun utente. È infatti necessario configurare il trustStore, ovvero l‘archivio dei certificati client e delle Autorità di Certificazione affidabili, che conterrà :
- i certificati root di Autorità di Certificazione: in questa modalità , tutti gli utenti in possesso di certificati validi ed emessi dalle Autorità di Certificazione i cui certificati sono inclusi nel trustStore sono automaticamente autorizzati ad accedere;
- i singoli certificati degli utenti che hanno facoltà di accedere ai servizi: in questo modo, solo gli utenti i cui certificati sono inclusi nel trustStore possono accedere al server.
Per fare questo, modificare il solito file di configurazione ${tomcat.server.home.dir}/conf /server.xml aggiungendo il percorso del truststore e la password per accedervi:
maxThreads="150" scheme="https" secure="true" clientAuth="true" keystoreFile="J:/Programmi/tomcat-6.0.13/conf/tempArti/mokaserverkeystore.ks" keystorePass="tds1977" truststoreFile="J:/Programmi/tomcat-6.0.13/conf/tempArti/mokaservertruststore.ks" truststorePass="sos888" sslProtocol="TLS" />
Al prossimo riavvio Tomcat si metterà in ascolto sulla porta indicata in modalità HTTPS con mutua autenticazione.
Dal punto di vista programmatico, anche nel caso della mutua autenticazione HTTPS, è possibile usare le Java system properties mostrate nel caso dell‘HttpsBA. Alle proprietà che servono per identificare ed accedere al truststore, si aggiungono nella mutua autenticazione le corrispondenti proprietà per il keystore (javax.net.ssl.keyStoreType, javax.net.ssl.keyStore e javax.net.ssl.keyStorePassword).
sysprops.put("java.protocol.handler.pkgs","javax.net.ssl.internal.www.protocol"); sysprops.put("javax.net.debug", "ssl,handshake,data,trustmanager"); sysprops.setProperty("javax.net.debug", "true"); sysprops.put("javax.net.ssl.trustStore",<>)); sysprops.put("javax.net.ssl.trustStorePassword", <>); sysprops.put("javax.net.ssl.trustStoreType", "JKS"); HttpClient httpclient = new HttpClient(); httpget = new GetMethod("https://localhost:8443/kokhttps/my_public_page.jsp"); // Execute the method. int statusCode = httpclient.executeMethod(httpget);
Questo approccio è valido nel caso in cui la password per recuperare la chiave nel keystore coincide con quella per aprire il keystore (questo tra l‘altro è il comportamento di default del keytool).
Se però non coincidono, per gestire programmaticamente la password della chiave privata bisogna creare un‘implementazione di una classe custom dell‘interfaccia ProtocolSocketFactory [HTTPCLIENT_SSL_GUIDE].
Conclusioni
In questo articolo si è parlato di come sia possibile accedere in modo sicuro al layer di presentation mediante i meccanismi di Basic Authentication e utilizzo dei certificati.
Riferimenti
[MOKA_INT]
S. Rossini, “Integrazione di applicazioni Enterprise. I parte”, MokaByte 71, Aprile 2003
S. Rossini, “Integrazione di applicazioni Enterprise. II parte”, MokaByte 72, Maggio 2003
S. Rossini, “Integrazione di applicazioni Enterprise. III parte”, MokaByte 73, Giugno 2003
S. Rossini, “Integrazione di applicazioni Enterprise. IV parte”, MokaByte 74, Luglio/Agosto 2003
S. Rossini, “Integrazione di applicazioni Enterprise. V parte”, MokaByte 74, Settembre 2003
S. Rossini, “Integrazione di applicazioni Enterprise. VI parte”, MokaByte 83, Marzo 2004
[MOKA_SSLIOP]
S.Rossini, “IIOP over SSL. I parte La teoria”, MokaByte 91, Dicembre 2004
S.Rossini, “IIOP over SSL. II parte”, MokaByte 92, Gennaio 2005
[JAKARTA_HTTPCLIENT]
http://commons.apache.org/httpclient
[HTTPCLIENT_TUT]
http://commons.apache.org/httpclient/tutorial.html
[TOMCAT] http://tomcat.apache.org/
[SSL_SPEC] SSL 3.0 specification
http://wp.netscape.com/eng/ssl3
[MODSSL]
http://www.modssl.org/docs/2.8/ssl_intro.html
[RFC_X509] RFC 2459 – Internet X.509 Public Key Infrastructure Certificate and CRL Profile
[10TCT] J. Brittain – I.F. Darwin “Top Ten Tomcat Configuration Tips”
http://www.onjava.com/pub/a/onjava/2003/06/25/tomcat_tips.html
[OPEN_SSL]
http://www.openssl.org/
[KEYTOOL]
http://java.sun.com/j2se/1.3/docs/tooldocs/win32/keytool.html
[MITM]
http://it.wikipedia.org/wiki/Man_in_the_middle
[HTTPCLIENT_SSL_GUIDE]
http://jakarta.apache.org/httpcomponents/httpclient-3.x/sslguide.html
Stefano Rossini è nato a Giussano (MI) il 29/10/1970 e ha conseguito il diploma universitario in Ingegneria Informatica presso il Politecnico di Torino. Ha maturato più di venti anni di esperienza in diversi progetti Enterprise mission-critical ricoprendo i ruoli di IT Program Manager, Project Manager & Software Architect presso importanti gruppi bancari, pubblica sanità, pubblica amministrazione e software house.
Attualmente ricopre il ruolo di Sofware Factory Manager, Lean Change Agent ed Enterprise Architect presso Capgemini.
Esperto in ambito di sanità pubblica come Project/Program Manager per la governance dei progetti IT strategici di Cartella Clinica Elettronica (CCE) e Fascicolo Sanitario Elettronico (FSE).
Esperto in ambito bancario dove ha ricoperto per una decina d'anni il ruolo di Project Manager e Leader Software Architect (BPM, IWBank e BPS) occupandosi della pianificazione e gestione del progetto, del coordinamento del gruppo di sviluppo software sia InHouse che Nearshore/Offshore. Esperto nella conduzione di progetti secondo metodologia di Project Management PMBok e metodologia agile Scrum.
Si occupa di Java dal 1999 arrivando da precedenti esperienze in C e C++ in ambito Telco (Alcatel & Siemens). Ha pubblicato più di un centinaio di articoli su argomenti di IT Governance, Project Management, architetture enterprise e problematiche di Integrazione e SOA. È coautore dei libri "Manuale pratico di Java" (2001) e "La programmazione della piattaforma J2EE" (2005) editi da Hops/Tecniche Nuove. Certificazioni IT Governance: COBIT V.4.1 Foundation Certificate; certificazioni IT Service Management: ITIL V.3 Foundation Examination; certificazioni Project Management: CSM - Scrum Master, CSPO - Scrum Product Owner, PMI: 35 contact hours.
Profilo linkedin: http://www.linkedin.com/pub/stefano-rossini/30/977/242
Amedeo Cannone si è laureato in Ingegneria Informatica presso l‘università degli studi di Bologna.
Dal 2003 lavora per il Gruppo Imola per cui svolge attività di consulenza su tematiche architetturali e di processo.
Al momento sta seguendo alcuni progetti di integrazione SOA e si sta interessando di ESB e JBI.
Il profilo completo è visualizzabile su LinkedIn all‘indirizzo
http://www.linkedin.com/pub/dir/Amedeo/Cannone