Esempio
Trust In Target: Generazione delle chiavi
Ricordiamo
dal numero precedente che uno dei capisaldi del
protocollo SSL è la infrastruttura a chiave
pubblica (o asimmetrica). Dovremo quindi per prima
cosa generare una coppia di chiavi, una pubblica
ed una privata. Abbiamo bisogno di una sola coppia
di chiavi perché in questo scenario vogliamo
che venga verificata soltanto l'identità
del target (cioè del server). Le chiavi
che dovremo generare 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à la chiave pubblica corrispondente
per verificarla. Ricordiamo inoltre che 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.
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.
Specificando
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 dalla quale è stato lanciato
il comando. La chiave pubblica viene inclusa in
un certificato self signed all'interno del file
prodotto. Ricordiamo dal numero precedente che
un certificato non è altro che un'attestazione
firmata da una certification authority (entità
issuer) che assicura la bontà della chiave
pubblica di un subject. Un certificato self-signed
è semplicemente quello in cui issuer e
subject sono la stessa entità.
Il
principale inconveniente dei certificati self
signed è appunto il fatto che non prevedono
la firma da parte di una well-known Certification
Authority (CA) che faccia da garante della validità
e bontà del certificato. In questo modo
i certificati self signed possono essere facile
oggetto di attacchi ad esempio di tipo Man In
The Middle (vedere[MITMA] e [TSSLC]).
Quindi
un certificato self signed è molto utile
per casi di prova (come il nostro) e/o di test
per applicazioni che devono comunicare con SSL;
nei casi reali sarà più opportuno
ottenere un certificato rilasciato da una Certification
Authority.
Il formato dei certificato utilizzato da SSL è
quello dettato dalla specifica X.509 (vedere [RFC_X509]).
Nel
nostro caso si provvede a creare un keystore di
nome mokastore.ks, identificato dall'alias mokalias
e "protetto" da una password 'alberto'
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).
%JAVA_HOME%\bin\keytool
-genkey -alias "mokalias" -keystore
"mokakeystore.ks" -dname "CN=Alberto,
OU=Redazione, O=Mokabyte, L=Mi, S=IT, C=IT"
-storepass "alberto"
Si
noti che per facilitare la lettura, assoceremo
in modo consistente in questo articolo il nome
'Alberto' al server ed il nome 'Stefano' al client,
sia come Distinguished Name del subject che come
password.
Per distribuire la chiave pubblica (nel nostro
caso al client) è necessario separarla
dal file di keystore. Tale procedura si compone
di due passi: dapprima si esegue l'export del
certificato self-signed, ottenendo un file .cer:
%JAVA_HOME%\bin\keytool
-export -keystore "mokakeystore.ks"
-alias "mokalias" -file "mokastorepass.cer"
-storepass "alberto"
Alcune
applicazioni (ad esempio 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). Come abbiamo accennato nello scorso
articolo, un file di chiavi contenente solo certificati
(e non anche la chiave privata, come avviene per
il keystore) viene chiamato trustStore.
Per
importare il certificato in un trustStore destinato
al client è possibile utilizzare l'utility
di import specificando il .cer che si vuole importare
e il nome del trustStore che si vuole creare.
%JAVA_HOME%\bin\keytool
-import -keystore "pub_mokakeystore.ks"
-alias "mokalias" -file "mokastorepass.cer"
-storepass "alberto"
Come
terminologia, useremo in modo consistente la denominazione
pub_xyz.ks per indicare il trustStore associato
al keystore xyz.ks. Il file così ottenuto
è pronto per essere distribuito alle postazioni
client.
Abilitazione di IIOP/SSL su JBoss
Vediamo
quindi quali sono i passi necessari per abilitare
il trasporto sicuro SSL nell'esposizione di un
EJB in JBoss. Ovviamente, si farà riferimento
alla Server Configuration 'all' di JBoss che è
quella che contiene il supporto per l'esposizione
attraverso IIOP e quindi i necessari file di configurazione
per poter abilitare anche il supporto a IIOP/SSL
(vedere [MOKA_INT_3]). Si raccomanda inoltre di
utilizzare una versione di JBoss almeno pari 3.2.5.
Innanizitutto
è bene precisare che le operazioni necessarie
per abilitare l'SSL non richiedono nessuna modifica
a livello di sorgente Java.
Come primo passo bisonga editare il file <<JBOSS_HOME>>/server/all/deploy/iiop-service.xml
e scommentare le sezioni specificate all'inizio
del file per abilitare il supporto SSL: si tratta
innanzitutto di scommentare l'MBean JaasSecurityDomain
<!--
(uncomment to use IIOP over SSL)
-->
<mbean code="org.jboss.security.plugins.JaasSecurityDomain"
name="jboss.security:service=JaasSecurityDomain,domain=IIOP+SSL">
<constructor>
<arg type="java.lang.String" value="IIOP+SSL"/>
</constructor>
<attribute name="KeyStoreURL">YOURKEYSTORE</attribute>
<attribute name="KeyStorePass">YOURKEYSTOREPASS</attribute>
</mbean>
Al
posto delle stringhe YOURKEYSTORE, YOURKEYSTOREPASS,
dovrete inserire rispettivamente un URL che punti
al file di keystore che il server dovrà
utilizzare e la relativa password.
Ottenuto
il keystore di nome mokakeystore.ks come descritto
al paragrafo precedente, al posto di YOURKEYSTORE
dovrete scrivere:
file://C:/mokabyte/moka_iiop_over_ssl/cert/trust_in_target/mokakeystore.ks
e
al posto di YOURKEYSTOREPASS scriverete 'alberto'.
Oltre a questo, si deve procedere a scommentare
l'attributo 'SecurityDomain' ed il relativo elemento
innestato 'depends' presente nel MBean CorbaORBService
nello stesso file
<mbean
code="org.jboss.iiop.CorbaORBService"
name="jboss:service=CorbaORB">
<attribute name="ORBClass">org.jacorb.orb.ORB</attribute>
<attribute name="ORBSingletonClass">org.jboss.system.ORBSingleton</attribute>
<attribute name="ORBSingletonDelegate">org.jacorb.orb.ORBSingleton</attribute>
<attribute name="ORBPropertiesFileName">jacorb.properties</attribute>
<attribute name="PortableInterceptorInitializers">
<interceptor-initializers>
<initializer>org.jboss.iiop.codebase.CodebaseInterceptorInitializer</initializer>
</interceptor-initializers>
</attribute>
<!-- (uncomment to use IIOP over SSL)
-->
<attribute name="SecurityDomain">java:/jaas/IIOP+SSL</attribute>
<depends>jboss.security:service=JaasSecurityDomain,domain=IIOP+SSL</depends>
</mbean>
Come
secondo passo bisogna editare il file <<JBOSS_HOME>>/server/all/conf/jacorb.properties
che contiene i settaggi dell'ORB Jacorb integrato
in JBoss.
Dovrete anzitutto impostare la property:
jacorb.security.support_ssl=on
(per default è impostata a off)
Poi
bisognerà impostare le property seguenti:
- jacorb.security.ssl.client.supported_options=20
- jacorb.security.ssl.client.required_options=0
- jacorb.security.ssl.server.supported_options=20
- jacorb.security.ssl.server.required_options=20
Il
valore numerico 20 (esadecimale) impostato corrisponde
all'opzione 'Trust in Target', come definito nella
specifica CSIv2 (vedere [CORBA3.02_SPEC] al paragrafo
24.9.3). Le quattro proprietà così
impostate indicano che l'ORB integrato in JBoss
richiede in modo mandatorio l'impiego della feature
'Trust in Target' e cioè la verifica dell'identità
dell'oggetto chiamato, quando l'ORB ha il ruolo
di server, mentre la supporta senza richiederla
obbligatoriamente quando è usato nel ruolo
di client.
Si
osservi che non si è impostata la seconda
proprietà 'client.required_options' al
valore 20, cosa che avrebbe reso la feature trustInTarget
obbligatoriamente richiesta nell'handshake dall'ORB
di JBoss anche nel suo ruolo client.
Il motivo di questa scelta è purtroppo
un bug presente in Jacorb versione 2.1 (vedere
[JCO_BG]), che è la versione integrata
in JBoss almeno fino alla versione 3.2.6, che
fa sì che quasi tutti i valori forniti
alla property jacorb.security.ssl.client.required_options
causino erroneamente la seguente eccezione:
NO_PERMISSION: Client-side policy requires TLS
but server doesn't support it
Per
le prove che faremo la proprietà jacorb.security.ssl.client.required_options
verrà quindi settata sempre al valore 0.
Si noti che la versione di Jacorb 2.2 standalone
(ossia non integrata in JBoss) non presenta questo
problema.
Se
vi fermate a quanto detto finora e tentate di
avviare JBoss e deployare un EJB esposto tramite
RMI/IIOP, l'effetto che avrete sarà un'eccezione
del tipo:
SSLHandshakeException:
.
no trusted certificate found.
Cosa
è successo? il fatto è che le impostazioni
ora eseguite si applicano ad ogni server CORBA
presente in JBoss e quindi anche al Naming Server.
Il problema si manifesta infatti nel momento in
cui JBoss all'atto del deploy tenta di registrare
il nome del nuovo EJB al proprio Naming Server.
In effetti bisogna considerare che adesso tale
Naming Server è 'blindato' e utilizza anch'esso
SSL in tutte le interazioni, anche quelle che
sono originate dall'interno dello stesso JBoss.
Figura 1: Fallimento comunicazione tra
JBoss Server e JNDI Server
Per superare questo "ostacolo", bisogna
rendere noto a JBoss il certificato corrispondente
alla private key che si è impostata nel
primo passo.
Per
rendere noto tale certificato a JBoss quando agisce
come client, è sufficiente specificare
come proprietà di sistema di JBoss quanto
segue:
-Djavax.net.ssl.trustStore=
C:/mokabyte/moka_iiop_over_ssl/cert/trust_in_target/pub_mokakeystore.ks
Per
specificare tale property potete ad esempio modificare
lo script di lancio di JBoss (run.bat / run.sh)
e specificare il settaggio come JAVA_ARGS.
Figura 2: Comunicazione "sicura"
tra JBoss Server e JNDI Server
Esecuzione
del Client
Proviamo per prima cosa a vedere se
un client Java riesce a collegarsi all'Application
Server che ha l'SSL abilitato, senza utilizzare
SSL.
Nel build.xml allegato agli esempi di questo articolo
abbiamo incluso a tale scopo i due target 'run_iiop_sun'
e 'run_iiop_jacorb'. Questi hanno lo scopo di
avviare un semplice programma client che tenta
di connettersi all'EJB Calculator, usando rispettivamente
l'ORB SUN e quello Jacorb, ma in entrambi i casi
senza utilizzare nessuna impostazione riguardo
a SSL e in particolare senza possedere alcun certificato.
L'operazione giustamente fallisce in ambedue i
casi perché il server forza l'utilizzo
di SSL e all'atto dell'handshake trasmette al
client il proprio certificato che però
il client non riconosce come fidato, in quanto
esso non è presente nel trustStore. Si
ricorda infatti che se non viene specificato diversamente,
il trustStore di un'applicazione Java è
il file %JAVA_HOME%\jre\lib\security\cacerts che
contiene soltanto i certificati delle CA principali.
L'errore che si ottiene è: org.omg.CORBA.NO_PERMISSION:
vmcid: 0x0 minor code: 3 completed: No (vedere
[MOKA_SECIOP_1]).
Vediamo
quindi di far sì che il client riconosca
ed autentichi il target invocato. Per far questo,
bisogna indicare al client di utilizzare in luogo
del trustStore di default il file pub_mokakeystore.ks
contenente il certificato con la chiave pubblica
del server, mediante le proprietà
-Djavax.net.ssl.trustStore=
C:/mokabyte/moka_iiop_over_ssl/cert/trust_in_target/pub_mokakeystore.ks
Oltre
a questo, bisogna specificare ulteriori proprietà
specifiche ad SSL e utilizzando Jacorb questo
può essere fatto fornendo un file di property
come quello incluso nello zip di esempi col nome
jsse_client_props.
In
questo file sono state impostate le property
- jacorb.security.ssl.client.supported_options=20
- jacorb.security.ssl.client.required_options=0
Non
sono presenti le property relative al server in
quanto questo è un puro client. E' inoltre
necessario specificare un qualunque file di keystore,
tramite la property jacorb.security.keystore,
altrimenti Jacorb genera un eccezione, ma in questo
caso può anche essere un keystore fasullo,
perché il server non è impostato
per autenticare il client.
Lanciando il client otteniamo che la comunicazione
avviene con successo visto che il client riconosce
correttamente il server.
Figura 3: Trust in Target: Comunicazione sicura
tra client e server
Per eseguire questa prova vi suggeriamo di lanciare
il target ANT run_iiop_ssl_jacorb presente nel
build.xml degli esempi.
Una
cosa importante da notare nelle opzioni di lancio
del client con SSL è l'URL che bisogna
fornire come bootstrap per inizializzare il JNDI
Context del Client. Si ricorderà che in
assenza di SSL bisognava utilizzare un URL CORBA
del tipo:
corbaloc:iiop:<<SERVER>>:3528/JBoss/Naming/root
Figura 4: Comunicazione IIOP tra client e
server
Utilizzando SSL (con Jacorb) l'Url da utilizzare
sarà invece del tipo:
corbaloc:ssliop:<<SERVER>>:3529/JBoss/Naming/root
Figura 5: Comunicazione SSL IIOP tra client
e server
Si notino le due differenze: la più elementare
è il fatto che viene utilizzata la porta
3529 in luogo di 3528 perché questa è
la porta usata per default dall'ORB in JBoss quando
è attivo SSL (similmente a quanto avviene
con il passaggio dalla porta 80 alla porta 443
quando si impiega HTTPS). L'altra differenza è
che viene specificata la stringa ssliop invece
che iiop. Questo serve a informare il client ORB
che l'Oggetto Corba da usare per il bootstrap
è esposto con SSLIOP. Si noti che questo
formato di Url Corba 'ssliop' ad oggi non è
ancora stato standardizzato dall'OMG benché
sia la scelta impiegata da diversi vendor tra
cui Jacorb.
Può
essere molto istruttivo impostare nel client la
system property
-Djavax.net.debug=ssl
con
cui si fa sì che vengano tracciati tutti
i messaggi scambiati tra il client ed il server
per portare a termine la fase di handshake che
è stata descritta nel numero precedente.
Esempio
di autenticazione
Mentre
l'autenticazione del target permette di essere
certi che quando si invoca un Server Remoto (il
Target) chi ci risponde sia effettivamente quello
che ci aspettiamo, l'autenticazione mutua permette
di accertarsi che anche il client che richiede
un certo servizio da un Server remoto sia riconosciuto
da questo come un'entità autorizzata a
fruirne.
In
uno scenario di comunicazioni diretta fra oggetti
(B2B) può essere rilevante sia l'autenticazione
del target che del client: cioè, la mutua
autenticazione.
Generazione
delle chiavi
Oltre
ai certificati mokakeystore.ks e pub_mokakeystore.ks
utilizzati nel precedente esempio dobbiamo creare
una nuova coppia di certificati.
Creiamo quindi il keystore client_mokakeystore.ks
associato a Stefano
%JAVA_HOME%\bin\keytool
-genkey -alias "client_mokalias" -keystore
"client_mokakeystore.ks" -dname "CN=Stefano,
OU=Redazione, O=Mokabyte, L=Mi, S=IT, C=IT"
-storepass "stefano"
e
provvediamo ad esportarlo e ad importarlo in un
file di nome pub_client_mokakeystore.ks:
%JAVA_HOME%\bin\keytool
-export -keystore "client_mokakeystore.ks"
-alias "client_mokalias" -file "client_mokastorepass.cer"
-storepass "stefano"
%JAVA_HOME%\bin\keytool
-import -keystore "pub_client_mokakeystore.ks"
-alias "clent_mokalias" -file "client_mokastorepass.cer"
-storepass "stefano"
E'
ora necessaria un'ulteriore operazione: in questo
scenario il server JBoss dovrà possedere
il certificato del client (per poterlo autenticare)
ma anche il certificato del server stesso (per
poter autenticare il Naming Server al momento
del deploy). Per ottenere questo, bisogna aggiungere
al file pub_client_mokakeystore.ks anche il certificato
del server:
%JAVA_HOME%\bin\keytool
-import -keystore "pub_client_mokakeystore.ks"
-alias "mokalias" -file "../trust_in_target/mokastorepass.cer"
-storepass "stefano"
Abilitazione
di IIOP/SSL su JBoss
Le
impostazioni da fare per questo scenario sono
molto simili a quelle del caso precedente: in
particolare, il file iiop-service.xml dovrà
essere impostato esattamente come fatto prima,
infatti il server JBoss dovrà referenziare
anche in questo caso la propria chiave privata
collocata in: C:/mokabyte/moka_iiop_over_ssl/cert/trust_in_target/mokakeystore.ks
Il
file jacorb.properties invece dovrà essere
leggermente variato: in questo caso infatti dovremo
impostare
jacorb.security.ssl.client.supported_options=60
jacorb.security.ssl.client.required_options=0
jacorb.security.ssl.server.supported_options=60
jacorb.security.ssl.server.required_options=60
Il
valore 60 è il codice associato alla feature
di mutua autenticazione; anche in questo caso
bisogna lasciare a zero la property client.required_options
a causa del bug sopra menzionato.
Infine,
bisognerà avviare JBoss fornendo la system
property
-Djavax.net.ssl.trustStore=
C:/mokabyte/moka_iiop_over_ssl/cert/mutual_authentication/pub_client_mokakeystore.ks
in
modo che il server JBoss sia in grado di autenticare
l'identità del proprio Naming Server e
del client remoto. All'avvio
di JBoss con queste impostazioni potrete notare
il seguente messaggio addizionale nei log:
20:04:22,261
INFO [SSLServerSocketFactory] Will create SSL
sockets that require client authentication
Esecuzione
del client
Per
completare questo passo è solo necessaria
qualche lieve modifica al file contenente le property
relative a ssl che abbiamo chiamato in questo
caso jsse_client_props_mutual. Dovremo infatti
impostare le property nel seguente modo:
jacorb.security.ssl.client.supported_options=60
jacorb.security.ssl.client.required_options=0
Inoltre
in questo caso il keystore specificato mediante
jacorb.security.keystore dovrà obbligatoriamente
contenere un chiave privata che corrisponda ad
uno dei certificati presenti nel trustStore di
JBoss, perché in questo caso l'identità
del client verrà validata.
Queste
impostazioni sono collocate nel file jsse_client_props_mutual.
Per eseguire un client in tale modalità,
potrete sfruttare il task ant incluso negli esempi
denominato 'run_iiop_ssl_jacorb_mutual'.
Per
quanto riguarda invece il trustStore da impostare
sul client attraverso la property javax.net.ssl.trustStore,
questo può essere lasciato invariato rispetto
al caso precedente, in quanto il client ha ancora
necessità di conoscere il certificato con
la chiave pubblica del server.
Figura 6: Mutua Autenticazione: Comunicazione
sicura tra client e server
Conclusioni
In questo articolo si sono presentati degli esempi
al fine di esemplificare quanto introdotto in
[MOKA_SSLIOP_1].
Per comodità del lettore abbiamo incluso
nella directory degli esempi anche i file di configurazione
per JBoss impostati come descritto nell'articolo.
Allegati
Scarica gli esempi
allegati all'articolo
Bibliografia
[MOKA_SSLIOP_1]
S.Rossini, A D'Angeli: IIOP over SSL (I): La teoria,
MokabyteN.91-
Dicembre 2004
[RFC_X509] RFC 2459 - Internet X.509 Public Key
Infrastructure Certificate and CRL Profile
[MITMA] http://en.wikipedia.org/wiki/Man_in_the_middle
[TSSLC] http://www.apachefrance.com/Manuels/Tomcat_3.0/tomcat-ssl-howto.html
[JCO_BG] http://www.jacorb.org/cgibin/bugzilla/show_bug.cgi?id=464
[JBOSS] http://www.jboss.org
[JACORB] http:// www.jacorb.org
[CORBA3.02_SPEC] http://www.omg.org/technology/documents/corba_spec_catalog.htm
|