Un modo per farlo � importare il certificato ed inserirlo nel repository del
browser per dirgli che quel certificato � "cosa nostra".
Per tornare all'autenticazione,questa viene fatta adottando i principi
della crittografia asimmetrica, che permette oltre alla
confidenzialità delle informazioni, anche il controllo dell'identità.
Per inciso questo meccanismo è alla base della firma digitale.
In pochissime parole, tanto per dare il senso dell'andamento delle
cose: la crittografia asimmetrica prevede che la cifratura e
decifratura vengano effettuate tramite due chiavi: con una chiave si
cifra (non importra quale delle due), con l'altra si decifra. Non è
possibile decifrare con la stessa chiave con cui si è cifrato, e non è
possibile ricostruire una chiave a partire dall'altra. Quando si vuole
adottare un sistema del genere, tutti i partecipanti hanno a
disposizione una coppia di chiavi, di cui una resta privata e nota al
solo proprietario, e l'altra è pubblica disponibile a tutti (in genere
disponibile su appositi server pubblici). In questo modo:
1. se io voglio spedirti un messaggio cifrato che solo tu possa
leggere, recupero la tua chiave pubblica e cifro il messaggio (non è
esattamente così, perché computazionalmente sarebbe troppo oneroso, ma
per semplificare diciamo che è esattamente così, la sostanza non
cambia poi molto), che a questo punto solo tu sarai in grado di
decifrare;
2. se io voglio spedirti un messaggio a darti la garanzia che sono
proprio io l'autore, cifro il mio messaggio con la mia chiave privata
e te lo spedisco. Tu sarai in grado di decifrarlo solo usando la mia
chiave pubblica, e questo ti darà la garanzia matematica (nel vero
senso della parola) che io sono l'autore, perché nessuno al di fuori
di me potrebbe aver cifrato il messaggio che tu hai potuto decifrare
con lamia chiave pubblica (a meno che qualcuno non mi abbia rubato la
chiave privata).
A ciascuna coppia di chiavi si usa associare una struttura dati
chiamata "certificato". Si tratta di un oggetto che contiene la chiave
pubblica,e alcune informazioni sull'identià del proprietario, e infine
una sezione detta "firma". Si tratta di una firma digitale,ovvero di
una cifratura fatta con una chiave privata delle struttura suddetta.
Se lafirmaè fatta con la chiave privata associata alla pubblica
contenuta nel certificato, parliamo di certificato "self-signed", Se
invece la firma è fatta da una terza parte, allora parliamo di
certificato firmata da una terza parte. Questa terza parte si chiama
Certification Authority, citata da Legione. In pratica una
Certification Authority è un'entità (costituita da un insieme di
hardware e software) che emette e firma, in maniera affidabile,
coppie di chiavi per utenti finali.
Un certificato firmato da una Certification Authority forma una catena
di certificazione: la coppia di chiavi risultata certificata da un
certificato, a sua volta certificato da una certification authority.
Se vai su un sito in https e dai uno sguardo al certificato che ti
viene proposto, troverai la catena di cui ti dicevo: vedrai un
certificato di Certification Authority (o anche più di uno: a volte le
Certification Authority sono certificate da altre Certification
Authority, e così via) su una catena,in cui l'elemento foglia è il
certificato del server.
Nell'autenticazione semplice, quando è il solo client che si assicura
del'identità del server, il controllo di affidabilità che il browser
fa quandosi connette ad un sito sicuro è proprio questo: verifica che
la firma sul certificato server sia stata apposta da una Certification
Authority sicura. La verifica della firma la fa molto semplicemente
recuperando la chiave pubblica del certificato di CA (Certification
Authority) e provando a "decifrare" la firma sul certificato server.
Tutti i browser in circolazione hanno un proprio database di
certificati di Certification Authority, necessari per questi compiti
(tali certificati possono anche essere scaricati da server pubblici,
in ogni caso, e installati nel proprio browser). Periodicamente questi
database vengono aggiornati tramite gli aggiornamentiai browser o al
sistema operativo (vedi il caso di Microsoft, che tra gli
aggiornamenti periodici propone anche quello dei "Certificati
Principali".
Quando lavori in Java, su un applicazione web, in cui non devi
preoccuparti della realizzazione del client, inteso com http user-
agent, e non devi fare particolari controlli sui vari certificati (ce
ne sono altri oltre quelli che ti ho detto che hanno a che fare con la
validità del certificato, basata su speciali strutture di controllo
delle Certificate Revocation Lists - CRL, ma anche altri controlli
aggiuntivi basati sull'identità stessa del server o del client), non
hai bisogno di scrivere codice, ti basta semplicemente configurare il
tuo servlet container o applicaiton server, o anche il solo server
http che ti fa da frontend verso il servlet container/applciaiton
server. La web application in linea di principio può essere del tutto
inconsapevole di trovarsi in esecuzione in un ambiente protetto omeno.
Ciao
Daniele
Penso che tu intenda "il protocollo adottato in entrambi i casi va sotto
il nome di...", o sbaglio?
In ogni caso, a me interessa (se non ho capito male) il primo caso:
applicazione server che deve scambiare dati con un'applet presente in
una pagina web (quindi, accessibile a tutti). Non ho nessun bisogno di
autenticare il client; tuttavia client e server devono scambiarsi dei
dati in modo protetto (non intercettabile), per cui ho pensato subito a
qualcosa come (sul server)
SSLServerSocketFactory
sslserversocketfactory=(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
SSLServerSocket
SSLserver=(SSLServerSocket)sslserversocketfactory.createServerSocket(/*int*/numeroPorta);
do {
SSLSocket client=(SSLSocket)SSLserver.accept();
//...
e analogamente sul client:
SSLSocketFactory
sslsocketfactory=(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket
client=(SSLSocket)sslsocketfactory.createSocket(/*String*/IP,/*int*/numeroPorta);
//...
Il problema nasce dal fatto che (almeno con certificati "self-signed"
con cui ho fatto i test) devo utilizzare una unica password per creare i
due certificati per il client e per il server:
keytool -genkey -alias marco -keystore serverkeystore.jks -keyalg RSA
-storepass "unicapassword" -keypass "unicapassword"
#creato certificato (serverkeystore.jks)
keytool -export -keystore serverkeystore.jks -alias marco -rfc -file
certificato.cer -storepass "unicapassword"
#esportato certificato (certificato.cer)
keytool -import -alias marco -file certificato.cer -keystore
clienttruststore.jks -storepass "unicapassword"
#importato certificato (clienttruststore.jsk)
Io pensavo che storepass e keypass fossero la chiave privata e quella
pubblica, ma se sono diverse i certificati vengono creati, ma la
connessione fallisce regolarmente (e, in effetti, tutti gli esempi che
ho trovato in rete riportano la stessa password per storepass e keypass).
Risultato: se il client non conosce "unicapassword", l'applet non
funziona: Quindi sono costretto ad inserirla nell'applet, il che
significa che, in teoria, chiunque potrebbe facilmente, decompilando il
codice, leggerla in chiaro.
Che tipo di sicurezza ottengo rendendo pubblica "unicapassword"? dovendo
esserci una chiave pubblica ed una privata, non capisco quale sia quella
privata. Forse viene generata automaticamente nel file
serverkeystore.jks, senza che io debba digitarla? Oppure, in modo
casuale, ad ogni tentativo di connessione? Oppure "unicapassword" �
proprio la chiave privata, e quindi rendendola pubblica viene inficiata
la sicurezza? Ed in quest'ultimo caso, quale sarebbe la strada corretta
da seguire?
Ciao
Marco
Corretto.
>
> In ogni caso, a me interessa (se non ho capito male) il primo caso:
> applicazione server che deve scambiare dati con un'applet presente in
> una pagina web (quindi, accessibile a tutti). Non ho nessun bisogno di
> autenticare il client; tuttavia client e server devono scambiarsi dei
> dati in modo protetto (non intercettabile), ...
OK, devi dunque impostare una connessione SSL tra applet e server, che
rientra dunque nel caso generale di scambio di dati su canale cifrato
tra un client e un server mediante protocollo SSL.
In teoria client e server possono utilizzare ognuno un proprio
certificato, self-signed o meno (se self-signed, si può dire che
l'entità stessa funge da Certification Authority di sé stessa).
Questi certificati (e relative chiavi private) risiedono nei keystore:
uno per il client, uno per il server. I certificati di CA utilizzati
per la validazione dei certificati della controparte risiedono nei
truststore. La differenza tra i due sta semplicemente nel fatto che i
primi contengono anche chiavi private, i secondi solo certificati
pubblici, da usare per validazione.
Keystore e truststore sono protetti da una password di accesso, la
storepass. Le chiavi private presenti in un keystore (possono
essercene più di una. O meglio: possono esserci più di una coppia di
chiavi; ogni coppia è identificata da un alias (come del resto ogni
oggetto in un keystore/truststore) e a sua volta può essere protette
da un'ulteriore password, la keypass. Se nessuna keypass è stata
definita in fase di creazione, la coppia di chiavi è cifrata con la
stessa storepass. Quando la JVM accede al contenuto di un keystore per
reperire una particolare coppia di chiavi, identificata da un
determinato alias, utilizza la storepass per "aprire" il keystore e
quindi recupera la coppia di chiavi desiderata e, se è stata fornita
una keypass, prova ad "aprirla"/decifrarla con questa, altrimenti
utilizza la storepass. Storepass e keypass sono dunque concetti
differenti da chiave pubblica e privata.
Considera che quando si configura un servlet container o un
application server per supportare SSL, occorre preparare un keystore
in cui la coppia di chiavi da utilizzare sia protetta dalla storepass
utilizzata per proteggere anche il keystore stesso: in altre parole,
quando si genera il keystore non si deve specificare la keypass, ma
solo la storepass.
Keystore e truststore sono utilizzati dalla JVM in cui è in esecuzione
l'applicazione di interesse: nel tuo caso la JVM che esegue l'applet,
che deve avere il suo keystore e il suo truststore, e la JVM in cui è
in esecuzione il server, che avrà il suo keystore per rendere
disponibile una connessione SSL.
Per default la JVM utilizza il trustore jre/lib/security/cacerts (con
password di default "changeit"), ma è possibile farla puntare ad un
trustore differente, passando le opzioni -
Djavax.net.ssl.trustStore=mySrvKeystore e -
Djavax.net.ssl.trustStorePassword=<password> alla JVM.
Nel tuo caso ciò di cui hai bisogno è aprire una connessione SSL con
il server, accedendo quindi ad una servlet su una url https. Non ti
consiglio invece di creare un server ad hoc che apra una connessione
su socket ssl.
Una guida utile è questo articolo di mokabyte:
http://www.mokabyte.it/2005/10/tunnel.htm
La sezione "Generazione di una richiesta HTTPS" ti mostra come aprire
una connessione https da un client generico. Il codice associato
all'articolo, all'url http://www.mokabyte.it/2005/10/tunnel_esempi.zip,
ti mostra invece un esempio di un applet che apre una connessione
verso una servlet. Si tratta di una connessione in chiaro, ma che
effettua una basic authentication. Puoi mettere insieme le due cose
per ottenere una connessione https verso la servlet (con o senza basic
authentication).
L'esempio di mokabyte prevede una mutua autenticazione, per cui
l'applet deve anche aprire il proprio keystore per recuperare la
coppia di chiavi, e certificato annesso, per farsi autenticare dal
server.
Nel tuo caso le cose si semplificano:
http://www.jguru.com/faq/view.jsp?EID=24672
Lato server invece non dovresti sviluppare nulla per la creazione di
una connessione SSL, ma solo configurare il servlet container/
application server per supportare https. A questo indirizzo trovi come
farlo per tomcat: http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html
(qualcosa anche a questi indirizzi: http://www.igate.it/solution?id=197
e http://www.digicert.com/ssl-certificate-installation-tomcat.htm).
> Il problema nasce dal fatto che (almeno con certificati "self-signed"
> con cui ho fatto i test) devo utilizzare una unica password per creare i
> due certificati per il client e per il server:
>
> keytool -genkey -alias marco -keystore serverkeystore.jks -keyalg RSA
> -storepass "unicapassword" -keypass "unicapassword"
> #creato certificato (serverkeystore.jks)
> keytool -export -keystore serverkeystore.jks -alias marco -rfc -file
> certificato.cer -storepass "unicapassword"
> #esportato certificato (certificato.cer)
> keytool -import -alias marco -file certificato.cer -keystore
> clienttruststore.jks -storepass "unicapassword"
> #importato certificato (clienttruststore.jsk)
>
> Io pensavo che storepass e keypass fossero la chiave privata e quella
> pubblica, ma se sono diverse i certificati vengono creati, ma la
> connessione fallisce regolarmente (e, in effetti, tutti gli esempi che
> ho trovato in rete riportano la stessa password per storepass e keypass).
Come detto più sopra, la coppia di chiavi è una cosa (si tratta
essenzialmente di due numeri molto grandi generati tramite due numeri
primi molto grandi: questa loro caratteristica rende
computazionalmente difficile adottare un attacco a forza bruta), le
password di protezione del keystore e delle chiavi private in esso
contenute sono un altra.
> Risultato: se il client non conosce "unicapassword", l'applet non
> funziona: Quindi sono costretto ad inserirla nell'applet, il che
> significa che, in teoria, chiunque potrebbe facilmente, decompilando il
> codice, leggerla in chiaro.
Qui c'è in realtà un problema di distribuzione dell'applicazione:
sulle macchine client, in cui dovrebbe andare in esecuzione l'applet,
occorre installare un truststore contenente il certificato self-signed
del server, per poterne validare l'identità. A meno che il certificato
server non sia stato emesso da una CA presente tra quelle disponibili
in cacerts (e qui dovresti pagare :-): in questo caso non hai bisogno
di nient'altro. Ad essere precisi, una cosa occorrerebbe farla:
modificare la password di default del truststore, altrimenti un
attaccante che avesse accesso alla macchina client potrebbe inserire
in cacerts un certificato di CA fasullo, o un certificato self-signed,
e ogni qual volta l'utente si connette ai server dell'attaccante, la
JVM non protesta ;-)
Ai link seguenti trovi svariati esempi di codice per architetture java
client-server che si scambiano dati con protocollo SSL:
http://stilius.net/java/java_ssl.php
http://www.exampledepot.com/egs/javax.net.ssl/GetCert.html?l=rel
http://www.jguru.com/faq/view.jsp?EID=32388
http://www.herongyang.com/JDK/SSL-Socket-Client-Example-SslSocketClient.html
Ma, ripeto, si tratta di esempi generali client-server, da cui puoi
prendere un po' di informazioni aggiuntive sul mondo ssl in java.
Ciao
Daniele
Bene, chiarissimo. Questo, secondo me, � il punto centrale.
> Per default la JVM utilizza il trustore jre/lib/security/cacerts (con
> password di default "changeit"), ma � possibile farla puntare ad un
> trustore differente, passando le opzioni -
> Djavax.net.ssl.trustStore=mySrvKeystore e -
> Djavax.net.ssl.trustStorePassword=<password> alla JVM.
Infatti, � quello che faccio io.
> Nel tuo caso ci� di cui hai bisogno � aprire una connessione SSL con
> il server, accedendo quindi ad una servlet su una url https. Non ti
> consiglio invece di creare un server ad hoc che apra una connessione
> su socket ssl.
Uso SSLSocket, funziona perfettamente, e non vorrei complicarmi la vita
con https, Selvlet, Tomcat e affini (che non credo siano adatti al mio
caso).
>> Risultato: se il client non conosce "unicapassword", l'applet non
>> funziona: Quindi sono costretto ad inserirla nell'applet, il che
>> significa che, in teoria, chiunque potrebbe facilmente, decompilando il
>> codice, leggerla in chiaro.
>
> [CUT] una cosa occorrerebbe farla:
> modificare la password di default del truststore, altrimenti un
> attaccante che avesse accesso alla macchina client potrebbe inserire
> in cacerts un certificato di CA fasullo, o un certificato self-signed,
> e ogni qual volta l'utente si connette ai server dell'attaccante, la
> JVM non protesta ;-)
Questo � ESATTAMENTE il problema che mi trovo a dover risolvere, come
spiego pi� sotto.
> http://stilius.net/java/java_ssl.php
Partiamo da qui, visto che uso proprio questa strada. Per eseguire il
file EchoServer.java (che allego in calce), devo creare il certificato
per il server, e poi quello per il client. Questo avviene con l'istruzione:
STOREPASS="123456"
KEYPASS=$STOREPASS
keytool -genkey -alias marco -keystore serverkeystore.jks -dname
"CN=prova" -keyalg RSA -storepass "$STOREPASS" -keypass "$KEYPASS"
Ora, la password immessa (123456), DEVE essere nota al client (che deve
poter eseguire ava -Djavax.net.ssl.trustStore=clienttruststore.jks
-Djavax.net.ssl.trustStorePassword=$STOREPASS EchoClient). Quindi,
l'applet presente in una pagina web accessibile a tutti deve contentere
la password, che quindi pu� facilmente essere letta da eventuali
attaccanti che, se avessero accesso ad una macchina client, potrebbero
fare cose poco simpatiche.
Ma, se creo i certificati con
STOREPASS="123456"
KEYPASS="abcdef"
keytool -genkey -alias marco -keystore serverkeystore.jks -dname
"CN=prova" -keyalg RSA -storepass "$STOREPASS" -keypass "$KEYPASS"
i certificati vengono creati regolarmente, ma eseguendo
java -Djavax.net.ssl.keyStore=serverkeystore.jks
-Djavax.net.ssl.keyStorePassword=$STOREPASS EchoServer
ottengo l'eccezione
java.net.SocketException: java.security.NoSuchAlgorithmException: Error
constructing implementation (algorithm: Default, provider: SunJSSE,
class: sun.security.ssl.DefaultSSLContextImpl)
at
javax.net.ssl.DefaultSSLServerSocketFactory.throwException(SSLServerSocketFactory.java:159)
at
javax.net.ssl.DefaultSSLServerSocketFactory.createServerSocket(SSLServerSocketFactory.java:171)
at EchoServer.main(EchoServer.java:15)
Caused by: java.security.NoSuchAlgorithmException: Error constructing
implementation (algorithm: Default, provider: SunJSSE, class:
sun.security.ssl.DefaultSSLContextImpl)
at java.security.Provider$Service.newInstance(Provider.java:1262)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
at javax.net.ssl.SSLContext.getInstance(SSLContext.java:142)
at javax.net.ssl.SSLContext.getDefault(SSLContext.java:85)
at
javax.net.ssl.SSLServerSocketFactory.getDefault(SSLServerSocketFactory.java:113)
at EchoServer.main(EchoServer.java:14)
Caused by: java.io.IOException: Keystore was tampered with, or password
was incorrect
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:788)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:55)
at java.security.KeyStore.load(KeyStore.java:1201)
at
sun.security.ssl.DefaultSSLContextImpl.getDefaultKeyManager(DefaultSSLContextImpl.java:167)
at
sun.security.ssl.DefaultSSLContextImpl.<init>(DefaultSSLContextImpl.java:57)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at
sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:532)
at java.lang.Class.newInstance0(Class.java:372)
at java.lang.Class.newInstance(Class.java:325)
at java.security.Provider$Service.newInstance(Provider.java:1238)
... 6 more
Caused by: java.security.UnrecoverableKeyException: Password
verification failed
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:786)
... 17 more
Quindi, il mio problema � il seguente: tu dici che dovrei cambiare la
password di default del truststore, ma io riesco solo a cambiare
storepass e keypass, purch� siano uguali, e non posso fare a meno di
rendere pubblica tale password. Come si fa a rendere privata una delle
due, senza pregiudicare la funzionalit� dell'applicazione, ma impedendo
ad eventuali malintenzionati di creare certificati a mio nome?
Ti ringrazio tantissimo della risposta, che mi ha chiarito alcune cose
che non avevo capito, e spero che tu sappia illuminarmi su questa, che
per me � la questione centrale.
Ciao
Marco
--------------------------------------------------------
Allego testo del file EchoServer.java:
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class EchoServer
{
public static void main(String[]args)
{
try
{
SSLServerSocketFactory
sslserversocketfactory=(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
SSLServerSocket sslserversocket=(SSLServerSocket)
sslserversocketfactory.createServerSocket(9999);
SSLSocket sslsocket=(SSLSocket)sslserversocket.accept();
InputStream inputstream=sslsocket.getInputStream();
InputStreamReader inputstreamreader=new
InputStreamReader(inputstream);
BufferedReader bufferedreader=new BufferedReader(inputstreamreader);
String string=null;
while ((string=bufferedreader.readLine())!=null)
{
System.out.println(string);
System.out.flush();
}
}
catch (Exception exception)
{exception.printStackTrace();}
}
}
Ovviamente la password non deve essere presente nel codice
dell'applet: come giustamente osservi tu, basta decompilare l'applet
(è vero che è possibile anche offuscare il codice (http://
proguard.sourceforge.net/), ma in ogni caso non è una buona idea).
Questo è il "problema di distribuzione" di cui ti parlavo: occorre
modificare sul client il comando di esecuzione della JVM, passandogli
i due parametri di cui sopra. Questo lo si fa accedendo al pannello di
controllo della JVM (client), tab Java, pulsante "Visualizza", che ti
fa accedere alla finestra di dialogo "Impostazioni Java Runtime
Environment": lì puoi specificare i parametri in questione. In
ambiente microsoft accedi alla JVM console dal pannello di controllo
di Windows. In ambiente Linux/Solaris, nella cartella bin dovresti
avere il file ControlPanel, che ti avvia il pannello di controllo di
Java per impostare quello di cui hai bisogno.
Naturalmente si tratta di una soluzione non accettabile se intendi
distribuire la tua applet a chiunque, ma valida se operi in un
ambiente ristretto, con un ben definito insieme di macchine client
sulle quali hai il controllo, e quindi la possibilità di modificare le
impostazioni della JVM.
>
> Ma, se creo i certificati con
>
> STOREPASS="123456"
> KEYPASS="abcdef"
> keytool -genkey -alias marco -keystore serverkeystore.jks -dname
> "CN=prova" -keyalg RSA -storepass "$STOREPASS" -keypass "$KEYPASS"
>
> i certificati vengono creati regolarmente, ma eseguendo
>
> java -Djavax.net.ssl.keyStore=serverkeystore.jks
> -Djavax.net.ssl.keyStorePassword=$STOREPASS EchoServer
>
> ottengo l'eccezione
>
> java.net.SocketException: java.security.NoSuchAlgorithmException: Error
> constructing implementation (algorithm: Default, provider: SunJSSE,
> class: sun.security.ssl.DefaultSSLContextImpl)
> at
[CUT]
Questo è un altro problema, segnalato da quel
"NoSuchAlgorithmException". L'implementazione di default della SUN
della parte crittografica non contiene tutto. SUN offre delle
interfacce, che poi devono essere implementate, un po' come accade per
JDBC. Si parla in questo caso di "Provider Crittografici", che offrono
l'implementazione richiesta. In circolazione c'è l'ottimo Bouncy
Castle (http://www.bouncycastle.org/), progetto Open Source che offre
l'implementazione di cui tu hai bisogno (e anche molto altro). Per
utilizzarlo, la via più breve è impostarlo come Provider crittografico
di default: in testa al tuo codice (sia client che server) devi avere
l'istruzione
Security.addProvider(new BouncyCastleProvider());
Questo risolve il tuo NoSuchAlgorithmException.
Come puoi immaginare, anche questa cosa pone un problema di
distribuzione: nella JVM client occorre avere nel classpath anche
Bouncy Castle (la via rapida è metterlo nella directory jre/lib/ext,
dove trovi anche l'implementazione di default del crypto provider
fatta da SUN. Un altra possibilità è impacchettarlo con l'applet, ma
questo fa aumentare notevolmente le dimensioni dell'applet stessa: il
file bcprov-jdkxxx.jar, che contiene l'implementazione richiesta,
"pesa" circa 1,5 MB .Nella mia esperienza lavorativa ho sviluppato
applet che effettuavano operazioni crittografiche (interagivano con
smartcard per firmare digitalmente informazioni che venivano poi
spedite al server), e mi rendo conto che si tratta di una bella rogna:
nel mio caso il provider crittografico che utilizzavo (era IAIK, a
pagamento) forniva una libreria alleggerita per applet, che conteneva
lo stretto necessario, proprio per evitare di dover intervenire sulle
macchine client, copiando nella JVM il provider crittografico.
L'applet conteneva ance il provider crittografico, che veniva
scaricato ogni volta sulla macchina client assieme all'applet. In ogni
caso le dimensioni erano comunque ragguardevoli, ma non c'era altra
possibilità, l'applet veniva distribuita ad un elevato numero di
utenti, sulle cui macchine non avevamo alcun controllo).
Nel pacchetto scaricato da Bouncy Castle trovi nella cartella dei
sorgenti anche numerosissimi esempi di utilizzo del provider
crittografico negli ambiti più svariati.
Spero che questo possa aiutarti a venirne fuori.
Ciao
Daniele
Appunto questo � il mio problema. Come posso procedere se l'applet deve
essere distribuita a tutti (quindi, pubblicamente accessibile tramite
web), se voglio usare ssl?
>> Ma, se creo i certificati con
>>
>> STOREPASS="123456"
>> KEYPASS="abcdef"
>> keytool -genkey -alias marco -keystore serverkeystore.jks -dname
>> "CN=prova" -keyalg RSA -storepass "$STOREPASS" -keypass "$KEYPASS"
>>
>> i certificati vengono creati regolarmente, ma eseguendo
>>
>> java -Djavax.net.ssl.keyStore=serverkeystore.jks
>> -Djavax.net.ssl.keyStorePassword=$STOREPASS EchoServer
>>
>> ottengo l'eccezione
>>
>> java.net.SocketException: java.security.NoSuchAlgorithmException: Error
>> constructing implementation (algorithm: Default, provider: SunJSSE,
>> class: sun.security.ssl.DefaultSSLContextImpl)
>> at
> [CUT]
>
> Questo � un altro problema [CUT]. In circolazione c'� l'ottimo Bouncy
> Castle (http://www.bouncycastle.org/), progetto Open Source che offre
> l'implementazione di cui tu hai bisogno [CUT]. Come puoi immaginare, anche questa cosa pone un problema di
> distribuzione: nella JVM client occorre avere nel classpath anche
> Bouncy Castle.
Eh, no, mica posso chiedere a tutti gli utenti di una pagina web di un
sito di installare altro, se no non funziona l'applet (anche perch� non
tutti sarebbero in grado).
> Spero che questo possa aiutarti a venirne fuori.
A venire fuori, per il momento, direi proprio di no. Mi fa solo capire
che la strada che seguivo, come sospettavo, � sbagliata. Io dovrei
scambiare questi benedetti dati (almeno nome utente e password) tramite
connessione criptata, senza che l'utente debba installare nulla sul PC
(oltre a java), senza pregiudicare la sicurezza, e senza dover usare il
protocollo http. Non devo creare pagine web, e ho gi� definito e
sviluppato il protocollo di comunicazione (cosa inviare, cosa
rispondere, e in che ordine). Mi rimane quest'ultimo piccolo (si fa per
dire) dettaglio che manda a carte 48 tutto il resto... la sicurezza!!!
Ma scusa, quando accedo ad una pagina https, non credo che il browser mi
comunichi la password con cui il certificato � stato creato, eppure la
connessione criptata si fa. Possibile che non ci sia proprio modo, in
java, di ottenere una connessione criptata senza dover comunicare al
client la password con cui il certificato � stato creato????
Grazie delle tue risposte, che mi hanno aiutato a chiarire i miei dubbi
(anche se ora non so proprio che pesci pigliare).
Marco
> Eh, no, mica posso chiedere a tutti gli utenti di una pagina web di un
> sito di installare altro, se no non funziona l'applet (anche perché non
> tutti sarebbero in grado).
Puoi allora semplicemente integrare nel jar contenente la tua applet
anche il provider crittografico. Ma attenzione: esistono versioni
diverse del provider crittografico, a seconda della JVM utilizzata
(parlo sempre di Bouncy Castle): questo perché l'implementazione JCE
della JVM è cambiata al cambiare delle versioni (parlo ovviamente di
major release: 1.3, 1.4(.2), 1.5, 6). Non ho mai provato ad usare una
JVM con un provider crittografico di versione differente quindi non
saprei suggerirti la combinazione giusta: es.: provider crittografico
compatibile con la 1.3 e JVM 6. Si tratta di fare qualche test al
riguardo. Una volta stabilita questa cosa, puoi procedere a
impacchettare l'applet con tutto il provider crittografico.
In ogni caso, quando si parla di applet, gli utenti finali sono
comunque costretti ad installare almeno la JVM (che non è installata
per default). Ma capisco che vujoi ridurre al minimo le cose da
installare, e che comunque le modifiche di cui ti ho parlato
presuppongono che l'utente finale abbia qualche conoscenza in più
dell'utente medio.
Però ti resta sempre il problema del certificato server: a meno che
non sia emesso da una CA presente tra quelle riconosciute dalla JVM
(file jre/lib/security/cacerts), ti tocca installarlo in tutti i
repository client. E da quello che mi dici, questa non è una soluzione
per te accettabile.
Per inciso, il problema si manifesterebbe anche se tu decidessi di
parlare, via https, con una servlet, utilizzando il protocollo http
ordinario, perché in ogni caso, durante l'handshake, la JVM client
deve verificare l'identità del server.
> [CUT]Io dovrei
> scambiare questi benedetti dati (almeno nome utente e password) tramite
> connessione criptata, senza che l'utente debba installare nulla sul PC
> (oltre a java), senza pregiudicare la sicurezza, e senza dover usare il
> protocollo http. Non devo creare pagine web, e ho già definito e
> sviluppato il protocollo di comunicazione (cosa inviare, cosa
> rispondere, e in che ordine). Mi rimane quest'ultimo piccolo (si fa per
> dire) dettaglio che manda a carte 48 tutto il resto... la sicurezza!!!
>
> Ma scusa, quando accedo ad una pagina https, non credo che il browser mi
> comunichi la password con cui il certificato è stato creato, eppure la
> connessione criptata si fa. Possibile che non ci sia proprio modo, in
> java, di ottenere una connessione criptata senza dover comunicare al
> client la password con cui il certificato è stato creato????
Purtroppo, togliendo di mezzo lo user agent (il browser), ti tocca
fare in modo che la tua applet implementi quell'insieme minimo di
funzionalità che lo user agent offre quando stabilisce connessioni
sicure. Il browser ha il suo repository di certificati, e il suo
strato crittografico per stabilire connessioni SSL. Se hai bisogno di
fare la stessa cosa, purtroppo ti tocca sviluppare la parte mancante.
Aggiungere uno strato di sicurezza ad una connessione tra applet e un
server che si parlano via stream (immagino avrai un tuo protocollo di
oggetti serializzabili che rappresentano le informazioni scambiate)
purtroppo non è una cosa banale.
In ogni caso ho fatto qualche ricerca sul web, e ho trovato un thread
interessante sui forum di SUN:
http://forums.sun.com/thread.jspa?forumID=2&threadID=479169
Uno degli ultimi interventi, a proposito della distribuzione del
truststore di cui parlavamo più sopra, prevede che il truststore
stesso sia presente nel jar dell'applet, e da lì acceduto.
Considera in ogni caso che per rendere effettivamente sicura una
soluzione di questo tipo, l'url da cui scaricare l'applet deve essere
a sua volta protetta da SSL: questo vale in generale, sia che tu metta
o non metta il truststore nel jar. In attacca man-in-the-middle
potrebbe sostituire la tua applet con un'altra e allora tutti i
discorsi di sicurezza andrebbero ma farsi benedire.
Fortunatamente per questa seconda cosa non devi implementare nulla, ti
occorre solo mettere l'ssl sulla pagina da cui scarichi l'applet.
Ciao
Daniele
Un attimo, mi sorge un dubbio: vuoi dire che se usassi un certificato a
pagamento (tipo Verisign), SSLServer funzionerebbe senza dover
comunicare la password al client? Perch� io per la fase operativa
prevedo di usare certificati "seri", solo che ancora sono in fase di test.
Supponiamo che il certificato server sia certificato da Verisign o
affini, e che l'applicazione server e l'applet seguano una strada tipo
http://stilius.net/java/java_ssl.php .
Vuoi dire che in questo caso il client riuscirebbe a stabilire la
connessione SSL senza dover digitare
java -Djavax.net.ssl.trustStore=mySrvKeystore
-Djavax.net.ssl.trustStorePassword=123456 EchoClient
ma solo
java EchoClient
...senza, quindi, dover conoscere la password con cui il certificato �
stato creato perch� SSL funzioni?
Grazie mille di tutte le tue spiegazioni
Marco
On 6 Nov, 15:35, Marco <nos...@excite.it> wrote:
> Un attimo, mi sorge un dubbio: vuoi dire che se usassi un certificato a
> pagamento (tipo Verisign), SSLServer funzionerebbe senza dover
> comunicare la password al client? Perché io per la fase operativa
> prevedo di usare certificati "seri", solo che ancora sono in fase di test.
>
> Supponiamo che il certificato server sia certificato da Verisign o
> affini, e che l'applicazione server e l'applet seguano una strada tipohttp://stilius.net/java/java_ssl.php.
> Vuoi dire che in questo caso il client riuscirebbe a stabilire la
> connessione SSL senza dover digitare
>
> java -Djavax.net.ssl.trustStore=mySrvKeystore
> -Djavax.net.ssl.trustStorePassword=123456 EchoClient
>
> ma solo
>
> java EchoClient
>
> ...senza, quindi, dover conoscere la password con cui il certificato è
> stato creato perché SSL funzioni?
Esatto: e se vuoi fare delle prove adesso, è sufficiente che tu
inserisca il tuo certificato self-signed in cacaerts (la password di
default è 'changeit').
Qui interviene un altro discorso di sicurezza: la password di default
per cacerts è changeit, chiaro riferimento al fatto che tale password
debba essere cambiata, altrimenti chiunque abbia accesso alla macchina
client può alterare il contenuto di cacerts inserendovi un proprio
certificato self-signed.
Se però decidi di cambiarla, poi occorre al solito operare sulla JVM
client per dare come parametro di avvio delle JVM la nuova password.
Al solito, qui si tratta di fare compromessi: quanto è preziosa la
"merce" scambiata (intesa come "informazioni scambiate")?
Un attacco man-in-the-middle senza compromettere la macchina client
(senza dover alterare il contenuto di cacerts) è sicuramente più
semplice di un attacco man-in-the-middle che richiede invece
l'alterazione di cacerts: se non altro, nel secondo caso l'attaccante
deve trovare il modo di entrare nelle macchine client e fare le sue
modifiche.
La cosa importante è che l'applet stessa sia scaricata da una url
protetta da SSL, per il discorso che ti facevo in precedenza, che un
attaccante, su una url in chiaro, potrebbe sostituire l'applet
originale con una maligna.
Per cui, lasciando la password di default, in ogni caso hai che il
traffico viene cifrato, non passa in chiaro. Ricordati che il traffico
è cifrato con chiavi di sessione, generate al momento dell'handshake
ssl, e che tali chiavi sono generate a partire da informazione
cirfrata con chiave pubblica che client e server si scambiano. Un
attaccante che sappia che la password dei truststore cacerts sui
client è changeit, non se ne fa nulla, se poi non riesce ad accedere
alle macchine client per alterarne fisicamente il cacerts.
Mi è venuto in mente che esistono un paio di software open source che
ti permettono di lavorare agevolmente con una GUI con chiavi,
keystore, certificati & C: si tratta di Portecle (http://
portecle.sourceforge.net/, download all'url http://sourceforge.net/projects/portecle/)
e di KeytoolUI (http://yellowcat1.free.fr/index_ktl.html).
Ciao
Daniele