You are here: Home>Blog>BlogPost>Uso_pratico_di_InlineJava_j4sign__Bouncy_Castle_Crypto_APIs (10 Oct 2012, GuidoBrugnara)EditAttach

Uso pratico di Inline::Java, j4sign & Bouncy Castle Crypto APIs

07 Jul 2012 - 12:43:17 by Guido Brugnara in Mason

In Italia dal 2011 le applicazioni che implementano meccanismi di firma digitale a valore legale devono supportare SHA 256 e il recente formato per imbustare documenti con marcatura temporale (RFC 5544).

Sembra che l'unica libreria Open Source che implementi la RFC 5544 siano le classi Java del progetto The Legion of the Bouncy Castle a loro volta utilizzate dal progetto j4sign (An open, multi-platform digital signature solution.).

Per utilizzare delle classi Java dall'ambiente di sviluppo HTML::MASON è possibile fare uso di Inline::Java che istanzia una JVM richiamabile dai processi Apache dell'applicazione.

Vediamo quindi come utilizzare le classi di Bouncy Castle e di j4sign e per effettuare la verifica della validità di documenti firmati e marcati digitalmente con le attuali regole italiane, opportunamente modificate per utilizzarle direttamente da Perl in ambiente Apache & mod_perl sfruttando le potenzialità di Inline::Java.

Deploy classi Java

Nota: Attualmente (8 Ott 2012) è disponibile la versione 0.2.1 di "verifica-firma", ma la path fornita e stata generata partendo dalla versione 0.2.0

mkdir -p /opt/my_application/src/java
cd /opt/my_application/src/java
wget http://sourceforge.net/projects/j4sign/files/verifica-firma/0.2.0/verifica-firma-0.2.0-src.zip
unzip verifica-firma-0.2.0-src.zip
mv verifica-firma-0.2.0-src verifica-firma-0.2.0-src-GDO

  • Applicare la Patch verifica-firma-0.2.0-src.diff alla cartella verifica-firma-0.2.0 :
    • Classi modificate:
      • it/trento/comune/j4sign/verification/RootsVerifier.java - init dei parametri come parametri passati alla chiamata del metodo getInstance
      • it/trento/comune/j4sign/verification/RootsVerifier.java - aggiunti parametri cnipaDir, cnipaCa, cnipaRoots al metodo RootsVerifier ed eliminati i riferimenti alla classe ...Configuration
      • it/trento/comune/j4sign/verification/X509CertRL.java - commentata riga "import org.apache.commons.configuration.!ConfigurationException;" - classe non utilizzata
      • it/trento/comune/j4sign/verification/CertValidity.java - aggiunto parametro Date ai metodi getExpired e getPassed per indicare la data di verifica della validità del certificato
      • it/trento/comune/j4sign/verification/VerifyResult.java - aggiunto parametro Date al metodo getPassed per indicare la data di verifica della validità dei certificati
      • it/trento/comune/j4sign/verification/servlet/Verifier.java - aggiunto parametro Date al metodo verify per indicare la data di verifica della validità dei certificati
    • Classe aggunta:
      • it/trento/comune/j4sign/verification/servlet/VerifyService.java

wget http://www.leader.it/pub/Blog/Uso_pratico_di_InlineJava_j4sign__Bouncy_Castle_Crypto_APIs/verifica-firma-0.2.0-src.diff
cd verifica-firma-0.2.0-src-GDO
# applica le modifiche ...
#   N.B. file generato con: diff -rupN verifica-firma-0.2.0-src verifica-firma-0.2.0-src-GDO >verifica-firma-0.2.0-src.diff
patch -p1 < ../verifica-firma-0.2.0-src.diff

  • Copia classi java compilate, necessarie, nella cartella /opt/my_application/lib/java

Nota: Nel sito ufficiale di Bouncy Castle la versione jdk16-146 non è più disponibile nel sito Web ma le classi si possono reperire su altri siti in Rete oppure dal server FTP ftp://ftp.bouncycastle.org/pub/release1.46/ (attenzione che non supporta i collegamenti in "passive mode").

