Indice Introduzione Wrapper OpenSSL Installazione di EJBCA Conclusioni Appendice A – Procedure di installazione di EJBCA Appendice B – Codice C/Java Bibliografia Introduzione Sistemi complessi richiedono accorgimenti altrettanto complessi; uno di questi è quello della sicurezza del sistema stesso. E' quasi impossibile realizzare un sistema intrinsecamente sicuro nella sua totalità in particolar modo se lo vogliamo anche flessibile e facile da utilizzare, specie se abbiamo a che fare con sistemi informatici. Essi sono quanto di più complesso ha creato l'uomo ed è facile quindi intuire quanto siano delicati. Strutture complesse richiedono soluzioni complesse ed il tema di questa tesi è proprio una di queste soluzioni. Durante il periodo del tirocinio da me frequentato all'ASL (Azienda Sanitaria Locale) di Ancona sono stati posti due obiettivi: realizzare un wrapper in Java della libreria OpenSSL (scritta in C) mediante l'uso della JNI (Java Native Interface) ed installare e configurare un software che applica una certification authority (EJBCA – Enterprise Java Bean Certification Authority). E' ovvio quindi che abbiamo a che fare con un tipo di sicurezza molto specifico: quello contro attacchi di tipo informatico eseguito mediante la rete Internet e che è stata utilizzata la crittografia come strumento di difesa. Questa tesi può essere quindi divisa in due parti piuttosto indipendenti. Riguardo alle scelte effettuate (riguardo ai software) non posso fare a meno di far notare che le soluzioni scelte sono tutte open source (o perlomeno gratuite) inoltre, per lo sviluppo dei progetti è stato utilizzato un pc con installata una particolare versione di linux (Knoppix). Durante quaste attività ho riscontrato problemi al di là della mia portata che non mi hanno consentito di raggiungere entrambi gli obiettivi prefissati è di questo ne discuterò nel corso di questa tesi. Prima di proseguire vorrei fare alcuni ringraziamenti: primo fra tutti l'ingegniere Giuseppe Giampieri, il mio tutore aziendale, per tutti i preziosi consigli ed aiuti forniti nei momenti di necessità, sono molto grato anche al professore Aldo Dragoni per la continua disponibilità e chiarezza nelle sue utili spiegazioni inoltre non voglio escludere i miei amici e la mia famiglia per il loro appoggio morale senza il quale non sarei mai giunto a questo punto. Vorrei inoltre ringraziare coloro che mi hanno aiutato tramite email in particolar modo Thomas Gustavsson uno degli sviluppatori di EJBCA. Wrapper OpenSSL Come già accennato in precedenza uno degli obiettivi posti nel tirocinio è stato quello di dover realizzare un wrapper (una libreria che funge da “involucro”) utilizzando il linguaggio Java della libreria OpenSSL. Prima di motivare questa scelta e le conclusioni alle quali sono arrivato vorrei accennare alcuni concetti base che potrebbero non essere noti a tutti. Concetti base Cos'è una CA e una PKI Il concetto di PKI (Public Key Infrastructure) e CA (Certification Authority) è pressoche impossibile da descrivere in poche parole, per questo motivo verrà fornito solo un accenno. Una certification authority (autorità di certificazione) è un'entità che permette la realizzazione di infrastrutture a chiave pubblica (PKI – Public Key Infrastructure) la quale, mediante l'utilizzo della crittografia simmetrica ed asimmetrica e di opportuni protocolli, consente di far transitare dati in una rete in modo tale da garantire l'identificazione di colui con il quale si scambia dati e garantire l'integrità e la confidenzialità dei dati stessi. Le CA hanno varie funzioni da svolgere come quella di distribuire certificati o di verificarne l'autenticità. Nella PKI ci sono anche altre entità che entrano in gioco; l'illustrazione seguente è stata tratta dal RFC (Request For Comment) 2510 [pagina 6]: Questo schema riassume in maniera molto chiara la PKI. Una CA in realtà è composta da una certification authority ed una registration authority (RA) ma questa distinzione è andata persa con il tempo e con CA si indica normalmente sia la CA che la RA come fossero un tutt'uno. L'infrastruttura si basa sull'utilizzo dei certificati che possono essere definiti come sequenze di bit che seguono un determinato protocollo (o una determinata struttura se si preferisce). La ”End Entity” rappresenta l'utente che chiede servizi offerto dalla CA (registrazione iniziale, aggiornamento certificati, ecc..). La CA ha la necessità di essere a sua volta certificata da un'altra CA (nel diagramma viene chiamata CA-2). Una trattazione esauriente della PKI e del ruolo delle CA non è tema di questa tesi e questi concetti base verranno dati per acquisiti. Per una trattazione completa consultare l'RFC 2510. La libreria OpenSSL La OpenSSL (Open Secure Socket Layer) è una libreria scritta in C che svolge molteplici funzioni: la principale è sicuramente quella di applicare il protocollo SSL e le routine di base della crittografia simmetrica e asimmetrica (MD5, RSA, Blowfish, DES, ecc..), inoltre permette di realizzare e manipolare certificati X.509. Il principale aspetto positivo è sicuramente quello di essere considerato lo standard “de facto”, infatti con questa libreria viene utilizzata come riferimento per molti software che devono svolgere compiti per il quale è nata questa libreria. E' inoltre una libreria molto completa ed il fatto che sia stata scritta in C la rende anche molto efficiente in termini di prestazioni. Tuttavia non è una libreria facile da utilizzare e questa complicazione è amplificata dal fatto che non possiede una buona documentazione (a dire la verità non ha una sua documentazione se escludiamo quella della sua versione precedente chiamata SSLeay). JNI (L'interfaccia nativa di Java) L'interfaccia nativa di Java è una componente del JDK (Java Development Kit) che consente di chiamate funzioni compilate in codice nativo come quello generato da linguaggi come il C/C++ e Pascal. Per realizzare quest'interfaccia è necessario eseguire alcuni procedimenti che verranno illustrati mediante un esempio. Sarà data per scontata una sufficiente conoscenza dalla JNI. Prima di tutto scriviamo la nostra interfaccia Java: testing.java package test; public class testing { public testing() { } // Dichiara la funzione nativa “stampa()” private native void stampa(); static { // Carica la libreria Runtime.getRuntime().load("/usr/lib/libtesting.so"); } public static void main(String[] args) { // Utilizza la funzione nativa “stampa()” new testing().stampa(); } } Scritto questo codice dobbiamo eseguire da console: javac testing.java javah -jni testing Quest'ultimo passaggio genererà una firma (signature) che consiste in un file con estensione “.h” che in questo caso si chiamerà testing.h. Ora dobbiamo realizzare la nostra libreria in C (o qualsiasi altro linguaggio compilato): testing.c #include "testing.h" #include <stdio.h> JNIEXPORT void JNICALL Java_testing_stampa (JNIEnv *env, jobject obj) { printf("ciao"); } dove la firma: JNIEXPORT void JNICALL Java_testing_stampa (JNIEnv *env, jobject obj) è stata copiata dal file .h. Compiliamo il file testing.c affinche otteniamo una libreria. A questo punto non ci rimane altro da fare che eseguire il programma in Java da console: java testing per veder apparire la scritta “ciao”. Questo procedimento va bene nel caso in cui non abbiamo ancora scritto la libreria in codice nativo quindi nel nostro caso dobbiamo aggiungere alcuni passaggi dato che dobbiamo scrivere un wrapper di una libreria già esistente e che quindi le funzioni ad essa appartenenti non hanno la “signature” appropriata. La soluzione a questo problema verrà descritta in seguito [vedi paragrafo Struttura del wrapper]. La JNI è sicuramente un utile strumento ma presenta alcuni aspetti negativi intrinseci e progettuali: i primi sono dati dal fatto che per realizzare un'interfaccia sono necessari molti passaggi, i problemi progettuali sono dati dal fatto che è possibile realizzare interfacce delle sole librerie alle quali è stata eseguita una fase di link statico, non è quindi possibile realizzare interfacce di librerie che utilizzano funzioni di altre librerie a meno che non sia stato utilizzato un link statico. A mio avviso quest'ultimo problema si sarebbe potuto evitare progettando il sistema JNI in maniera diversa. Scelte progettuali e obiettivi Prima di proseguire con la spiegazione è necessario fare qualche passo indietro. All'inizio del tirocinio era stato stabilito un obiettivo che poi a causa delle circostanze verificatesi è mutato. Si era deciso infatti di realizzare una certification authority pertendo da quello che avevano svolto altri studenti tirocinanti in precedenza. Perchè era stato deciso di realizzare una nuova CA anzichè utilizzarne una già esistente open source? Il motivo è semplice: tra le varie CA non ce n'è nessuna che soddisfi in pieno le necessità dell'ASL cioè fornire un'infrastruttura PKI che svolga i servizi necessari all'azienda. Sono stati presi in considerazione diversi software di CA prima di eseguire questa scelta: primo fra tutti l'EJBCA ma oltre che ad essere difficile da installare, non utilizza la libreria OpenSSL che come abbiamo detto in precedenza è considerato come uno standard ed è segno di massima affidabilità; il secondo software preso in considerazione è l'OpenCA ma il problema più grave è che il programma è scritto in Perl e quindi particolarmente difficile da modificare secondo le proprie necessità. E' stato quindi necessario partire dalle basi: una libreria multifunzionale come l'OpenSSL. E' stato scelto il linguaggio Java per le sue proprietà intrinseche (prima fra tutti la sicurezza del codice) e per la disponibilità di strumenti specifici come librerie e applicativi Svolgimento Struttura del wrapper Il procedimento descritto in precedenza non è di per se sufficiente a raggiungere l'obiettivo da noi imposto è necessario frapporre un ulteriore strato (interfaccia) tra l'interfaccia in Java e la libreria OpenSSL: creiamo una libreria in C che invoca funzioni della libreria OpenSSL e che a sua volta viene invocata dall'inerfaccia Java. Tra le varie funzioni è stato deciso di iniziare con delle funzioni di generica utilità e di controllo degli errori. Prima di tutto riportiamo il primo file scritto contenete l'interfaccia JNI che permette di richiamare le funzioni di gestione di errore della OpenSSL con opportuni commenti (alcune parti sono state omesse per semplicità): Errors.java package it.asl7.wOpenSSL; public class Errors { //----- Costruttore public Errors() { LoadCryptoStrings(); LoadERRStrings(); } //----- Metodi // Cancella tutti gli errori dalla lista statica public void ClearAll() { // Chiamata a una funzione della libreria che verrà // frapposta tra questa e la OpenSSL. Questa funzione // cancella tutti gli errori dalla lista statica. ERR_clear_error(); } // ... public void FreeStrings() { // Libera tutte le stringhe d'errore caricate nella // tabella statica degli errori che mappa codici di // librerie e funzioni in stringhe di testo ERR_free_strings(); } // ... public long GetError() { // Ottiene il codice dell'errore e rimuove questa // voce dalla coda degli errori return ERR_get_error(); } // ... public String LibErrorString(long e) { // Restituisce il nome della libreria corrispondente // al codice di errore numerico return ERR_lib_error_string(e); } public void LoadCryptoStrings() { // Carica in memoria l'insieme “crypto” delle stringhe d'errore ERR_load_crypto_strings(); } // ... // Caricamento stringhe di errore public void LoadStrings(ErrStringData[] str) { int[] error = new int[str.length]; String[] message = new String[str.length]; for (int i = 0; i < str.length; i++) { error[i] = str[i].getError(); message[i] = str[i].getString(); } ERR_load_strings(error, message); } // ... // Dichiarazioni di funzioni native. Come è facile notare è // sufficiente l'utilizzo della parola chiave “native” oltre // che al prototipo della funzione private native long ERR_get_error(); private native int ERR_get_next_error_library(); private native long ERR_peek_error(); private native String ERR_error_string(long num); private native void ERR_error_string_n(long num); private native void ERR_clear_error(); private native void ERR_put_error(int lib, int func, int reason, String file, int line); private native void ERR_set_error_data(String data, int flags); private native String ERR_lib_error_string(long e); private native String ERR_func_error_string(long e); private native String ERR_reason_error_string(long e); // ... static { // Ci sono vari metodi per caricare una libreria. Tra quelli // disponibili, quello utilizzato ci permette di avere più // informazioni in caso di errore. In alternativa è possibile // utilizzare “System.loadLibrary("errors");” come mostrato // tra gli esempi presenti nella documentazione. Runtime.getRuntime().load("/usr/lib/liberrors.so"); } // Un piccolo test.. public static void main(String []args) { Errors err = new Errors(); err.ClearAll(); for (int i = 0; i < 50; i++) { System.out.println(err.ErrorString(i)); } } } Il file completo (come tutti quelli che seguiranno) sono disponibili nell'Appendice A e nel CDROM allegato. Eseguendo i comandi javac e javah otteniamo il file class e la signature seguente: Errors.h /* DO NOT EDIT THIS FILE - it is machine generated */ #ifndef __it_asl7_wOpenSSL_Errors__ #define __it_asl7_wOpenSSL_Errors__ #include <jni.h> #ifdef __cplusplus extern "C" { #endif extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error (JNIEnv *env, jobject); extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error_1line (JNIEnv *env, jobject); // ... #ifdef __cplusplus } #endif #endif /* __it_asl7_wOpenSSL_Errors__ */ E' facile notare che la signature segue delle regole ben precise infatti il prototipo di ogni funzione ha la seguente struttura: Java_ + “percorso della classe” + “nome funzione”. Se nell'interfaccia Java, un nome di funzione contiene un carattere di underscore, nella signature verrà sostituito con “_1”. Ora dobbiamo dare corpo a queste funzioni scrivendo il file “Errors.c”: Errors.c // Includiamo la parte della libreria OpenSSL inerente la gestione degli errori #include <openssl/err.h> // Includiamo la signature #include "Errors.h" // Includiamo funzioni che verranno descritte in seguito #include "Utils.h" char error[256]; JNIEXPORT jlong JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error (JNIEnv *env, jobject obj) { return ERR_get_error(); } // ... JNIEXPORT jstring JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1reason_1error_1string (JNIEnv *env, jobject obj, jlong e) { return CharpToJstr( env, ERR_reason_error_string(e) ); } // ... JNIEXPORT jstring JNICALL Java_it_asl7_wOpenSSL_Errors_val_1error (JNIEnv *env, jobject obj) { return CharpToJstr( env, error ); } Scrivere questa parte di codice è piuttosto semplice infatti è sufficiente copiare i vari prototipi dalla signature e nella maggior parte dei casi il corpo della funzione è composto dalla sola chiamata alla funzione delle OpenSSL. E' fondamentale ora notare l'utilizzo della funzione CharpToJstr. Il prototipo di questa funzione è presente nel file Utils.h e definita nel file Utils.c. Il file Utils.h dichiara anche altre funzioni: Utils.h #ifndef __Utils__ #define __Utils__ #include <jni.h> JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved); JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved); void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg); char * JstrToCharp(JNIEnv *env, jstring str); jstring CharpToJstr(JNIEnv *env, const char *str); #endif Queste funzioni sono di varia utilità: ad esempio JNI_OnLoad è una funzione che viene invocata all'avvio dalla Java Virtual Machine da parte di una funzione JNI. La funzione CharToJstr richiede una particolare spiegazione. Nonostante le similitudini, tra il Java ed il C ci sono delle incompatibilità tra i tipi di dato esistenti, primo fra tutti la rappresentazione delle stringhe infatti nel C abbiamo a disposizione i puntatori a carattere mentre il Java ha un tipo di dato astratto che si occupa di questo. Le funzioni CharToJstr e JstrToCharp si occupano di eseguire questa conversione in “entrambe le direzioni”. La definizione (il corpo) di queste cinque funzioni sono state tratte da un documento presente nel sito della Sun: “The Java Native Interface”, il codice è il seguente: Utils.c #include "Utils.h" jclass Class_java_lang_String = NULL; jmethodID MID_String_getBytes = NULL; jmethodID MID_String_init = NULL; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env; if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { return JNI_ERR; } // Crea un riferimento globale if (Class_java_lang_String == NULL) { jclass localRefCls = (*env)->FindClass(env, "java/lang/String"); if (localRefCls == NULL) { return JNI_ERR; // Lancio eccezione } Class_java_lang_String = (*env)->NewGlobalRef(env, localRefCls); (*env)->DeleteLocalRef(env, localRefCls); if (Class_java_lang_String == NULL) { return JNI_ERR; // Lancio eccezione OutOfMemory } } // Inizializazione Method ID MID_String_getBytes = (*env)->GetMethodID(env, Class_java_lang_String, "getBytes", "()[B"); MID_String_init = (*env)->GetMethodID(env, Class_java_lang_String, "<init>", "([C)V"); if ( (MID_String_getBytes == NULL) || (MID_String_init == NULL) ) { return JNI_ERR; } return JNI_VERSION_1_4; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved) { JNIEnv *env; if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { return; } } void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) { jclass cls = (*env)->FindClass(env, name); if (cls != NULL) { (*env)->ThrowNew(env, cls, msg); } (*env)->DeleteLocalRef(env, cls); } char * JstrToCharp(JNIEnv *env, jstring str) { jbyteArray bytes = 0; jthrowable exc; char *result = 0; if ( (*env)->EnsureLocalCapacity(env, 2) < 0 ) { return 0; } bytes = (*env)->CallObjectMethod(env, str, MID_String_getBytes); return NULL; exc = (*env)->ExceptionOccurred(env); if (!exc) { jint len = (*env)->GetArrayLength(env, bytes); result = (char *)malloc(len + 1); if (result == 0) { JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0); (*env)->DeleteLocalRef(env, bytes); return 0; } (*env)->GetByteArrayRegion(env, bytes, 0, len, (jbyte *)result); result[len] = 0; } else { (*env)->DeleteLocalRef(env, exc); } (*env)->DeleteLocalRef(env, bytes); return result; } jstring CharpToJstr(JNIEnv *env, const char *str) { jstring result; jbyteArray bytes = 0; int len; if ( (*env)->EnsureLocalCapacity(env, 2) < 0 ) { return NULL; } len = strlen(str); bytes = (*env)->NewByteArray(env, len); if (bytes != NULL) { (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *)str); result = (*env)->NewObject(env, Class_java_lang_String, MID_String_init, bytes); return result; } return NULL; } Durante la fase di compilazione non si verifica alcun problema ma eseguendo il comando “java Errors” otteniamo il seguente messaggio da parte della Java Virtul Machine: Unexpected Signal : 11 occurred at PC=0x401B53A7 Function=(null)+0x401B53A7 Library=/usr/j2sdk1.4.2_04/jre/lib/i386/client/libjvm.so NOTE: We are unable to locate the function name symbol for the error just occurred. Please refer to release documentation for possible reason and solutions. Current Java thread: Dynamic libraries: 08048000-08049000 r-xp 00000000 03:01 117962 /usr/source/OpenSSL_C/a.out 08049000-0804a000 rw-p 00000000 03:01 117962 /usr/source/OpenSSL_C/a.out 40000000-40012000 r-xp 00000000 03:01 425365 /lib/ld-2.3.2.so 40012000-40013000 rw-p 00012000 03:01 425365 /lib/ld-2.3.2.so 40014000-40024000 r-xp 00000000 03:01 637723 / usr/j2sdk1.4.2_04/jre/lib/i386/libverify.so 40024000-40026000 rw-p 0000f000 03:01 637723 / usr/j2sdk1.4.2_04/jre/lib/i386/libverify.so 40026000-40422000 r-xp 00000000 03:01 637718 / usr/j2sdk1.4.2_04/jre/lib/i386/client/libjvm.so 40422000-4043d000 rw-p 003fb000 03:01 637718 / usr/j2sdk1.4.2_04/jre/lib/i386/client/libjvm.so 4044f000-4046f000 r-xp 00000000 03:01 637725 / usr/j2sdk1.4.2_04/jre/lib/i386/libjava.so 4046f000-40471000 rw-p 0001f000 03:01 637725 / usr/j2sdk1.4.2_04/jre/lib/i386/libjava.so 40472000-4047a000 r-xp 00000000 03:01 637712 / usr/j2sdk1.4.2_04/jre/lib/i386/native_threads/libhpi.so 4047a000-4047b000 rw-p 00007000 03:01 637712 / usr/j2sdk1.4.2_04/jre/lib/i386/native_threads/libhpi.so 4047b000-4047f000 rw-s 00000000 03:01 69255 /tmp/hsperfdata_root/1007 4047f000-40481000 r-xp 00000000 03:01 179698 /usr/lib/liberrors.so 40481000-40482000 rw-p 00001000 03:01 179698 /usr/lib/liberrors.so 40483000-40595000 r-xp 00000000 03:01 425368 /lib/libc-2.3.2.so 40595000-4059a000 rw-p 00111000 03:01 425368 /lib/libc-2.3.2.so 4059d000-40679000 r-xp 00000000 03:01 554049 / usr/lib/i686/cmov/libcrypto.so.0.9.7 40679000-4068a000 rw-p 000db000 03:01 554049 / usr/lib/i686/cmov/libcrypto.so.0.9.7 4068e000-4069e000 r-xp 00000000 03:01 425372 /lib/libnsl-2.3.2.so 4069e000-4069f000 rw-p 00010000 03:01 425372 /lib/libnsl-2.3.2.so 406a1000-406c2000 r-xp 00000000 03:01 425371 /lib/libm-2.3.2.so 406c2000-406c3000 rw-p 00020000 03:01 425371 /lib/libm-2.3.2.so 406c3000-406c5000 r-xp 00000000 03:01 425370 /lib/libdl-2.3.2.so 406c5000-406c6000 rw-p 00001000 03:01 425370 /lib/libdl-2.3.2.so 406c6000-406d2000 r-xp 00000000 03:01 425383 /lib/libpthread-0.10.so 406d2000-406d3000 rw-p 0000c000 03:01 425383 /lib/libpthread-0.10.so 40716000-40742000 r-xp 00000000 03:01 554050 / usr/lib/i686/cmov/libssl.so.0.9.7 40742000-40745000 rw-p 0002b000 03:01 554050 / usr/lib/i686/cmov/libssl.so.0.9.7 40753000-40759000 r-xp 00000000 03:01 425373 /lib/libnss_compat-2.3.2.so 40759000-4075a000 rw-p 00005000 03:01 425373 /lib/libnss_compat-2.3.2.so 4075a000-40761000 r-xp 00000000 03:01 425377 /lib/libnss_nis-2.3.2.so 40761000-40762000 rw-p 00007000 03:01 425377 /lib/libnss_nis-2.3.2.so 40762000-4076a000 r-xp 00000000 03:01 425375 /lib/libnss_files-2.3.2.so 4076a000-4076b000 rw-p 00007000 03:01 425375 /lib/libnss_files-2.3.2.so 4076b000-4077f000 r-xp 00000000 03:01 637727 / usr/j2sdk1.4.2_04/jre/lib/i386/libzip.so 4077f000-40782000 rw-p 00013000 03:01 637727 / usr/j2sdk1.4.2_04/jre/lib/i386/libzip.so 40782000-42126000 r--s 00000000 03:01 639155 / usr/j2sdk1.4.2_04/jre/lib/rt.jar 42170000-42186000 r--s 00000000 03:01 637759 / usr/j2sdk1.4.2_04/jre/lib/sunrsasign.jar 42186000-42263000 r--s 00000000 03:01 639063 / usr/j2sdk1.4.2_04/jre/lib/jsse.jar 42263000-42274000 r--s 00000000 03:01 637761 usr/j2sdk1.4.2_04/jre/lib/jce.jar / 42274000-427cd000 r--s 00000000 03:01 639101 / usr/j2sdk1.4.2_04/jre/lib/charsets.jar Heap at VM Abort: Heap def new generation total 576K, used 97K [0x44880000, 0x44920000, 0x44d60000) eden space 512K, 19% used [0x44880000, 0x44898758, 0x44900000) from space 64K, 0% used [0x44900000, 0x44900000, 0x44910000) to 0% used [0x44910000, 0x44910000, 0x44920000) space 64K, tenured generation the space 1408K, compacting perm gen total 1408K, used 0K [0x44d60000, 0x44ec0000, 0x48880000) 0% used [0x44d60000, 0x44d60000, 0x44d60200, 0x44ec0000) total 4096K, used 827K [0x48880000, 0x48c80000, 0x4c880000) the space 4096K, 20% used [0x48880000, 0x4894ecd8, 0x4894ee00, 0x48c80000) Local Time = Wed Aug 25 08:58:59 2004 Elapsed Time = 0 # # HotSpot Virtual Machine Error : 11 # Error ID : 4F530E43505002EF # Please report this error at # http://java.sun.com/cgi-bin/bugreport.cgi # # Java VM: Java HotSpot(TM) Client VM (1.4.2_05-b04 mixed mode) # E' un messaggio che rivela un bug della JVM. Per risolvere questo problema sono state cercate informazioni su questo tipo di problema ed eventuali soluzioni ma non è stato trovato nulla che possa soddisfare le nostre necessità. Vorrei evidenziare il fatto che nel codice di queste funzioni di utilità non ci sono errori di sintassi ne di semantica. Il problema di conversione da “char *” a “jstring” (e viceversa) non è raggirabile. Dopo vari tentativi (eseguendo semplici esempi) è stato deciso di abbandonare la realizzazione del wrapper. Avremmo potuto tentare di rieseguire il codice in altri sistemi operativi come ad esempio MS Windows o altre versioni della JVM ma questa scelta sarebbe andata contro uno degli obiettivi preposti: realizzare una CA utilizzabile nella maggior parte dei sistemi. Non a caso è stato scelto il Java come linguaggio di programmazione. Installazione di EJBCA Il secondo obiettivo preposto è stato quello di installare il software EJBCA e descrivere in maniera abbastanza dettagliata la procedura di installazione. EJBCA (Enterprise Java Bean Certification Authority) è un software per la certification authority scritto in Java. Per l'esecuzione è necessiario che sia avviato un application server (per ulteriori dettagli sugli application server consultare la documentazione del JDK). La procedura di installazione qui descritta è valida per sistemi operativi linux ma con determinati accorgimenti è facilmente adattabile per altri ambienti. Per una sua versione più schematica consultare l'appendice B. Descrizione dei software da installare JDK Il primo software da installare è il JDK. Si consiglia di installare l'ultima versione stabile disponibile nel sito ufficiale http://java.sun.com. La versione da me utilizzata è la 1.4.2_05. Copiare il contenuto dell'archivio scaricato in una directory “a scelta”. Per i passaggi successivi (e per convenienza) andiamo a modificare uno dei file di avvio dove possiamo andare a definire variabili di ambiente come il file “profile” inserendo le variabili JDK_HOME, JAVA_HOME e CLASSPATH a seconda della directory in cui è stato installato il JDK. Ad esempio nel mio caso ho inserito le seguenti righe: export PATH=/usr/lib/java: ... export JDK_HOME="/usr/lib/java/bin" export JAVA_HOME=/usr/j2sdk1.4.2_05 export CLASSPATH=/usr/j2sdk1.4.2_05/jre/lib/ext/ ... :$CLASSPATH Verificare quindi che sia installato il package JCE (Java Cryptography Extension). Questo package è già presente nella versione 1.4 e probabilmente lo sarà anche nelle successive. E' inoltre necessario installare l'Unlimited Strength Java Cryptography Extension per poter utilizzare crittografia forte e “illimitata”. Il “pacchetto” è disponibile nel sito della sun nella stessa pagina dove è possibile scaricare il JDK e consiste in due file *.jar (local_policy.jar e US_export_policy.jar) che devono essere copiati nelle directory $JAVA_HOME/jre/lib e $JAVA_HOME/lib/security (questa directory è da creare). Questo pacchetto è necessario fondamentalmente per scopi legali infatti nel file README.txt che si trova insieme ai file jar è possibile leggere: JCE for J2SDK, v 1.4.2 has been through the U.S. export review process. The JCE framework, along with the SunJCE provider that comes standard with it, is exportable. The JCE architecture allows flexible cryptographic strength to be configured via jurisdiction policy files. Due to the import restrictions of some countries, the jurisdiction policy files distributed with the J2SDK, v 1.4.2 software have built-in restrictions on available cryptographic strength. The jurisdiction policy files in this download bundle (the bundle including this README file) contain no restrictions on cryptographic strengths. This is appropriate for most countries. Framework vendors can create download bundles that include jurisdiction policy files that specify cryptographic restrictions appropriate for countries whose governments mandate restrictions. Users in those countries can download an appropriate bundle, and the JCE framework will enforce the specified restrictions. MySQL EJBCA richiede l'utilizzo di un database per immagazzinare ogni tipo di informazione necessaria e ci da la possibilità di scegliere tra varie opzioni e tra le quali abbiamo scelto il MySQL disponibile all'indirizzo www.mysql.com. E' stato scelto questo DBMS perchè possiede molte qualità che vengono incontro alle nostre necessità infatti il MySQL è multipiattaforma, open source, efficiente, dispone di una buona documentazione e in caso di problemi non si hanno grosse difficoltà nel trovare una soluzione. La versione che è stata installata è la 4.0.21. C'è da fare attenzione con la scelta della versione da scegliere perchè si possono riscontrare anomalie: installando la versione 4.0.14 ci sono stati dei problemi alla tabella “UserData” del database “ejbca”, tale tabella veniva danneggiata durante l'installazione di EJBCA. Una volta scaricato il pacchetto dal sito ufficiale, creare il gruppo e l'utente “mysql”: shell> groupadd mysql shell> useradd -g mysql mysql Possiamo quindi proseguire iniziando l'installazione: shell> cd /usr/local shell> gunzip < /PATH/TO/MYSQL-VERSION-OS.tar.gz | tar xvf shell> ln -s FULL-PATH-TO-MYSQL-VERSION-OS mysql shell> cd mysql Copiamo il file “/directory-di-installazione-mysql/support-file/my-medium.cnf” nella directory “/etc” e rinominarlo in “my.cnf” e verificare che all'interno di tale file la voce “skip-networkin” sia posta sotto commento (anteponendo il carattere “#”) o cancellata; questa operazione è necessaria perchè il linguaggio Java accede al MySQL come se si trovasse in un altro pc utilizzando un indirizzo IP. Dalla directory “/usr/local/mysql” eseguire: scripts/mysql_install_db --user=mysql Acquisiamo i privilegi necessari: shell> chown -R root . shell> chown -R mysql data shell> chgrp -R mysql . L'installazione è completata. Per poter utilizzare MySQL è necessario che il demone “mysqld” sia attivo, quindi sarebbe è necessario avviarlo all'avvio del sistema operativo o perlomeno prima di utilizzare il DBMS: shell> /usr/local/mysql/bin/mysqld_safe --user=mysql & Per terminare il demone in qualsiasi momento: shell> killall mysqld Il MySQL mette a disposizione una particolare console che ci consente di creare e manipolare database digitiamo quindi: shell> /usr/local/mysql/bin/mysql Inviamo i comandi: > use mysql > create database ejbca; > grant all privileges on *.* to 'ejbca'@'%' identified by 'ejbca' with grant option; > grant all privileges on *.* to 'ejbca'@'linux' identified by 'ejbca' with grant option; > quit Il primo comando ci permette di accedere al database “mysql”, successivamente creiamo il database “ejbca”, con le due operazioni successive forniamo privilegi illimitati a tutti coloro che accedono dall'esterno (“%”) e dall'interno (“linux” alias di 127.0.0.1) per tutte le tabelle di tutti i database. Sarebbe bene modificare le tabelle (e i relativi utenti) alle quali si vogliono fornire i privilegi (per motivi di sicurezza). Sarebbe opportuno verificare il funzionamento di mysql provando ad accedere dall'esterno (con l'opzione '-h ip_host' del comando mysql) ad esempio: “mysql -u ejbca -puna_password -h 10.6.160.73”, “mysql -u ejbca -puna_password -h linux” e “mysql -u ejbca -puna_password -h localhost”. Fornire inoltre un'opportuna password per l'utente “root” di mysql. JDBC Il JDBC (Java DataBase Connector) è uno strumento necessario per potersi connettere a database MySQL mediante programmi Java. Questa interfaccia è reperibile all'indirizzo www.mysql.com/products/connector/j/. Per l'installazione è sufficiente copiare il contenuto dell'archivio scaricato in “/usr” e inserire il percorso di installazione (ad esempio: “/usr/mysqlconnector-java-3.0.11-stable”) nella variabile di ambiente CLASSPATH. BerkeleyDB BerkeleyDB è un DBMS necessario per il software OpenLDAP disponibile all'indirizzo www.sleepycat.com/download/db/index.shtml. Copiare il contenuto dell'archivio in una directory (ad esempio /usr), spostarsi nella directory “build_unix”e per compilare la libreria eseguire: shell> ../dist/configure shell> make Quindi installare la libreria: shell> make install Verranno richiesti alcuni software specifici se non sono già installati come la versione appropriata di Automake (disponibile all'indirizzo ftp ftp://ftp.gnu.org/pub/gnu/automake). OpenLDAP OpenLDAP è un software open source (disponibile all'indirizzo www.openldap.org) che applica il protocollo LDAP (Lightweight Directory Access Protocol) che fornisce un modo per accedere ed aggiornare informazioni immagazzinate con un determinato metodo in altri calcolatori accedibili mediante una rete. Spesso l'LDAP viene utilizzato in contesti dove è necessario utilizzare informazioni riguardanti la sicurezza (certificati, password, accesso a determinate risorse, ecc..). L'OpenLDAP richiede anche l'installazione di alcuni software tra i quali la libreria Cyrus SASL (reperibile all'indirizzo http://asg.web.cmu.edu/cyrus/download/). In seguito è possibile installare OpenLDAP copiando il contenuto dell'archivio in una directory a scelta e mediante la seguente sequenza di comandi: ./configure make depend make make test make install Il comando “make test” non è obbligatorio ma utile al fine di identificare eventuali errori. Per eseguire correttamente “make install” sono necessari i privilegi di root. Editare quindi il file slapd.conf (in /usr/local/etc/openldap/) ed inserire gli opportuni valori: ...... database bdb suffix "O=NISEC,C=CN" rootdn "CN=Manager,O=NISEC,C=CN" rootpw ldappasswd directory /usr/local/var/data index objectClass eq Se non si sono verificati problemi di alcun tipo, l'installazione di OpenLDAP è terminata. E' consigliabile eseguire qualche test sull'effettivo funzionamento del software da una postazione remota aggiungendo, modificando e cancellando record mediante un qualsiasi programma in grado di eseguire queste operazioni. JBoss JBoss è un application server J2EE open source scritto in Java disponibile all'indirizzo http://www.jboss.org. La versione che è stata utilizzata per eseguire EJBCA è la 3.2.5. Per l'installazione è sufficiente copiare il contenuto dell'archivio nella directory /usr e creare la variabile di ambiente JBOSS_HOME alla quale assegniamo la directory di installazione. Per l'esecuzione di JBoss consultare il paragrafo “Operazioni conclusive”. Ant Apache Ant è uno strumento di compilazione scritto in Java (http://ant.apache.org/bindownload.cgi) che ci consentirà di completare l'installazione di EJBCA. Copiare il contenuto dell'archivio nella directory /usr/j2ee (da creare), verificare la presenza dei file di shell bin/ant.sh e bin/antRun.sh (se non sono presenti, scaricare i sorgenti di Ant e copiare questi file nella directory) e verificare la presenza dei privilegi di esecuzione. Inserire inoltre la directory bin di apache-ant nella variabile di ambiente PATH. EJBCA Come detto in precedenza, EJBCA è un software che realizza una certification authority ed offre molte funzioni che altre CA non hanno a disposizione, ad esempio ci consente di gestire CA multiple e gerarchiche ed utilizzarle contemporaneamente avviando una sola istanza dell'applicazione. E' possibile inoltre creare gruppi di amministratori con poteri configurabili. Il software fornisce il supporto per l'OCSP (Online Certificate Status Protocol), per le smart cart e per tutti i principali formati di certificati. Infine per chi non vuole utilizzare il prompt dei comandi, può disporre di una comoda interfaccia grafica (GUI) accedibile mediante browser. EJBCA è reperibile all'indirizzo http://ejbca.sourceforge.com. Per eseguire l'installazione copiare il contenuto dell'archivio in /usr/j2ee. Dobbiamo quindi creare il modulo (ejbca-ca.ear) di EJBCA che verrà eseguito (deploy) all'avvio di JBoss. Per creare il file ejbca-ca.ear (che verrà automaticamente copiato nell'opportuna directory) eseguire: cd /usr/j2ee/ejbca ant replaceDS Il programma chiederà di selezionare un dbms, nel nostro caso digitiamo “mysql” poi dovremo scegliere un datasource: java:/EjbcaDS Nel caso in cui si utilizzi HypersoniqDS (il database di default di JBoss), l'input da inserire è java:/DefaultDS. Terminare infine con: ant deploy Si dovrebbe vedere qualcosa del genere: root@linux:/usr/j2ee/ejbca# ant replaceDS Buildfile: build.xml replaceDS: [input] Type of database :(oracle,mssql,mysql,postgres,sapdb,hsqldb,sybase) mysql [input] Data source (default java:/DefaultDS): EjbcaDS [copy] Copying 1 file to /usr/j2ee/ejbca/src/ca/ca/META-INF [copy] Copying 1 file to /usr/j2ee/ejbca/src/ra/META-INF [copy] Copying 1 file to /usr/j2ee/ejbca/src/log/META-INF [copy] Copying 1 file to /usr/j2ee/ejbca/src/authorization/META-INF [copy] Copying 1 file to /usr/j2ee/ejbca/src/hardtoken/META-INF [copy] Copying 1 file to /usr/j2ee/ejbca/src/keyrecovery/META-INF BUILD SUCCESSFUL Total time: 12 seconds root@linux:/usr/j2ee/ejbca# ant deploy Buildfile: build.xml init: compile: apply.war: status.war: webdist.war: ca.jar: [copy] Copying 1 file to /usr/j2ee/ejbca/tmp/ca/ca.jar [jar] Building jar: /usr/j2ee/ejbca/dist/ca.jar ra.jar: [copy] Copying 1 file to /usr/j2ee/ejbca/tmp/ra.jar [jar] Building jar: /usr/j2ee/ejbca/dist/ra.jar adminweb.war: log.jar: [copy] Copying 1 file to /usr/j2ee/ejbca/tmp/log.jar [jar] Building jar: /usr/j2ee/ejbca/dist/log.jar hardtoken.jar: [copy] Copying 1 file to /usr/j2ee/ejbca/tmp/hardtoken.jar [jar] Building jar: /usr/j2ee/ejbca/dist/hardtoken.jar keyrecovery.jar: [copy] Copying 1 file to /usr/j2ee/ejbca/tmp/keyrecovery.jar [jar] Building jar: /usr/j2ee/ejbca/dist/keyrecovery.jar authorization.jar: [copy] Copying 1 file to /usr/j2ee/ejbca/tmp/authorization.jar [jar] Building jar: /usr/j2ee/ejbca/dist/authorization.jar ca.ear: [copy] Copying 6 files to /usr/j2ee/ejbca/tmp/ca/ear/ear [jar] Building jar: /usr/j2ee/ejbca/dist/ejbca-ca.ear admin.jar: deploy: [copy] Copying 1 file to /usr/jboss-3.2.5/server/default/deploy [copy] Copying /usr/j2ee/ejbca/dist/ejbca-ca.ear to /usr/jboss-3.2.5/server/default/deploy/ejbca-ca.ear BUILD SUCCESSFUL Total time: 10 seconds Operazioni conclusive Affinche l'installazione di questi software sia completa è necessario fare ulteriori passi. Copiare il file *.jar del JDBC nella directory $JBOSS_HOME/server/default/lib/, Spostarsi nella directory /usr/j2ee/ejbca/ e copiare il file doc/mysql-ds.xml (nel caso in cui si utilizzi MySQL, altrimenti scegliere il file *-ds.xml a seconda del dbms utilizzato) in $JBOSS_HOME/server/default/deploy/ e modificare quest'ultimo affinche risulti nel seguente modo: ...... <datasources> <local-tx-datasource> <jndi-name>EjbcaDS</jndi-name> <connection-url>jdbc:mysql://localhost:3306/ejbca</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>ejbca</user-name> <password>ejbca</password> <!-- Eventualmente da adattare --> <min-pool-size>5</min-pool-size> <max-pool-size>20</max-pool-size> <idle-timeout-minutes>15</idle-timeout-minutes> <track-statements>true</track-statements> </local-tx-datasource> </datasources> ...... Nel caso in cui utilizzassimo un altro tipo di database, il relativo file *-ds.xml potrebbe presentare un diverso numero di tag. In tal caso non aggiungerne ne cancellarne alcuno e assegnare ai tag presenti i corretti valori. Da notare inoltre che il jndi-name, username e password sono da modificare se sono stati cambiati dei valori nei passaggi precedenti (jndi-name: ant replaceDS; .. ; java:/EjbcaDS => EjbcaDS). Ora possiamo avviare JBoss: $JBOSS_HOME/bin/run.sh Otterremo qualcosa del genere: root@linux:/usr/jboss-3.2.5/bin# ./run.sh =================================== JBoss Bootstrap Environment JBOSS_HOME: /usr/jboss-3.2.5 JAVA: /usr/j2sdk1.4.2_04/bin/java JAVA_OPTS: -server -Dprogram.name=run.sh CLASSPATH: /usr/jboss-3.2.5/bin/run.jar:/usr/j2sdk1.4.2_04/lib/tools.jar ================================== 08:32:42,833 INFO [Server] Starting JBoss (MX MicroKernel)... 08:32:42,852 INFO [Server] Release ID: JBoss [WonderLand] 3.2.5 (build: CVSTag=JBoss_3_2_5 date=200406251954) 08:32:42,853 INFO [Server] Home Dir: /usr/jboss-3.2.5 08:32:42,854 INFO [Server] Home URL: file:/usr/jboss-3.2.5/ 08:32:42,854 INFO [Server] Library URL: file:/usr/jboss-3.2.5/lib/ ... Durante questa fase è necessario fare molta attenzione perchè non dovrà essere visualizzato alcun errore. Nel caso in cui ci sia anche un solo errore, non proseguire con i passi successivi dell'installazione fino a quando non si è risolto il problema, ulteriori informazioni possono essere ricavate nel file $JBOSS_HOME/server/default/log/server.log. Ad esempio: 10:56:51,715 ERROR [EntityContainer] Starting failed jboss.j2ee:jndiName=local/EndEntityProfileData,service=EJB org.jboss.deployment.DeploymentException: Error: can't find data source: EjbcaDS; - nested throwable: (javax.naming.NameNotFoundException: EjbcaDS not bound) Bisogna fare attenzione anche ad alcuni messaggi di warning come il seguente 11:56:13,974 WARN [JBossManagedConnectionPool] Throwable while attempting to get a new connection: null org.jboss.resource.JBossResourceException: Could not create connection; - nested throwable: (java.sql.SQLException: Invalid authorization specification, message from server: "Access denied for user: 'ejbca@linux' (Using password: YES)") Avviare quindi l'installazione di EJBCA: /usr/j2ee/ejbca/install.sh Durante l'installazione dovranno essere inseriti vari dati. Per quanto riguarda la policy id, va bene quella suggerita nell'esempio ("All Issuance (2.5.29.32.0). The all issuance policy indicates that the issuance policy contains all other issuance policies. Typically, this object identifier is only assigned to CA certificates"). Per quanto riguarda la password, è necessario annotarla infatti verrà richiesta quando si importerà il certificato nel browser. Si otterrà un output simile al seguente: root@linux:/usr/j2ee/ejbca# ./install.sh java version "1.4.2_05" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_05-b04) Java HotSpot(TM) Client VM (build 1.4.2_05-b04, mixed mode) Welcome to EJBCA Installation This script acts as a wizard helping you with the installation of your Certificate Authority. Before the installation will begin make sure of the following preparations have been done: 1. The EJBCA application is deployed to the application server. ('ant deploy') 2. You run this installation with access to administrative privileges. Is these requirements meet (Yes/No) :Yes This installation will create a first administrative CA. This CA will be used to create the first superadministrator and for the SSL server certificate of administrative web server. When the administrative web server have been setup you can create other CA:s and administrators. Please enter the short name for the CA. This is only used for administrative purposes, avoid spaces or odd characters (Ex 'AdminCA1') :AdminCA1 Enter the Distinguished Name of the CA. This is used in the CA certificate to distinguish the CA. (Ex 'CN=AdminCA1,O=PrimeKey Solutions AB,C=SE') :C=CN,O=ASLADMIN,CN=AdminCA1 Enter the keysize in bits of the CA, only digits. (Ex '2048') : 2048 Enter the validity in days for the CA, only digits (Ex '3650') :3650 Enter the policy id of the CA. Policy id determine which PKI policy the CA uses. Type your policy id or use '2.5.29.32.0' for any policy or 'NO' for no policy at all. (Ex '2.5.29.32.0') :2.5.29.32.0 Now for some information required to set up the administration web interface. Please enter the computer name of CA server. (Ex 'caserver.primekey.se') :kittmad.asl.it Enter the Distinguished Name of the SSL server certificate used by the administrative web gui (Ex 'CN=caserver.primekey.se,O=PrimeKey Solutions AB,C=SE') :C=CN,O=ASLADMIN,CN=kittmad.asl.it Enter a good password for the super administrators keystore. Please remember this one:********** You have entered the following data : CA short name : AdminCA1 Distinguished Name CA : C=CN,O=ASLADMIN,CN=AdminCA1 Keysize of the CA : 2048 Validity in days for the CA : 3650 Policy id of the CA : 2.5.29.32.0 Computer name of CA server : kittmad.asl.it Distinguished Name of the SSL server certificate : C=CN,O=ASLADMIN,CN=kittmad.asl.it Password for the super administrators keystore : ********** Is this correct ( Yes/No/Exit ) :Yes The installation will now start, please wait ..... E' importante ricordare anche il valore CN del DN del certificato del server SSL, infatti tale valore deve essere inserito nel file degli host (es: /etc/hosts) al termine di install.sh. Al file hosts dovremo quindi aggiungere la seguente riga (o modificarla se in parte è già presente): 127.0.0.1 localhost linux kittmad.asl.it Terminare JBoss chiudendo la finestra di shell o terminando il relativo processo. Verificare che il file server.xml (presente nella directory $JBOSS_HOME/server/default/deploy/jbossweb-tomcat50.sar/) sia uguale al seguente (esclusi gli attributi “keystorePass” presente in alcuni tag “Connector”): <Server> <Service name="jboss.web" className="org.jboss.web.tomcat.tc5.StandardService"> <!-- A HTTP/1.1 Connector on port 8080 --> <Connector port="8080" address="${jboss.bind.address}" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" connectionTimeout="20000"cates that the issuance policy contains all other issuance policies. Typically, this object identifier is only assigned to CA certif disableUploadTimeout="true" /> <!-- A AJP 1.3 Connector on port 8009 --> <Connector port="8009" address="${jboss.bind.address}" enableLookups="false" redirectPort="8443" debug="0" protocol="AJP/1.3" /> <!-- A HTTPS Connector without client cert on port 8442 --> <Connector port="8442" address="${jboss.bind.address}" maxThreads="100" minSpareThreads="5" maxSpareThreads="15" scheme="https" secure="true" clientAuth="false" keystoreFile="${jboss.server.home.dir}/../../bin/tomcat.jks" keystorePass="nZbVDFjj" sslProtocol = "TLS" /> <!-- HTTPS Connector requiring client cert on port 8443 --> <Connector port="8443" address="${jboss.bind.address}" maxThreads="100" minSpareThreads="5" maxSpareThreads="15" scheme="https" secure="true" clientAuth="true" keystoreFile="${jboss.server.home.dir}/../../bin/tomcat.jks" keystorePass="nZbVDFjj" sslProtocol = "TLS" /> <Engine name="jboss.web" defaultHost="localhost"> <Logger className="org.jboss.web.tomcat.Log4jLogger" verbosityLevel="WARNING" category="org.jboss.web.localhost.Engine"/> <Realm className="org.jboss.web.tomcat.security.JBossSecurityMgrRealm" subjectAttributeName="j_subject" certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping" /> <Host name="localhost" autoDeploy="false" deployOnStartup="false" deployXML="false"> <DefaultContext cookies="true" crossContext="true" override="true" /> </Host> </Engine> </Service> </Server> attenzione in particolar modo ai tag: Service, Engine, Logger, Realm, Host e a quelli in più che devono essere rimossi (o inseriti sotto commento). Avviare di nuovo JBoss: $JBOSS_HOME/bin/run.sh Importare nel browser il certificato contenuto nel file superadmin.p12 (nella directory / usr/j2ee/ejbca/p12/). Per un'ulteriore verifica è possibile eseguire /usr/j2ee/ejbca/runtest.sh(Non deve verificarsi nessun errore [Failure]). Il test non dovrebbe essere eseguito nella stessa macchina dove verrà utilizzato ejbca infatti esso creerà dei record nel database 'ejbca'. Aprire il browser all'indirizzo https://localhost:8443/ejbca dove apparirà la pagina iniziale della GUI dove è possibile creare e gestire le certification authority. Note aggiuntive Durante le varie fasi di installazione è stato necessario far fronte a molti errori di diversa natura (versioni di software errate, configurazioni, ecc..) e per la loro risoluzione è stato necessario consultare diverse fonti, sarebbe quindi opportuno avere sempre a disposizione i manuali dei vari programmi utilizzati (database, application server, ecc..). Alcune soluzioni a problemi di diversa natura possono essere trovate nei file presenti nella directory “doc” di EJBCA: HOWTO-database INSTALL-guide README HOWTO-LDAP FAQ In alcuni casi è consigliabile reinstallare JBoss ed EJBCA dopo aver cancellato ogni loro traccia (comprese le tabelle create in mysql). Possono rivelarsi molto utili strumenti come Webmin (http://www.webmin.com/download.html) in modo tale da velocizzare operazioni come la verifica dei dati nel database. Conclusioni Da come è stato possibile notare dai capitoli precedenti, molto spesso la teoria non riesce a rappresentare la realtà senza dover eseguire adattamenti e risolvere problemi di diversa natura, in fin dei conti l'ingegneria si occupa proprio di risolvere questo genere di problemi. Ci eravamo proposti di scrivere un wrapper Java delle OpenSSL ma a causa di problemi pratici che richiedono modifiche all'infrastruttura sul quale si lavora è stato necessario abbandonare il progetto. Credo che la causa di questo problema sia l'elevata complessità del sistema sul quale si è dovuto lavorare, d'altronde questa complessità è difficilmente evitabile è data dalla necessità di creare un numero sempre maggiore di livelli di astrazione. I software sono scritti da persone ed in quanto tali sono fallibili, in maggior modo in architetture complesse. Per ridurre la complessità è ovviamente necessaria maggiore semplicità (si pensi alla complessità dell'infrastruttura EJB [Enterprise Java Bean]). Credo che questa semplicità si possa raggiungere in diversi modi, ad esempio facendo in modo tale che gli standard fossero più standard infatti a volte uno standard non coincide con le relative applicazioni, un altro metodo potrebbe essere quello di individuare correttamente di volta in volta il numero ed il tipo dei livelli di astrazione necessari. Un altro metodo per ridurre le problemi che si riscontrano in architetture complesse è quello di utilizzare e scrivere software open source, tale scelta permette di avere l'appoggio di una grande comunità mondiale, inoltre il software utilizzato essendo verificato e corretto da una gran varietà di programmatori, tende a ridurre il numero di bug presenti. Oltre a dover affrontare problemi reali, durante questo tirocinio ho potuto approfondire la mia conoscenza della PKI , delle CA e di software come OpenSSL e Linux, tali conoscenze si sono rivelate fondamentali per il proseguimento degli obiettivi preposti. Vorrei fare inoltre una considerazione sull'utilizzo del java (e sui linguaggi interpretati con strutture simili) in qualunque software: dal momento in cui si sceglie di utilizzare tale linguaggio dobbiamo tenere bene a mente che stiamo riponendo la nostra fiducia (e quindi eventuale tempo ed eventuali futuri guadagni) ad una struttura che non possiamo modificare, dal comportamento non sempre prevedibile e che potrebbe “lasciarci a piedi” in qualunque momento. Questa considerazione non è valida in altri casi come ad esempio libreria scritte in C++ dove però si dovrà affrontare un diverso genere di problemi. Appendice A – Procedura di installazione di EJBCA 1) Java (java.sun.com) Il primo software da installare è l'ultima versione stabile del JDK. Inserire le seguenti variabili di ambiente (profile) con i relativi valori: JDK_HOME JAVA_HOME CLASSPATH Ad esempio: export PATH=/usr/lib/java: ... export JDK_HOME="/usr/lib/java/bin" export JAVA_HOME=/usr/j2sdk1.4.2_05 export CLASSPATH=/usr/j2sdk1.4.2_05/jre/lib/ext/ ... :$CLASSPATH Verificare che la versione installata contenga il package JCE (Java Cryptography Extension) nel caso in cui non sia presente rimediare il package dal sito della Sun. Questo package è presente nella versione 1.4 (e successive). Per utilizzare la crittografia forte in maniera illimitata installiamo l'Unlimited Strength Java Cryptography Extension (anch'esso disponibile nel sito della Sun nella stessa pagina dove è possibile scaricare il JDK). Esso consiste in due file *.jar (local_policy.jar e US_export_policy.jar) da inserire in $JAVA_HOME/jre/lib e $JAVA_HOME/lib/security (la directory è da creare). 2) Database (www.mysql.com) Ejbca richiede l'utilizzo di un database. Possiamo scegliere tra: Oracle, SQL Server 2000, MySQL, PostgreSQL, SapDB, HypersoniqDB e Sybase. E' stato scelto MySQL versione 4.0.21 ATTENZIONE! Alcune versioni precedenti possono causare problemi Per l'istallazione è necessario eseguire i seguenti passi: Creazione gruppo e utente shell> groupadd mysql shell> useradd -g mysql mysql Installazione shell> cd /usr/local shell> gunzip < /PATH/TO/MYSQL-VERSION-OS.tar.gz | tar xvf shell> ln -s FULL-PATH-TO-MYSQL-VERSION-OS mysql shell> cd mysql (*) Leggi sotto shell> scripts/mysql_install_db --user=mysql Acquisizione privilegi shell> chown -R root . shell> chown -R mysql data shell> chgrp -R mysql . Avvio demone (da eseguire ogni volta che è necessario o più semplicemente all'avvio di linux) shell> bin/mysqld_safe --user=mysql & (*) Prima di eseguire 'scripts/mysql_install_db --user=mysql', copiare il file /directory-diinstallazione-mysql/support-file/my-medium.cnf in /etc e rinominarlo in my.cnf, editarlo e verificare che la voce skip-networking sia sotto commento (#skip-networking). Una volta eseguita l'installazione, digitare 'mysql' per entrare nell'opportuna shell. Inviamo il comando 'use mysql'. Creiamo a questo punto il database 'ejbca' mediante il comando 'create database ejbca;' e creiamo l'utente ejbca (la password è ovviamente da modificare [... identified by 'nuova_password' ...]) con il comando 'grant all privileges on *.* to 'ejbca'@'%' identified by 'ejbca' with grant option;'. E' necessario anche il comando 'grant all privileges on *.* to 'ejbca'@'linux' identified by 'ejbca' with grant option;'. Chiudere la shell con 'quit'. Verificare il funzionamento dell'utente provando ad accedere dall'esterno (con l'opzione '-h ip_host' del comando mysql) [es: mysql -u ejbca -pquesta_password -h 10.6.160.73, mysql -u ejbca -pquesta_password -h linux e mysql -u ejbca -pquesta_password -h localhost]. Fornire inoltre un'opportuna password per l'utente root di mysql. Terminare il demone mysqld ('killall mysqld') e riavviarlo (/usr/local/mysql/bin/mysqld_safe -user=mysql &). 3) JDBC (Nel caso di mysql: www.mysql.com/products/connector/j/) Copiare il contenuto dell'archivio in /usr e inserire il percorso di installazione (es: /usr/mysqlconnector-java-3.0.11-stable) nella variabile di ambiente CLASSPATH. 4) OpenLDAP (www.openldap.org) L'installazione di OpenLDAP richiede che siano installati vari software. Primo fra questi il BerkeleyDB (l'ultima versione) disponibile all'indirizzo www.sleepycat.com/download/db/index.shtml che a sua volta può richiedere l'installazione della versione appropriata del programma Automake disponibile sul server ftp ftp://ftp.gnu.org/pub/gnu/automake. L'OpenLDAP richiede anche l'installazione della libreria Cyrus SASL (http://asg.web.cmu.edu/cyrus/download/). Una volta eseguite queste operazioni preliminari è possibile installare OpenLDAP mediante la seguente sequenza di comandi ./configure make depend make make test <- E' facoltativo ma consigliato make install <- Sono necessari i privilegi di root Editare quindi il file slapd.conf (in /usr/local/etc/openldap/) e inserire gli opportuni valori: ...... database bdb suffix "O=NISEC,C=CN" rootdn "CN=Manager,O=NISEC,C=CN" rootpw ldappasswd directory /usr/local/var/data index objectClass eq ...... 5) Ant (http://ant.apache.org/bindownload.cgi) Copiare il contenuto dell'archivio nella directory /usr/j2ee (da creare), verificare la presenza dei file di shell bin/ant.sh e bin/antRun.sh (se non sono presenti, scaricare i sorgenti di Ant e copiare questi file nella directory) e verificare la presenza dei privilegi di esecuzione. Inserire la directory bin di apache-ant nella variabile di ambiente PATH. 6) JBOSS (www.jboss.org) Per effettuare l'installazione è stata utilizzata la versione 3.2.5 di JBoss. Copiare il contenuto dell'archivio nella directory /usr e creare la variabile di ambiente JBOSS_HOME alla quale assegniamo la directory di installazione. In alternativa a JBoss è comunque possibile utilizzare altri application server come Weblogic. 7) EJBCA (http://ejbca.sourceforge.net/) Copiare il contenuto dell'archivio in /usr/j2ee. Per creare il file ejbca-ca.ear (che verrà automaticamente copiato nell'opportuna directory e del quale verrà eseguito il deploy all'avvio di JBoss) eseguire: cd /usr/j2ee/ejbca ant replaceDS scegliere un database (es: mysql) e al secondo input.. java:/EjbcaDS ant deploy Nel caso in cui si utilizzi HypersoniqDS (il database di default di JBoss), l'input da inserire nel penultimo passaggio è java:/DefaultDS anziche java:/EjbcaDS 8) Operazioni conclusive Copiare il file *.jar del JDBC nella directory $JBOSS_HOME/server/default/lib/. Spostarsi nella directory /usr/j2ee/ejbca/ e copiare il file doc/mysql-ds.xml (nel caso in cui si utilizzi MySQL, altrimenti scegliere l'opportuno file *-ds.xml) in $JBOSS_HOME/server/default/deploy/ e modificare quest'ultimo nel seguente modo: ...... <datasources> <local-tx-datasource> <jndi-name>EjbcaDS</jndi-name> <connection-url>jdbc:mysql://localhost:3306/ejbca</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>ejbca</user-name> <password>ejbca</password> <!-- Eventualmente da adattare --> <min-pool-size>5</min-pool-size> <max-pool-size>20</max-pool-size> <idle-timeout-minutes>15</idle-timeout-minutes> <track-statements>true</track-statements> </local-tx-datasource> </datasources> ...... ATTENZIONE! Gli altri tipi di database potrebbero avere un file *-ds.xml con un numero minore di tag. Non aggiungerne nessuno. ATTENZIONE! Il jndi-name, username e password sono da modificare se sono stati cambiati dei valori nei passaggi precedenti (jndi-name: ant replaceDS; .. ; java:/EjbcaDS => EjbcaDS) Avviare JBoss: $JBOSS_HOME/bin/run.sh Avviare l'installazione di EJBCA: /usr/j2ee/ejbca/install.sh Durante l'installazione dovranno essere inseriti vari dati. Per quanto riguarda la policy id, va bene quella suggerita nell'esempio. Per quanto riguarda la password, è necessario annotarla infatti verrà richiesta quando si importerà il certificato nel browser. E' importante ricordare anche il valore CN del DN del certificato del server SSL, infatti tale valore deve essere inserito nel file degli host (es: /etc/hosts) al termine di install.sh. Avremo quindi che il file hosts avrà una riga simile alla seguente: 127.0.0.1 localhost linux kittmad.asl.it Terminare JBoss (è sufficiente chiudere la finestra di shell). Verificare che il file server.xml (presente nella directory $JBOSS_HOME/server/default/deploy/jbossweb-tomcat50.sar/) sia uguale al seguente (esclusi gli attributi keystorePass): <Server> <Service name="jboss.web" className="org.jboss.web.tomcat.tc5.StandardService"> <!-- A HTTP/1.1 Connector on port 8080 --> <Connector port="8080" address="${jboss.bind.address}" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" connectionTimeout="20000"cates that the issuance policy contains all other issuance policies. Typically, this object identifier is only assigned to CA certif disableUploadTimeout="true" /> <!-- A AJP 1.3 Connector on port 8009 --> <Connector port="8009" address="${jboss.bind.address}" enableLookups="false" redirectPort="8443" debug="0" protocol="AJP/1.3" /> <!-- A HTTPS Connector without client cert on port 8442 --> <Connector port="8442" address="${jboss.bind.address}" maxThreads="100" minSpareThreads="5" maxSpareThreads="15" scheme="https" secure="true" clientAuth="false" keystoreFile="${jboss.server.home.dir}/../../bin/tomcat.jks" keystorePass="nZbVDFjj" sslProtocol = "TLS" /> <!-- HTTPS Connector requiring client cert on port 8443 --> <Connector port="8443" address="${jboss.bind.address}" maxThreads="100" minSpareThreads="5" maxSpareThreads="15" scheme="https" secure="true" clientAuth="true" keystoreFile="${jboss.server.home.dir}/../../bin/tomcat.jks" keystorePass="nZbVDFjj" sslProtocol = "TLS" /> <Engine name="jboss.web" defaultHost="localhost"> <Logger className="org.jboss.web.tomcat.Log4jLogger" verbosityLevel="WARNING" category="org.jboss.web.localhost.Engine"/> <Realm className="org.jboss.web.tomcat.security.JBossSecurityMgrRealm" subjectAttributeName="j_subject" certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping" /> <Host name="localhost" autoDeploy="false" deployOnStartup="false" deployXML="false"> <DefaultContext cookies="true" crossContext="true" override="true" /> </Host> </Engine> </Service> </Server> attenzione in particolar modo ai tag: Service, Engine, Logger, Realm, Host e a quelli in più che devono essere rimossi (o messi sotto commento). Avviare JBoss. Importare nel browser il certificato presente nel file superadmin.p12 (nella directory / usr/j2ee/ejbca/p12/). [Facoltativo] Eseguire /usr/j2ee/ejbca/runtest.sh (Non deve verificarsi nessun errore [Failure]). Il test non dovrebbe essere eseguito nella stessa macchina dove verrà utilizzato ejbca infatti esso creerà dei record nel database 'ejbca'. Aprire il browser all'indirizzo https://localhost:8443/ejbca dove apparirà la pagina iniziale dove è possibile gestire la/le certification authority. 9) Appunti ATTENZIONE! Ad ogni avvio di JBoss non deve verificarsi nessun errore (ERROR). In caso di errore, consultare con attenzione il log di JBoss (quello della shell e quello del file server.xml), individuare il problema, terminare JBoss, risolvere il problema e riavviare JBoss. In caso di problemi con JBoss, leggere il log mostrato nella shell e nel caso in cui siano necessarie altre informazioni, riferirsi al file $JBOSS_HOME/server/default/log/server.log. E' possibile evitare di utilizzare la GUI a favore di comandi della shell. Può essere molto utile anche la documentazione del database utilizzato. In caso di problemi generali, consultare anche i file presenti nella directory doc di ejbca se necessario: HOWTO-database INSTALL-guide README HOWTO-LDAP FAQ Possono rivelarsi molto utili strumenti come Webmin. Appendice B – Codice C/Java Utils.h Header del file Utils.c. #ifndef __Utils__ #define __Utils__ #include <jni.h> JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved); JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved); void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg); char * JstrToCharp(JNIEnv *env, jstring str); jstring CharpToJstr(JNIEnv *env, const char *str); #endif Utils.c Alcune funzioni di varia utilità. #include "Utils.h" //#include <stdio.h> jclass Class_java_lang_String = NULL; jmethodID MID_String_getBytes = NULL; jmethodID MID_String_init = NULL; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env; if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { return JNI_ERR; } // Creating a global reference if (Class_java_lang_String == NULL) { jclass localRefCls = (*env)->FindClass(env, "java/lang/String"); if (localRefCls == NULL) { return JNI_ERR; // Exception thrown } Class_java_lang_String = (*env)->NewGlobalRef(env, localRefCls); (*env)->DeleteLocalRef(env, localRefCls); if (Class_java_lang_String == NULL) { return JNI_ERR; // OutOfMemory exception thrown } } // Initializating Method ID MID_String_getBytes = (*env)->GetMethodID(env, Class_java_lang_String, "getBytes", "()[B"); MID_String_init = (*env)->GetMethodID(env, Class_java_lang_String, "<init>", "([B)V"); if ( (MID_String_getBytes == NULL) || (MID_String_init == NULL) ) { return JNI_ERR; } return JNI_VERSION_1_4; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved) { JNIEnv *env; if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { return; } } void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) { jclass cls = (*env)->FindClass(env, name); if (cls != NULL) { (*env)->ThrowNew(env, cls, msg); } (*env)->DeleteLocalRef(env, cls); } char * JstrToCharp(JNIEnv *env, jstring str) { jbyteArray bytes = 0; jthrowable exc; char *result = 0; if ( (*env)->EnsureLocalCapacity(env, 2) < 0 ) { return 0; } //char buf[128] = "qqqqq"; //str = (*env)->NewStringUTF(env, buf); bytes = (*env)->CallObjectMethod(env, str, MID_String_getBytes); // Problema qui! exc = (*env)->ExceptionOccurred(env); if (!exc) { jint len = (*env)->GetArrayLength(env, bytes); result = (char *)malloc(len + 1); if (result == 0) { JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 0); (*env)->DeleteLocalRef(env, bytes); return 0; } (*env)->GetByteArrayRegion(env, bytes, 0, len, (jbyte *) result); result[len] = 0; } else { (*env)->DeleteLocalRef(env, exc); } (*env)->DeleteLocalRef(env, bytes); return result; } jstring CharpToJstr(JNIEnv *env, const char *str) { jstring result; jbyteArray bytes = 0; int len; if ( (*env)->EnsureLocalCapacity(env, 2) < 0 ) { return NULL; } len = strlen(str); bytes = (*env)->NewByteArray(env, len); if (bytes != NULL) { (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *)str); result = (*env)->NewObject(env, Class_java_lang_String, MID_String_init, bytes); return result; } return NULL; } Errors.h Signature generata dal compilatore Java. /* DO NOT EDIT THIS FILE - it is machine generated */ #ifndef __it_asl7_wOpenSSL_Errors__ #define __it_asl7_wOpenSSL_Errors__ #include <jni.h> #ifdef __cplusplus extern "C" { #endif extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error (JNIEnv *env, jobject); extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error_1line (JNIEnv *env, jobject); extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error_1line_1data (JNIEnv *env, jobject); extern jint Java_it_asl7_wOpenSSL_Errors_ERR_1get_1next_1error_1library (JNIEnv *env, jobject); extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1peek_1error (JNIEnv *env, jobject); extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1peek_1error_1line (JNIEnv *env, jobject); extern jlong Java_it_asl7_wOpenSSL_Errors_ERR_1peek_1error_1line_1data (JNIEnv *env, jobject); extern jstring Java_it_asl7_wOpenSSL_Errors_ERR_1error_1string (JNIEnv *env, jobject, jlong); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1error_1string_1n (JNIEnv *env, jobject, jlong); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1print_1errors (JNIEnv *env, jobject); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1print_1errors_1fp (JNIEnv *env, jobject); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1free_1strings (JNIEnv *env, jobject); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1clear_1error (JNIEnv *env, jobject); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1put_1error (JNIEnv *env, jobject, jint, jint, jint, jstring, jint); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1set_1error_1data (JNIEnv *env, jobject, jstring, jint); extern jstring Java_it_asl7_wOpenSSL_Errors_ERR_1lib_1error_1string (JNIEnv *env, jobject, jlong); extern jstring Java_it_asl7_wOpenSSL_Errors_ERR_1func_1error_1string (JNIEnv *env, jobject, jlong); extern jstring Java_it_asl7_wOpenSSL_Errors_ERR_1reason_1error_1string (JNIEnv *env, jobject, jlong); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1load_1strings (JNIEnv *env, jobject, jintArray, jobjectArray); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1load_1ERR_1strings (JNIEnv *env, jobject); extern void Java_it_asl7_wOpenSSL_Errors_ERR_1load_1crypto_1strings (JNIEnv *env, jobject); //extern void Java_it_asl7_wOpenSSL_Errors_ERR_1load_1SSL_1strings (JNIEnv *env, jobject); //extern void Java_it_asl7_wOpenSSL_Errors_ERR_1load_1error_1strings (JNIEnv *env, jobject); extern jstring Java_it_asl7_wOpenSSL_Errors_val_1error (JNIEnv *env, jobject); #ifdef __cplusplus } #endif #endif /* __it_asl7_wOpenSSL_Errors__ */ Errors.c Parte di libreria (un'interfaccia) che si frappone tra lo “strato” Java e la libreria C. #include #include #include #include <openssl/err.h> "Errors.h" "Utils.h" <stdio.h> // Debug char error[256]; JNIEXPORT jlong JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error (JNIEnv *env, jobject obj) { return ERR_get_error(); } JNIEXPORT jlong JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error_1line (JNIEnv *env, jobject obj) { //.. attenzione argomenti } JNIEXPORT jlong JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1get_1error_1line_1data (JNIEnv *env, jobject obj) { //.. attenzione argomenti } JNIEXPORT jint JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1get_1next_1error_1library (JNIEnv *env, jobject obj) { return ERR_get_next_error_library(); } JNIEXPORT jlong JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1peek_1error (JNIEnv *env, jobject obj) { return ERR_peek_error(); } JNIEXPORT jlong JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1peek_1error_1line (JNIEnv *env, jobject obj) { //.. attenzione argomenti } JNIEXPORT jlong JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1peek_1error_1line_1data (JNIEnv *env, jobject obj) { //.. attenzione argomenti } JNIEXPORT jstring JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1error_1string (JNIEnv *env, jobject obj, jlong num) { return CharpToJstr( env, ERR_error_string(num, error) ); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1error_1string_1n (JNIEnv *env, jobject obj, jlong num) { ERR_error_string_n((unsigned long)num, error, sizeof(char) * 256 ); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1print_1errors (JNIEnv *env, jobject obj) { //.. attenzione argomenti } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1print_1errors_1fp (JNIEnv *env, jobject obj) { //.. attenzione argomenti } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1free_1strings (JNIEnv *env, jobject obj) { ERR_free_strings(); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1clear_1error (JNIEnv *env, jobject obj) { ERR_clear_error(); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1put_1error (JNIEnv *env, jobject obj, jint lib, jint func, jint reason, jstring file, jint line) { ERR_put_error( lib, func, reason, JstrToCharp(env, file), line ); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1set_1error_1data (JNIEnv *env, jobject obj, jstring data, jint flags) { ERR_set_error_data( JstrToCharp(env, data), flags ); } JNIEXPORT jstring JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1lib_1error_1string (JNIEnv *env, jobject obj, jlong e) { return CharpToJstr( env, ERR_lib_error_string(e) ); } JNIEXPORT jstring JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1func_1error_1string (JNIEnv *env, jobject obj, jlong e) { return CharpToJstr( env, ERR_func_error_string(e) ); } JNIEXPORT jstring JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1reason_1error_1string (JNIEnv *env, jobject obj, jlong e) { return CharpToJstr( env, ERR_reason_error_string(e) ); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1load_1strings (JNIEnv *env, jobject obj, jintArray error, jobjectArray str) { //.. attenzione argomenti } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1load_1ERR_1strings (JNIEnv *env, jobject obj) { ERR_load_ERR_strings(); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1load_1crypto_1strings (JNIEnv *env, jobject obj) { ERR_load_crypto_strings(); } /* JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1load_1SSL_1strings (JNIEnv *env, jobject obj) { ERR_load_SSL_strings(); } JNIEXPORT void JNICALL Java_it_asl7_wOpenSSL_Errors_ERR_1load_1error_1strings (JNIEnv *env, jobject obj) { ERR_load_error_strings(); } */ JNIEXPORT jstring JNICALL Java_it_asl7_wOpenSSL_Errors_val_1error (JNIEnv *env, jobject obj) { return CharpToJstr( env, error ); } Errors.java Wrapper per le funzioni di gestione di errore della libreria OpenSSL. package it.asl7.wOpenSSL; /** * This class handle errors of the OpenSSL C libray using JNI (Java Native Interface) * @version 0.1 * @author Michele Accattoli */ public class Errors { //----- Constructors /** * Creates a new instance of Errors loading all libraries */ public Errors() { LoadCryptoStrings(); LoadERRStrings(); } //----- Methods /** * Delete all error from the static list */ public void ClearAll() { ERR_clear_error(); } /** * returns the error string corresponding to the numeric error code e in * buf if buf is not NULL, and returns a pointer to the message in either * case. If you haven't loaded up all of the error strings in advance, * the unpacked form of the error code will be returned in humanreadable * text, which is better than nothing. buf should be at least 120 bytes * long; the function uses sprintf and does not check for buffer overruns * @param num ... * @return ... */ public String ErrorString(int num) { return ERR_error_string(num); //test //return val_error(); } /** * */ public String ErrorStringN(int num) { ERR_error_string_n(num); return val_error(); } /** * frees all error strings loaded into the static error table that maps * library/function/reason codes to text strings. */ public void FreeStrings() { ERR_free_strings(); } /** * returns the name of the function corresponding to the numeric error * code e, or NULL if it can't find a match */ public String FuncErrorString(long e) { return ERR_func_error_string(e); } /** * * Get the Error Code and remove that error report (from the queue) * */ public long GetError() { return ERR_get_error(); } /** * return the line (and the source) where is the error * and remove the error report from the queue */ public long GetErrorLine(/**/) { return ERR_get_error_line(/**/); } /** * ..return also extra data and flags */ public long GetErrorLineData(/**/) { return ERR_get_error_line_data(/**/); } /* * returns the next unused number that can be used to register library * error codes. It uses a static variable to keep track. This function * is not called by *anything* at present and is either left over from * old code or part of something that hasn't been written yet. Someone * can tell me which someday */ public int GetNextErrorLibrary() { return ERR_get_next_error_library(); } /* * return the name of the library corresponding to the numeric error code * e, or NULL if it can't find a match. */ public String LibErrorString(long e) { return ERR_lib_error_string(e); } /* * Load the "crypto" set of error strings */ public void LoadCryptoStrings() { ERR_load_crypto_strings(); } /* * Load "crypto" and "SSL" sets of error strings * public void LoadErrorStrings() { ERR_load_error_strings(); } */ /* * initializes arrays for the error-handling library with messages * specific to the ERR library. Call this before using any ERR routines * in your code. */ public void LoadERRStrings() { ERR_load_ERR_strings(); } /* * Load the "SSL" set of error strings * public void LoadSSLStrings() { ERR_load_SSL_strings(); } */ /* * loads up for lib the array of ERR_STRING_DATA pointed to by err. It * also does ERR_load_ERR_strings as a special bonus */ public void LoadStrings(ErrStringData[] str) { int[] error = new int[str.length]; String[] message = new String[str.length]; for (int i = 0; i < str.length; i++) { error[i] = str[i].getError(); message[i] = str[i].getString(); } ERR_load_strings(error, message); } /** * * Get the Error Code but remove that error report (from the queue) * */ public long PeekError() { return ERR_peek_error(); } /** * return the error code * but it don't remove the error report from the queue */ public long PeekErrorLine(/**/) { return ERR_peek_error_line(/**/); } /** * */ public long PeekErrorLineData(/**/) { return ERR_peek_error_line_data(/**/); } /* * does what ERR_print_errors_fp does except that it uses a BIO instead of a file pointer. In fact, ERR_print_errors_fp calls this function */ public void PrintErrors(/**/) { ERR_print_errors(/**/); } /* * prints out all errors to the file fp in the static error list in human-readable form. The errors are removed from the static buffer as they are printed. */ public void PrintErrorsFp(/**/) { ERR_print_errors_fp(/**/); } /* * adds the error contained in line, numeric error code reason, from the function func and the library lib, to a static list of errors. This static list can only contain 16 errors, so if you add one more then the oldest error gets overwritten. */ public void PutError(int lib, int func, int reason, String file, int line) { ERR_put_error(lib, func, reason, file, line); } /* * returns the text error message corresponding to the numeric error code e, or NULL if it can't find a match */ public String ReasonErrorString(long e) { return ERR_reason_error_string(e); } /* * sets the data and flags subfields of the top entry in the static list of errors; these don't get set with ERR_put_error, which sets all the *other* subfields */ public void SetErrorData(String data, int flags) { ERR_set_error_data(data, flags); } //----- Native functions private private private private native native native native long ERR_get_error(); long ERR_get_error_line(/*pag 79*/); long ERR_get_error_line_data(/*pag 79*/); int ERR_get_next_error_library(); private native long ERR_peek_error(); private native long ERR_peek_error_line(/*pag 79*/); private native long ERR_peek_error_line_data(/*pag 79*/); private native String ERR_error_string(long num); private native void ERR_error_string_n(long num); private native void ERR_print_errors(/*Bio *fp*/); private native void ERR_print_errors_fp(/*FILE *fp*/); private native /**/void ERR_free_strings(); private native void ERR_clear_error(); private native void ERR_put_error(int lib, int func, int reason, String file, int line); private native void ERR_set_error_data(String data, int flags); private native String ERR_lib_error_string(long e); private native String ERR_func_error_string(long e); private native String ERR_reason_error_string(long e); //private native void ERR_add_error_data( VAR_PLIST(int, num) ); private native void ERR_load_strings(int[] error, String[] str); private native void ERR_load_ERR_strings(); private native void ERR_load_crypto_strings(); //private native void ERR_load_SSL_strings(); //private native void ERR_load_error_strings(); //--- Thread & locking ---------------------------\\ //private native ERR_STATE *ERR_get_state(); //private native LHASH *ERR_get_err_state_table(); //private native void ERR_remove_state(long pid); //private native LHASH *ERR_get_string_table(); //************************************************\\ // Getting values.. private native String val_error(); //----- Consts //----- Data private String error = ""; static { //System.loadLibrary("errors"); Runtime.getRuntime().load("/usr/lib/liberrors.so"); } //----- Testing.. public static void main(String []args) { Errors err = new Errors(); err.ClearAll(); for (int i = 0; i < 50; i++) { System.out.println(err.ErrorString(i)); // reinserire System.out.println(err.error); } } //----- End test! } /** * This class is the equivalent of the ERR_STRING_DATA struct of the OpenSSL C library * it is needed to getting some values. * @version 0.1 * @author Michele Accattoli */ class ErrStringData { /** * Default constructor */ ErrStringData() { } public void setValues(int err, String st) { error = err; str = st; } public int getError() { return error; } public String getString() { return str; } /** * Type of error */ public int error; /** * The human readable error message */ public String str; } ///:~ test.c Piccolo programma scritto per verificare il funzionamento di alcune chiamate di funzioni Java sotto C. #include <stdio.h> #include <jni.h> #include "All.h" #include "Utils.h" #include "Errors.h" int main(void) { printf("Inizio\n\n"); //.. JNIEnv *env; JavaVM *jvm; jint res; jclass cls; jmethodID mid; jstring jstr; jclass stringClass; jobjectArray args; char *asda; char buf[128] = "qwerty"; JavaVMInitArgs vm_args; JavaVMOption options[1]; options[0].optionString = "-Djava.class.path=."; vm_args.version = 0x00010004; vm_args.options = options; vm_args.nOptions = 1; vm_args.ignoreUnrecognized = JNI_TRUE; res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args); /* jobject obj = NULL; Java_it_asl7_wOpenSSL_Errors_ERR_1free_1strings (env, obj); ERR_free_strings(); */ jstring str = (*env)->NewStringUTF(env, buf); asda = JstrToCharp(env, str); printf("\n\n\n"); return 0; } Bibliografia Libri Bruce Schneier, “Sicurezza Digitale”, Tecniche nuove 2001 Bruce Schneier, “Appied Cryptography”, John Wiley & Sons 1996 B. Chandra, M. Messier, J. Viega, “Network Security with OpenSSL” O'Reilly 2002 Documentazioni Documentazione Java (java.sun.com) [JDK, JNI] Documentazione OpenSSL RFC 2510: Internet X.509 Public Key Infrastructure - Certificate Management Protocols RFC 2559: Internet X.509 Public Key Infrastructure - Operational Protocols – LDAPv2