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