Accesso HTTPS via Java

Accesso sicuro al layer di presentationdi e

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].

Figura 1 - HTTP(S) e IIOP(S)

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


Figura 2 - Configurazione del web.xml per HTTP-BA

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.

Figura 3 - HTTP Basic Auhtentication

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".

Figura 4 - 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_06inkeytool 
-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.

Figura 6 - HTTPS Basic Auhtentication

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

Condividi

Pubblicato nel numero
123 novembre 2007
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.…
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…
Ti potrebbe interessare anche