mkdir -p /opt/my_application/lib/java
cd /opt/my_application/lib/java
cp -a /opt/my_application/src/java/verifica-firma-0.2.0-src-GDO/lib/j4sign-core.jar ./
wget --no-passive-ftp ftp://ftp.bouncycastle.org/pub/release1.46/bcmail-jdk16-146.jar
wget --no-passive-ftp ftp://ftp.bouncycastle.org/pub/release1.46/bcpg-jdk16-146.jar
wget --no-passive-ftp ftp://ftp.bouncycastle.org/pub/release1.46/bcprov-jdk16-146.jar
wget --no-passive-ftp ftp://ftp.bouncycastle.org/pub/release1.46/bctest-jdk16-146.jar
wget --no-passive-ftp ftp://ftp.bouncycastle.org/pub/release1.46/bctsp-jdk16-146.jar

  • Compilazione delle classi Java

cd /opt/my_application/src/java/verifica-firma-0.2.0-src-GDO
sh compila_java.sh

Verrà generata la classe nel file /opt/my_application/lib/java/VerifyService.jar

File CA Root

Per la verifica della firma è necessario fornire l'elenco delle CA accreditate al Cnipa (Ora DigitPA)

mkdir -p /opt/my_application/etc/CNIPA
cd /opt/my_application/etc/CNIPA
wget http://www.cnipa.gov.it/site/_files/LISTACER_20120911.zip.p7m
wget http://sourceforge.net/projects/j4sign/files/digitpa-certs/Certificati.zip

Esempio di utilizzo

File di configurazione

  • Nel file di configurazione di apache aggiungere:

# Parametri configurazione verify_service_java (vedi /etc/init.d/verify_service_java)
#
# Cartella usata da Inline::Java
PerlSetVar VerifyService_directory "/opt/my_application/lib/java/InlineDir"
PerlSetVar VerifyService_classPath "/opt/my_application/lib/java/VerifyService.jar:/opt/my_application/lib/java/bcprov-jdk16-146.jar:/opt/my_application/lib/java/bcmail-jdk16-146.jar:/opt/my_application/lib/java/bctsp-jdk16-146.jar"
PerlSetVar VerifyService_confDir "/opt/my_application/etc"
# Cartella in "VerifyService_confDir" contenente i certificati 
PerlSetVar VerifyService_cnipaDir "CNIPA"
# CA Root
PerlSetVar VerifyService_cnipaCa "DigitPA.cer"
# Elenco delle Root CA accreditate
PerlSetVar VerifyService_cnipaRoots "LISTACER_20101129.zip.p7m"
# L'impronta corrisponde a quella pubblicata nella Gazzetta Ufficiale n. 122 del 27 maggio 2010 (vedi es. http://gazzette.comune.jesi.an.it/2010/122/2.htm )
PerlSetVar VerifyService_fingerprintDigitPA "20EA4FB83593DB5C4407A538C920EBC3C5B1559C"
PerlSetVar VerifyService_validityMPMdate "2011-07-01 00:00:00.000 +0200"

Applicazione di esempio

  • Cartelle usate da Inline::Java

mkdir -p /opt/my_application/lib/java/InlineDir
mkdir -p /opt/my_application/htdocs/_Inline

  • Codice di upload del file firmato e marcato digitalmente

<FORM ACTION="./firma/put_firmato.html" target="_blank" name="my_file" METHOD="POST" ENCTYPE="multipart/form-data">
    <SPAN ID="ArchiviazioneFileFirmato">
      Path file: <input type="file" name="file_firmato" lenght=25>
      <INPUT TYPE="SUBMIT" VALUE="Archiviazione File Firmato">
    </SPAN>
  </FORM>

File /firma/put_firmato.html

Nel file vengono definite le quey SQL:
  • per estrarre l'elenco dei firmatari del documento da verificare
  • per verificare se nel database è presente un documento con la hash corrispondente al file da verificare
  • la query per archiviare il file firmato.

