Smart Card Authentication Paviotti Marco June 17, 2008 Contents 1 Specifiche 1.1 Installazione di Tomcat e JVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 Configurazione 2.1 Server Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Client Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 3 3 Avvio 4 4 Applicazione d’esempio 4.1 OpenCMS . . . . . . . . . . . . . . 4.2 La Modifica . . . . . . . . . . . . . 4.3 Uso . . . . . . . . . . . . . . . . . 4.3.1 Disabilitare il login classico 4.4 Considerazioni sulla sicurezza . . . 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 5 6 7 7 Specifiche La Regione FVG, come altre regioni italiane, ha distribuito a tutti i cittadini del FVG una tessera contenente una chiave privata RSA, e il relativo certificato, abilitata all’autenticazione forte. Al momento si può impiegare tale tessera per autenticarsi solo presso il sito web della regione, ma nulla vieta di utilizzare tale tessera presso altri siti. Si vuole studiare questa possibilità; più precisamente, si vuole definire chiaramente come creare un sito web, o un servizio web, supportante l’autenticazione con tale smartcard. Tessera sanitaria 1.1 Installazione di Tomcat e JVM Per l’implementazione delle specifiche in Java sono richiesti due componenti fondamentali : 1. La Java Virtual Machine 1 2. Il web server Tomcat L’installazione della JVM è molto semplice, dopo averla scaricata dal sito web ufficiale di Sun (http://java.sun.com/javase/downloads/?intcmp=1281) si può procedere all’esecuzione del file binario che estrae i file necessari in una cartella jdk-X.Y.Z creata nella working directory su cui stiamo lavorando. L’installazione di Tomcat è altrettanto semplice, il file binario si può ottenere da http://tomcat.apache. org/download-60.cgi e segue la stessa procedura descritta sopra. Ambe due le installazioni non richiedono nessuna configurazione iniziale, per avviare il webserver serviranno tre semplici comandi.Assumiamo come JVM DIR la directory in cui è stata scompattata la Java virtual Machine, e TOMCAT DIR la rispettiva directory di Tomcat, (si farà riferimento a queste due Alias per il resto del documento): export JAVA_HOME=$JVM_DIR $TOMCAT_DIR/bin/startup.sh Installazione e configurazione potrebbero variare a seconda della versione della JVM o di Tomcat, per questo esperimento si è scelto di utilizzare JDK-1.6.0 Update 3, e Tomcat 6.0.16. 2 Configurazione A questo punto per realizzare una configurazione in cui server e client si autentichino a vicenda serve una piccola modifica al file di configurazione di tomcat, il file in questione è situato in $TOMCAT DIR/conf/server.xml. Esplorando tale file si nota che c’è una parte commentata relativa alla porta 8443, in primo luogo questa sezione và decommentata, in secondo luogo bisogna modificarla . Le modifiche da fare sono minime ma essenziali : innanzitutto dobbiamo dire a tomcat di autenticare opzionalmente anche il client (clientAuth = “want”) dopodichè dobbiamo specificare il file (per questo esempio abbiamo usato keystoreFile= $TOMCAT DIR/keystore) dove verranno memorizzati i certificati relativi alla connessione SSL e la password per aprire tale file (keystorePass= $PASSWORD), la sezione corrispondente al Connector per SSL diventa quindi : <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="want" sslProtocol="TLS" keystoreFile="$TOMCAT_DIR/keystore" keystorePass="$PASSWORD"> <Factory className="org.apache.catalina.net.SSLServerSocketFactory" clientAuth="true" protocol="TLS" keystorePass="PASSWORD"/> </Connector> N.B.: “Autenticare opzionalmente” significa che il server non si occuperà di controllare che il certficato sia presente per forza, nel caso che non sia presente sarà la web application a restituire un messaggio di errore 2.1 Server Authentication In questa sezione dobbiamo dotare tomcat di un certificato con cui il client possa autenticare la fonte a cui ci si stà connettendo, al momento della richiesta infatti il server manderà al browser il suo certificato e l’utente sceglierà se fidarsi o no.Il server potrebbe disporre di un certificato valido (firmato da autorità note), ma per il nostro scopo basta un certificato autofirmato. Il seguente comando crea un truststore con un certificato autofirmato: $JVM_DIR/bin/keytool -genkey -alias tomcat-keyalg RSA -keystore $TOMCAT_DIR/keystore 2 Si noti che il path per la memorizzazione è quello specificato anche in fase di configurazione del server nel file server.xml . Al momento della generazione del certificato verrà chiesta una password, la stessa password che và specificata nel file di configurazione di tomcat (server.xml). Di seguito è riportato un esempio pratico del comando : marco@marco:~/Desktop/apache-tomcat-6.0.16$ /home/marco/jdk1.6.0_03/bin/keytool -genkey -alias tomcat -keyalg RSA -keystore /home/marco/Desktop/apache-tomcat-6.0.16/keystore Enter keystore password: Re-enter new password: What is your first and last name? [Unknown]: Marco Paviotti What is the name of your organizational unit? [Unknown]: What is the name of your organization? [Unknown]: What is the name of your City or Locality? [Unknown]: Udine What is the name of your State or Province? [Unknown]: Italy What is the two-letter country code for this unit? [Unknown]: IT Is CN=Marco Paviotti, OU=Unknown, O=Unknown, L=Udine, ST=Italy, C=IT correct? [no]: yes Enter key password for <tomcat> (RETURN if same as keystore password): Re-enter new password: 2.2 Client Authentication A questo punto il server ha il suo certificato, sà che deve autenticare il client, e quindi tramite SSL richiede il certificato del client.Il browser connesso dall’altra parte deciderà (prova i certificati in sequenza o chiede un input dall’utente) che certificato inviare, dopodichè il server riceve il certificato e verifica la firma in esso contenuta. Il certificato dev’essere firmato da un’autorità fidata (Thawte, VerySign.. per esempio) altrimenti il web server non potrà validare la richiesta ed essere sicuro dell’ identità del client. Se il certificato è firmato da una autorità di cui tomcat non si fida (o che semplicemente non è presente nel database delle certification autorities) il server risponderà con un messaggio di errore: Could not enstablish an encrypted connection because your certificate was rejected by localhost. Error Code : -12271 Per questo tipo di esperimento ci serve quindi il certificato della Certification Authority che ha firmato i certificati delle Smart Card del Friuli. Una volta ottenuto bisogna aggiungerlo ai certificati della autorità fidate con il seguente comando. $JVM_DIR/bin/keytool -import -keystore $JVM_DIR/jre/lib/security/cacerts \ -storepass $PASSWORD -alias rootca -file ca_cittadini.cer -trustcacerts Dove ca cittadini.cer è il certificato della Root Authority. 3 3 Avvio Non resta che avviare il server con il comando indicato anche all’inizio di questa guida : export JAVA_HOME=$JVM_DIR $TOMCAT_DIR/bin/startup.sh Attenzione : l’output del comando startup.sh è sempre identico ed è simile a questo : Using Using Using Using CATALINA_BASE: /home/marco/Desktop/apache-tomcat-6.0.16 CATALINA_HOME: /home/marco/Desktop/apache-tomcat-6.0.16 CATALINA_TMPDIR: /home/marco/Desktop/apache-tomcat-6.0.16/temp JRE_HOME: /home/marco/jdk1.6.0_03/ ma per verificare se ci sono stati eventuali malfunzionamenti o errori di configurazione bisogna guardare il log di tomcat : tail -f $TOMCAT_DIR/logs/catalina.out Se tutto è andato a buon fine e il server è up, ci si può connettere, con un browser configurato per acquisire il certificato dalla smart card (per la configurazione del browser si segua la guida sul sito ufficiale della carta servizi http://cartaservizi.regione.fvg.it/CRSPortale/Index.jsp?STATO=FEComeAccedo.jsp), all’indirizzo https://localhost:8443/ (eventualmente con il nome dell’applicazione installata in fondo all’URL); il server risponder con il proprio certificato lasciando all’utente (client) la facoltà di decidere se accettarlo o meno nel caso che questo non sia in grado di verificare l’identità della certification authority. Dopodichè sarà la volta del client : il browser chiederà all’utente quale certificato usare e lo invierà al server. 4 Applicazione d’esempio A questo punto se le due parti sono state autenticate correttamente possiamo installare la nostra applicazione per autenticare gli utenti. Nel caso generale l’applicazione dovrà prendere il certificato del client e verificare che il soggetto identificato dal campo CN sia presente nel proprio database degli utenti. 4.1 OpenCMS Nel nostro caso abbiamo scelto di usare un CMS, opencms. Questo CMS prevede una schermata di login iniziale per dare accesso alla parte amministrativa del software e sarà qui che andremo a intervenire per farci autenticare via CNS. La form di login ha come action la pagina /opencms/opencms/system/login/index.html la quale sostanzialmente inizializza la classe CMSLogin e chiama il metodo di displayDialog() : <%@ page import=” o r g . opencms . w o r k p l a c e . ∗ ” %><% CmsLogin wp = new CmsLogin ( pageContext , r e q u e s t , r e s p o n s e ) ; %><%= wp . d i s p l a y D i a l o g ( ) %> displayDialog a seconda dei parametri passati stampa l’html di output. 1. login : se true indica alla web-page che si stà eseguendo il login, di conseguenza verifica i parametri ocUname e ocPword. 2. requestedResource : la pagina richiesta . 3. ocUname : username. 4. ocPword : password. La classe CMSLogin e contenuta nel package org.opencms.workplace 4 4.2 La Modifica Per raggiungere i nostri scopi aggiungiamo un altro parametro : certificate. In particolare vogliamo che, nel caso questo parametro sia true, la pagina salti il login convenzionali e guardi solo ai certificati. Il codice seguente verifica la variabile certificate caricata in precendenza nel costruttore, se questa a true 1. carica il certificato dalla Request, sostituisce l’username con il codice fiscale e forza il codice a effettuare il login impostando m actionLogin = true (la corrispondente variabile all’interno della pagina che contiene il valore login della form) 2. se il certificato non è stato caricato imposta le variabili m username e m password a null e il m actionLogin a true in modo che al controllo successivo venga ritornato un messaggio di errore specificato dalla variabile Messages.ERR NO CERT della classe org.opencms.workplace.Messages e impostato nel file messages.properties i f ( c e r t i f i c a t e != null && c e r t i f i c a t e . e q u a l s ( ” t r u e ” ) ) { X509Certificate certChain [ ] = ( X509Certificate [ ] ) getRequest ( ) . getAttribute ( ” javax . net . s s l . p e e r c e r t i f i c a t e s ” ) ; i f ( ( c e r t C h a i n == null ) | | ( c e r t C h a i n . l e n g t h == 0 ) ) { certChain = ( X509Certificate [ ] ) getRequest ( ) . getAttribute ( ” javax . s e r v l e t . request . X509Certificate ” ) ; } i f ( c e r t C h a i n != null && c e r t C h a i n . l e n g t h > 0 ) { c o d i c e F i s c a l e = c e r t C h a i n [ 0 ] . getSubjectDN ( ) . getName ( ) ; codiceFiscale = codiceFiscale . substring ( ”CN=%22” . l e n g t h () −2 , c o d i c e F i s c a l e . indexOf ( ” / ” ) ) ; m username = c o d i c e F i s c a l e ; m actionLogin = ” true ” ; m password = ” ” ; } else { m username = null ; m password = null ; m actionLogin = ” true ” ; } } Più avanti nel codice, a seguito dell’impostazione a true della varibile m actionLogin, il programma eseguirà il login utilizzando la funzione login(): l o g i n ( ( m oufqn == null ? C m s O r g a n i z a t i o n a l U n i t .SEPARATOR : m oufqn ) + m username , m password ) ; Questa riga andrà poi sostituita con il codice seguente in quanto noi vogliamo che la verifica, nel caso sia stata richiesta l’autenticazione tramite certificati, avvenga solo per l’username; per raggiungere questo obbiettivo usiamo la funzione login() utilizzando come parametri d’accesso l’username e la password entrambi uguali al codice fiscale, e nell’username aggiungiamo la stringa “–CNS–:” per far capire alla classe che andrà a verificare l’utente nel db che non ci servirà la verifica della password: i f ( c e r t i f i c a t e != null && c e r t i f i c a t e . e q u a l s ( ” t r u e ” ) ) { l o g i n ( ( m oufqn == null ? C m s O r g a n i z a t i o n a l U n i t .SEPARATOR : m oufqn ) + ”−−CNS−−:” + m username , m username ) ; 5 } else { i f ( m username . c o n t a i n s ( ”−−CNS−−:” ) ) { throw new IOException ( ” I m p o s s i b i l e a u t e n t i c a r e l ’ u t e n t e ” ) ; } l o g i n ( ( m oufqn == null ? C m s O r g a n i z a t i o n a l U n i t .SEPARATOR : m oufqn ) + m username , m password ) ; } A questo punto dobbiamo individuare il codice che verifica le credenziali di accesso nel db, la classe in questione è org.opencms.db.CMSDriverManager. La parte modificata è nel metodo loginUser, qui la modifica è molto semplice : se l’username contiente la stringa “–CNS–:” vuol dire che l’username proviene da un certificato, per cui non serve verificare anche la password, basterà verificare che l’utente esista nel db, quindi ci preoccuperemo di togliere semplicemente la stringa incriminata e di passare l’username alla funziona readUser. Nel caso questo non esista viene lanciata una eccezione CmsAuthentificationException, altrimenti esiste viene restituita la variabile newUser; Se l’username non contiene “–CNS–:” allora si eseguirà la procedura di login convenzionale. i f ( userName . c o n t a i n s ( ”−−CNS−−:” ) ) { try { newUser = r e a d U s e r ( dbc , userName . s u b s t r i n g ( 0 , userName . indexOf ( ”−−CNS−−:” ) ) + userName . s u b s t r i n g ( userName . indexOf ( ”−−CNS−−:” )+8 , userName . l e n g t h ( ) ) ) ; } catch ( CmsDataAccessException e2 ) { throw new C m s A u t h e n t i f i c a t i o n E x c e p t i o n ( o r g . opencms . s e c u r i t y . Messages . g e t ( ) . c o n t a i n e r ( o r g . opencms . s e c u r i t y . Messages . ERR LOGIN FAILED 2 , userName , remoteAddress ) , e2 ) ; } } else { newUser = m u s e r D r i v e r . r e a d U s e r ( dbc , userName , password , remoteAddress ) ; } 4.3 Uso Apriamo il seguente URL per verificare che tutto funzioni: https://localhost:8443/opencms/opencms/ system/login/index.html Verrà visualizzata una pagina come quella nella figura sottostante in cui si potrà utilizzare l’account per l’amministrazione al fine di aggiungere utenti associati al codice fiscale, oppure si potrà seguire il link sottostante alla form per eseguire l’autenticazione per mezzo della CNS. Login Se il codice fiscale contenuto nella CNS è presente anche nella lista degli utenti di opencms, allora la pagina seguente sarà di conferma dell’avvenuta autenticazione: 6 Autenticazione effettuata con successo 4.3.1 Disabilitare il login classico L’amministratore può impedire il login tradizionale per gli utenti che usano l’autenticazione con Smart Card, impostando per questi ultimi una password random (ad esempio di 50 caratteri ed oltre) senza comunicargliela. In questo modo tali utenti non possono usare il login con user/password, e quindi sono obbligati a passare al login via smartcard. Questa soluzione è efficace pur non essendo troppo elegante; l’implementazione di una funzionalità analoga nel codice di OpenCMS è possibile, ma comporta la modifica di molti moduli amministrativi del software. 4.4 Considerazioni sulla sicurezza Un utente potrebbe tentare un uso non corretto della stringa “–CNS–:” anteponendola ad un codice fiscale. Questo è impedito dalla seguente condizione : if(m_username.contains("--CNS--:")) Se quest’espressione è vera ed è stato effettuato il login normale (cioè senza smart card) si lancia un eccezione . Un attaccante potrebbe inoltre tentare di usare una stringa del genere, impostando certificate=true e selezionando il codice fiscale di un altro utente : https://localhost:8443//opencms/opencms/system/login/index.html ?certificate=true&login=trueo&cUname=$codiceFiscaleAltri in questo caso, siccome è stata richiesta l’autenticazione per mezzo di certificati SSL, anche se cUname viene inviato in ogni caso in query string (o POST data), quest’ultima sovrascrive il parametro con quello contenuto nel certificato. 7