Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

SSL Engine per la sicurezza in Java

Analizziamo le basi per creare un programma Java che utilizzi due Thread per simulare Client e Server di una comunicazione sicura basata su DTLS e SSL Engine
Analizziamo le basi per creare un programma Java che utilizzi due Thread per simulare Client e Server di una comunicazione sicura basata su DTLS e SSL Engine
Link copiato negli appunti

Le API JSSE, presenti nei package javax.net e javax.net.ssl forniscono le seguenti funzionalità:

  • Socket sicure: comunicazione Client/Server.
  • Un non-blocking engine: produzione e consumo di stream basati sui protocolli SSL/TLS/DTLS (SSLEngine).
  • Factories: creazione di sockets, server sockets, SSL sockets e SSL server sockets.
  • Una classe rappresentante un contesto di sicurezza per un socket.
  • Interfacce Key e trust manager.
  • Una classe per le connessioni HTTPS.

La classe javax.net.ssl.SSLEngine rappresenta il core delle API incapsulando una macchina a stati finiti per SSL/TLS/DTLS. La classe SSLEngine rappresenta l'engine SSL/TLS/DTLS che si colloca tra lo strato applicativo e quello di trasporto della pila protocollare di Internet, fornendo il servizio di cifratura ed integrità dei dati:

Un'istanza della classe SSLEngine può trovarsi in uno dei seguenti stati:

Stato Descrizione
Creation L'SSLEngine è stato creato e inizializzato ma non ancora utilizzato.
Initial handshaking L'handshake iniziale è una fase dove le entità coinvolte si scambiano messaggi per realizzare operazioni quali lo scambio dei certificati e l'autenticazione di una o entrambe le parti coinvolte. I dati applicativi possono essere scambiati solo se l'handshake si conclude positivaente.
Application Data In questa fase avviene lo scambio dei dati applicativi.
Rehandshaking Entrambi le parti coinvolte nella comunicazione possono richiedere una rinegoziazione durante lo stato Application Data.
Closure Quando la connessione non è più necessaria è possibile chiudere l'engine SSL.

Con i concetti esposti siamo in grado di utilizzare le API per creare un programma che utilizzi due Thread per simulare il Client ed il Server di una comunicazione sicura basata sul protocollo DTLS (SSL over Datagram). L'unico aspetto che dobbiamo tener presente, rispetto al classico uso di SSL over TCP, è che con DTLS non abbiamo, a livello di trasporto, un affidabile trasferimento dei dati. Questa caratteristica impone di inserire, all'interno del codice un meccanismo di ritrasmissione dei dati quando necessario.

Iniziamo con il creare la classe principale DtlsDatagramDemo.java inserendo in essa i primi metodi per la costruzione di un SSLContext:

import java.io.FileInputStream;
import java.net.*;
import java.nio.ByteBuffer;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import javax.net.ssl.*;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
import static javax.net.ssl.SSLEngineResult.Status.*;
public class DtlsDatagramDemo {
    private final String dtls1 = "DTLSv1.0";
    private final String dtls12 = "DTLSv1.2";
    ..
    private SSLContext getClientDTLSContext() throws Exception {
        KeyStore ks = KeyStore.getInstance("JKS");
        KeyStore ts = KeyStore.getInstance("JKS");
        char[] password = "password".toCharArray();
        ks.load(new FileInputStream("c:\\cert\\client\\local\\client.jks"), password);
        ts.load(new FileInputStream("c:\\cert\\client\\others\\server.jks"), password);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
        kmf.init(ks, password);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
        tmf.init(ts);
        SSLContext sslCtx = SSLContext.getInstance(dtls1);
        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        return sslCtx;
    }
    private SSLContext getServerDTLSContext() throws Exception {
        KeyStore ks = KeyStore.getInstance("JKS");
        KeyStore ts = KeyStore.getInstance("JKS");
        char[] password = "password".toCharArray();
        ks.load(new FileInputStream("c:\\cert\\server\\local\\server.jks"), password);
        ts.load(new FileInputStream("c:\\cert\\server\\others\\client.jks"), password);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
        kmf.init(ks, password);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
        tmf.init(ts);
        SSLContext sslCtx = SSLContext.getInstance(dtls1);
        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        return sslCtx;
    }

SSLContext rappresenta il contesto di configurazione dal quale è possibile ottenere un SSLEngine. Attraverso di essa possiamo impostare diversi parametri. Nel codice presentato, definiamo un keystore ed un truststore di tipo JKS sia per il client che per il server.

Un keystore è un file con estensione JKS contenente informazioni sulla chiave privata e chiave pubblica con certificato. Per il nostro programma dimostrativo non possiamo avvalerci di una CA, ragion per cui genereremo un keystore, sia per il client che per il server, di tipo self-signed (autofirmato). Questa operazione ci consente di testare il funzionamento dell'SSLEngine considerando sicuri, nel nostro programma, i certificati da noi firmati.

Generare un keystore è relativamente semplice: è sufficiente utilizzare l'utility keytool da console Windows per la creazione del certificato appartenente al client:

keytool -genkey -keyalg RSA -alias client -keystore c:\cert\client\local\client.jks -storepass password -validity 360 -keysize 2048

e successivamente quello appartenente al server dopo aver creato preliminarmente i path che conducono ai file JKS:

keytool -genkey -keyalg RSA -alias server -keystore c:\cert\client\others\server.jks -storepass password -validity 360 -keysize 2048

Mentre un keystore è utilizzato da un entità comunicante come database dal quale reperire certificati da inviare quando richiesto (generalmente per autenticarsi), un truststore contiene invece certificati di terze parti che l'entità utilizzerà per autenticare il lato opposto coinvolto nella comunicazione.

Per il nostro programma dimostrativo, client.jks rappresenta il keystore del client mentre server.jks il suo truststore. Possiamo utilizzare in modo inverso gli stessi certificati lato server: dal punto di vista del server server.jks rappresenta il keystore mentre client.jks il suo trustsore.

Come mostra il codice del metodo getServerDTLSContext() per il server abbiamo utilizzato percorsi diversi per i file JKS per evidenziare meglio quanto appena detto. I metodi getClientDTLSContext() e getServerDTLSContext() svolgono il medesimo lavoro: recuperare i file di keystore e truststore, costruire un KeyManagerFactory ed un TrustManagerFactory, costruire un SSLContext scegliendo la versione DTLS desiderata e, infine, inizializzare il contesto prima di restituirlo.

Queste operazioni creano un contesto SSL in cui sono specificati i certificati che client e server utilizzeranno durante la comunicazione.

Il lavoro appena svolto ci consente di creare un SSL Engine per il client ed uno per il server:

private SSLEngine getClientSSLEngine() throws Exception {
        SSLContext context = getClientDTLSContext();
        SSLEngine engine = context.createSSLEngine();
        SSLParameters paras = engine.getSSLParameters();
        paras.setMaximumPacketSize(1024);
        engine.setUseClientMode(true);
        engine.setSSLParameters(paras);
        return engine;
    }
    private SSLEngine getServerSSLEngine() throws Exception {
        SSLContext context = getServerDTLSContext();
        SSLEngine engine = context.createSSLEngine();
        engine.setNeedClientAuth(true);
        SSLParameters paras = engine.getSSLParameters();
        paras.setMaximumPacketSize(1024);
        engine.setUseClientMode(false);
        engine.setSSLParameters(paras);
        return engine;
    }

Nel codice notiamo la chiamata preliminare ai metodi di creazione del contesto e la creazione dell'SSLEngine attraverso createSSLEngine(). Di particolare interesse è l'uso del metodo setUseClientMode(boolean) che consente la creazione di un SSLEngine di tipo client(valore true) o server (valore false).

Per un engine di tipo server possiamo specificare la richiesta di autenticazione per il client attraverso il metodo setNeedClientAuth(boolean). Con l'impostazione al valore true per il parametro, il server farà fallire la procedura di handshake se il client non fornirà un certificato valido.

Il codice proposto può essere integrato analizzando la macchina a stati che implementa la procedura di handshake, ma questo sarà il tema di un prossimo approfondimento.


Ti consigliamo anche