<BR>
<BLOCKQUOTE>
<%flags>
  inherit => '/firma/put_firmato.comp'
</%flags>
<%perl>
  $m->clear_buffer;
  $m->call_next;
</%perl>
%# Nome del campo contenente il file scaricato
<%method Field_filename>file_firmato</%method>
%# Firme da cercare nel file firmato
<%method Query_firme>
    select distinct cognome, nome, codice_fiscale
        from documenti
        inner join firmatari_documenti on firmatari_documenti.id_documento =
documenti.id
        inner join anagrafiche on firmatari_documenti.id_firmatario =
anagrafiche.id 
        where documenti.hash_file = ?
</%method>
%# Per verificare se nel DB e' presente la hash calcolata
<%method Query_archivio>
  select codice, marca_temporale
  from documenti
  where hash_file = ?;
</%method>
%# Per aggiornare l'archivio caricando il file firmato
<%method Query_update_archivio>
  update documenti 
  set tsd = ?,   
      file_firmato = ?,
      file_originale = null,
      marca_temporale = ?,  
      stato_rapporto = 'firmato'
   where hash_file = ?;
</%method>

Il file put_firmato.html fa riferimento al file put_firmato.comp nel quale sono implementate le procedure di verifica della firma

File /firma/put_firmato.comp

Di seguito si commentano le parti riguardanti l'interazione con le classi Java:

Attivazione del servizio java per la verifica della firma

Viene istanziata una Java Virtual Machine condivisa tra tutti i processi Apache:

use Inline (
    Java => 'STUDY',
    SHARED_JVM => 1,
    START_JVM => 0, 
    PORT => 7893,   
    AUTOSTUDY => 1, 
    # DEBUG => 2,   
    DIRECTORY => $r->dir_config('VerifyService_directory'),
    STUDY => ['it.trento.comune.j4sign.verification.servlet.VerifyService'],
    CLASSPATH => $r->dir_config('VerifyService_classPath')
  );

  • STUDY & AUTOSTUDY - Permettono di inglobare nel codice Perl le chiamate ai metodi delle classi Java elencate
  • SHARED_JVM - istanzia una sola JVM condivisa tra tutti i processi Apache
  • CLASSPATH - Percorso di ricerca delle classi Java

Inizializzazione della classe nella Java Virtual Machine condivisa, con i parametri di configurazione

I parametri di configurazione vengono letti dal file di configurazione di Apache.

my $VerifyService = new HTML::Mason::Commands::it::trento::comune::j4sign::verification::servlet::VerifyService(
    $r->dir_config('VerifyService_confDir'),
    $r->dir_config('VerifyService_cnipaDir'),
    $r->dir_config('VerifyService_cnipaCa'), 
    $r->dir_config('VerifyService_cnipaRoots'),
    $r->dir_config('VerifyService_fingerprintDigitPA')
  );

Verifica nel vecchio formato m7m

A seconda dell'estensione del file scaricato viene utilizzato un diverso codice per la lettura dei file.

Nel caso il formato del file sia m7m vengono estratti file firmato e pmarcatura temporale, contenuti nel file in formato MIME:
#Parser file MIME 
my $parser = new MIME::Parser;
mkdir "$tmpDir/mime_dir" || die "Errore creazione dir $tmpDir/mime_dir: $!\n";
$parser->output_dir("$tmpDir/mime_dir");
. . . . . . . .
      my $entity = $parser->parse_open($sfile);
      $fileP7M = $entity->parts(0)->{ME_Bodyhandle}->{MB_Path};
      my $fileTSR = $entity->parts(1)->{ME_Bodyhandle}->{MB_Path};
      if ($entity->parts != 2 || $fileP7M !~ m/\.p7m$/i || $fileTSR !~ m/\.tsr$/i) {
        $m->out("<b>Era atteso un file in formato MIME contenente un file .p7m e un file con estensione .tsr ($filename)</b><BR>");
        fine();
      }
      # data di applicazione della marcatura temporale (per la verifica della validità delle firme)
      my $token = $VerifyService->verifyTSR($fileTSR); # class org.bouncycastle.tsp.TimeStampToken
      # class org.bouncycastle.util.Store restituito chiamando token.getCertificates
      my $certificates = $token->getCertificates->getMatches(undef)->toArray;
      foreach my $cert (@$certificates){
        # class 
        $m->out('Timestamp emesso da: '.$cert->getIssuer->toString);
      }
      $timestampDate = $token->getTimeStampInfo->getGenTime;

Verifica nel nuovo formato tsd

Se il file è invece nel nuovo formato tsd si fa ricorso del metodo java parseTSD:

# il file è nel formato p7m TSD?
      my $res = $VerifyService->parseTSD($sfile);
      $fileP7M = "$sfile.p7m";
      if(!$VerifyService->validateTSD($fileP7M)){ # nome del file contenuto nella busta, da salvare      
        $m->out("<b>Il file $sfile non contiene un certificato .p7m marcato temporalmente (TSD).</b><br>\n");
        fine();
      }
      my $tokens = $VerifyService->tokensTSD;
      foreach my $token (@$tokens){
        my $certificates = $token->getCertificates->getMatches(undef)->toArray;
        foreach my $cert (@$certificates){
          $m->out('Timestamp emesso da: '.$cert->getIssuer->toString);
        }
        $timestampDate = $token->getTimeStampInfo->getGenTime;
        last;
      }
      $tsd = 1;

Conversioni e test sulla data

Per entrambi i formati m7m e tsd viene effettuata la verifica di validità della marcatura temporale rapportata al limite temporale di validità delle firma applicate:

# conversioni e test sulla data
    my $date_string = $timestampDate->toString;
    my $date_sec = str2time($date_string);
    my($sec,$min,$hour,$mday,$mon,$year) = localtime($date_sec);
    my $date_iso = sprintf('%04i-%02i-%02i %02i:%02i:%02i', 1900+$year, $mon+1, $mday, $hour, $min, $sec);
    $m->out(" il giorno: $date_iso<BR>\n");
    if(!$tsd){
      # limite di validità della data per il formato M7M
      my $date_limit = $r->dir_config('VerifyService_validityMPMdate');
      my $date_limit_sec = str2time($date_limit);
      if($date_sec >= $date_limit_sec){
        $m->out("<b>La data della marcatura temporale eccede il limite ($date_limit) concordato per il formato M7M</b><br>\n");
        next;
      }      
    }        

Verifica delle firme nel file firmato

Si verifica inoltre che le firme siano valide alla data della marcatura temporale:

# verifica delle firme nel file firmato
    my $Risultati = $VerifyService->verifyP7M($fileP7M, $timestampDate);
    # salvataggio del file firmato
    $VerifyService->saveContent("$fileP7M.content");
    # slurp file  
    open SLURP, "<$fileP7M.content" || die "$! file $fileP7M.content\n";
    my $let_file = do{local $/; <SLURP>};
    close SLURP;
    # codifica del file nel formato base64    
    $let_file = encode_base64($let_file);     
    # Calcola hash sul file letto
    my $hash = Digest::SHA1::sha1_hex($let_file, "password");

Individuata l'impronta del file viene verificato che lo stesso sia presente in archivio:

# Verifica se nel DB e' presente la hash calcolata
    $sth_archivio->execute($hash);  
    my $file_sconosciuto = 1;
    while(my $archivio = $sth_archivio->fetchrow_hashref){
      my $update_nuovo_file = 1;
      $file_sconosciuto = 0;
      if($archivio->{marca_temporale}){
        $m->out("<B>Un file firmato è già presente in archivio con il codice $archivio->{'codice'}</B><BR>\n");
        $update_nuovo_file = 0;
      }else{
        my %lista_firme_verifica;
        if($sth_firme){

Verifico che i soggetti firmatari coincidano con i firmatari dichiarati in archivio utilizzando il Codice Fiscale:

$sth_firme->execute($hash, $hash, $hash, $hash);
          while(my $row = $sth_firme->fetchrow_hashref) { 
            $lista_firme_verifica{uc $row->{codice_fiscale}} = $row;
          }
        }  
        # lettura e confronto dei risultati
        foreach my $result (@{$Risultati->values->toArray}){
          my $cert = $result->getCert; # class org.bouncycastle.jce.provider.X509CertificateObject
          my $X500str = $cert->getSubjectX500Principal->toString; # class javax.security.auth.x500.X500Principal
          my $Subject = SubjectX500stringToHash($X500str);
          $m->out("Firmato da: $X500str<BR>\n");
          if(!$result->isPassed){
            $m->out("<b>Test validità della firma fallito! ( range date ".$cert->getNotBefore->toString." - ".$cert->getNotAfter->toString.")</b><BR>\n");
            $update_nuovo_file = 0;
            next;
          }
          # confronto degli attributi dei soggetti firmatari
          my $CF; # Codice Fiscale
          if($Subject->{SERIALNUMBER}){
            $CF = $Subject->{SERIALNUMBER};
            $CF =~ s/^IT://;
          }elsif($Subject->{CN}){
            my @fields =  split /\//, $Subject->{CN}, 4;
            $CF = $fields[2];
          }
          my $firma = $lista_firme_verifica{uc $CF};
          if($firma){
            # firmatario presente
            # verifico che nome e cognome corrispondano
            if(uc $Subject->{GIVENNAME} ne uc $firma->{'nome'} ||
               uc $Subject->{SURNAME} ne uc $firma->{'cognome'}){
              $m->out("<b>Test nominativo non corrispondente: ( $Subject->{GIVENNAME} $Subject->{SURNAME} <> $firma->{'nome'} $firma->{'cognome'} )</b><BR>\n");
              $update_nuovo_file = 0;
              next;
            }
            # lo tolgo dalla lista per verificare poi le firme mancanti
            delete $lista_firme_verifica{uc $CF};
          }else{
            # firmatario non previsto: lo ignoro
          }
        }  
        # verifica firme mancanti
        foreach my $CF (keys %lista_firme_verifica){
          $update_nuovo_file = 0;
          my $sogg = $lista_firme_verifica{$CF};
          $m->out("<B>Manca la firma di $sogg->{nome} $sogg->{cognome} - C.F. $CF</B><BR>\n");
        }        
      }          
      if($update_nuovo_file){
        # aggiorno l'archivio con il file firmato e marcato temporalmente
        open FIRMATO, "<$sfile" || die "$! file $sfile\n";
        my $firmato = do{local $/; <FIRMATO>};
        close FIRMATO;                
        $sth_update_archivio->execute($tsd, encode_base64($firmato), $timestampDate->toString, $hash);
        $dbh->commit();
        $file_sconosciuto = 0;
        $m->out("<B>Verifica e archiviazione file firmato eseguita con successo</B> ($filename)<BR>");
      }
    }  

Link:

Copyright (©)

Comments (edit)

 

Topic attachments
I Attachment Action Size Date Who Comment
put_firmato.compcomp put_firmato.comp manage 12.1 K 07 Jul 2011 - 14:09 GuidoBrugnara File put_firmato.comp
put_firmato.htmlhtml put_firmato.html manage 1.2 K 07 Jul 2011 - 14:09 GuidoBrugnara File put_firmato.html
verifica-firma-0.2.0-src.diffdiff verifica-firma-0.2.0-src.diff manage 62.3 K 07 Jul 2011 - 11:47 GuidoBrugnara Patch Verifica Firma
Topic revision: r3 - 10 Oct 2012, GuidoBrugnara
 
This site is powered by FoswikiCopyright © Leader.IT - Italy P.I. IT01434390223 Privacy policy & use of cookies