Per instaurare una connessione sicura SSL è necessario disporre degli archivi dei certificati utili a fornire e verificare l’identità degli attori, questi archivi sono il keystore (che contiene anche una chiave privata) e il truststore. Nella SE viene fornito un tool, keytool, utile a generare questi archivi e relativi certificati.
In questa lezione si descriveranno le funzioni principali del tool e si mostrerà come generare e popolare gli archivi. Inoltre, poiché in alcune situazioni non è possibile utilizzare direttamente il keytool (ad esempio sono già presenti in forma separata una chiave privata e un certificato), si descriveranno i passi base per installare ed utilizzare OpenSSL, un tool che ci permetterà di generare un archivio in un altro formato, PKCS12, da cui il keytool è in grado di ricavare un archivio in formato JKS, formato di riferimento per la gestione della sicurezza nella libreria JSSE.
Keytool
Keytool è un tool contenuto nella Java Standard Edition per la gestione di chiavi e certificati. Consente di generare e amministrare chiavi pubbliche e private e certificati associati per utilizzarli in modalità self-signed se impiegati in un contesto users/services, o per l’integrità dei propri dati e servizi di autenticazione utilizzando firme digitali. Inoltre permette la cache della chiave pubblica sotto forma di certificato in comunicazioni tra peers. Keytool consente poi di amministrare chiavi segrete utilizzate in algoritmi di criptografia simmetrica come DES e di registrare chiavi e certificati in archivi quali keystore o trustore. E’ utile creare una cartella apposita per eseguire qualche test e prendere dimestichezza con i comandi a console. I comandi verranno presentati su console Windows, semplicemente trasponibili su Unix/Linux.
Generazione del keystore
Avviato un nuovo terminale e ci spostiamo in una cartella di test, nel nostro caso TestKeytool
(es. cd C:\Tests\TestKeytool). Per generare la chiave pubblica/privata utilizzeremo il comando -genkey
fornendo alcune opzioni per specificare un alias per la chiave (-alias
), quale algoritmo utilizzare per generare la chiave (-keyalg
), una password per proteggere la chiave (-storepass
, almeno sei caratteri), in quale archivio memorizzare la chiave (-keystore
), la password per proteggere l’archivio (-keypass
, almeno sei caratteri):
keytool -genkey -alias serverkey -keyalg RSA -storepass password -keystore server.jks -keypass password
Si andrà a generare un keystore, server.jks
, contenente una chiave generata con algoritmo RSA con il certificato (self-signed) associato, utilizzando la stessa password per proteggere l’archivio e la chiave. Avviato il comando, per poter popolare il certificato verranno richieste le credenziali dell’organizzazione sotto forma di nome comune del proprietario del certificato, unità aziendale del proprietario del certificato, nome dell’azienda, località, stato/regione, nazione. Per il nome è buona pratica utilizzare un nome di dominio non ambiguo che specifichi la posizione assoluta all’interno della gerarchia di una intranet o di internet, per cominciare si può utilizzare il nome della macchina su cui si opera. E’ possibile evitare di fornire questi parametri uno alla volta aggiungendoli direttamente al comando:
keytool -genkey -alias serverkey -keyalg RSA -storepass password -keystore server.jks -keypass password -dname "CN=Nome, OU=IT, O=CNO, L=loc, S=reg, C=IT"
Altre opzioni possono riguardare la dimensione della chiave (keysize
, di norma 1024 bits), l’algoritmo usato per firmare il certificato (-sigalg
), il formato dell’archivio generato (-storetype
) e il numero di giorni prima che il certificato scada (-validity
, di norma 90 o 180 giorni, anche se va considerato che questo valore non ha effetto in sessioni configurate per utilizzare SSH con autenticazione a chiave pubblica).
-list
e -delete
sono altri comandi utili. Con il primo possiamo esplorare il contenuto di un keystore mentre con il secondo possiamo rimuoverne una voce:
keytool -list -keystore server.jks -storepass password
keytool -delete -alias serverkey -keystore server.jks -storepass password
keytool -list -keystore server.jks -storepass password
Al primo comando verremo informati della presenza di una voce, chiave e certificato che abbiamo precedentemente creati (PrivateKeyEntry
), la voce verrà eliminata dall’archivio con il secondo comando. L’esecuzione dell’ultimo comando segnalerà che l’archivio è ora vuoto.
Esportazione del certificato
Sempre nella cartella precedente, assicuriamoci di avere un keystore con una voce al suo interno. Partendo dalle credenziali inserite è possibile esportare il certificato tramite il comando:
keytool -export -alias serverkey -keypass password -file server.crt -keystore server.jks -storepass password
Esso ha lo scopo di generare un file, server.crt
, contenente il certificato. Esplorando le proprietà del file generato troveremo i dati che abbiamo popolato in precedenza, ossia nome, scadenza, algoritmo utilizzato per la firma elettronica; poiché abbiamo utilizzato un algoritmo RSA per la firma avremo sha1RSA.
Volendo invece avere la certificazione da parte di un’agenzia di certificazione (CA), è possibile esportare le informazioni in un file CSR (Certificate Signing Request) e inviare il CSR alla CA. L’operazione dovrebbe restituire un certificato autenticato che andrebbe a sostituire il precedente certificato self-signed:
keytool -keystore server.jks -certreq -alias serverkey -keyalg rsa -file server.csr
Ottenuto il certificato dall’autorità competente (ipotizziamo venga restituito il file root.cer
), si potrà importarlo nel keystore sovrapponendolo al certificato già presente tramite il commando -import
:
keytool -import -alias serverkey -keypass password -file root.cer -keystore server.jks -storepass password
L’operazione ha due scopi: il primo è quello di importare il certificato ricevuto in risposta da un’autorità di certificazione, in questo caso si utilizzerà l’alias già esistente; il secondo scopo, che approfondiremo, è importare certificati per la validazione.
Generazione del truststore
Abbiamo visto come generare archivi keystore, estrarne certificati da distribuire ed eventualmente fare autenticare. Resta da stabilire come creare archivi per scopi di verifica, ossia archivi che collezionano certificati di entità terze. A tale scopo, è sufficiente disporre di un certificato e utilizzare il comando -import
su un alias non ancora contenuto in un archivio. Sarà sempre possibile rimuovere un certificato, magari per far posto ad una nuova versione o posizione di un server, tramite il comando -delete
. Ipotizziamo quindi di voler creare un nuovo truststore:
keytool -import -alias servercert -keypass password -file server.crt -keystore truststore.jks -storepass password
Lanciato il comando, verrà richiesto se considerare attendibile il certificato. Dato l’assenso, troveremo nella cartella il nuovo file truststore.jks
. Esplorandolo con il comando -list
troveremo che la voce è contrassegnata dalla dicitura trustedCertEntry
. Sarà successivamente possibile aggiungere altri certificati.
Approccio programmatico
Finora abbiamo utilizzato keytool. Potrebbe essere però necessario generare un archivio o il certificato programmaticamente. Una possibilità sarebbe quella di usare keytool richiamandolo tramite Runtime.exec
, ma non è la soluzione ideale. Java mette a disposizione pertanto una classe, java.security.KeyStore
, utile alla gestione dell’importazione di chiavi private, relativi certificati, catene di certificati e certificati trusted, con diverse opzioni utili per aggiornare i dati contenuti nei keystore.
Il problema è che pur essendo presenti in Java diverse classi per la gestione di certificati X509Certificate, manca la possibilità di creare un certificato X509, nonostante sia questa una funzionalità presente nel keytool. Pertanto, se risulta necessario creare dinamicamente il keystore con un certificato self-signed senza utilizzare il keytool e senza fare riferimento a package non documentati (come il sun.security.x509
), è possibile utilizzare la classe X509v3CertificateBuilder
dalle API Bouncy Castle.
Di seguito la classe CreateTrustStorec
, basata sulle API java.security.KeyStore
, che acquisisce un certificato (server.crt
) dalla cartella testFolde
r, genera nella stessa un keystore (truststore.jks
) e vi importa il certificato.
package testKeyTool;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
public class CreateTrustStore {
public static void main(String[] args) {
try {
// Inizializzazione del keystore
KeyStore ks = KeyStore.getInstance("JKS");
char[] password = "password".toCharArray();
ks.load(null, password);
// Acquisizione del certificate e archiviazione nel keystore
CertificateFactory cerFact = CertificateFactory.getInstance("X.509");
Certificate cert = cerFact.generateCertificate(new FileInputStream("testFolder/server.crt"));
ks.setCertificateEntry("trustedCert", cert);
// Memorizzazione del keystore.
FileOutputStream fos = new FileOutputStream("testFolder/truststore.jks");
ks.store(fos, password);
fos.close();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
OpenSSL
keytool presenta qualche limite rispetto alle azioni che potremmo dovere compiere. Un’alternativa è rappresentata dall’utilizzo di OpenSSL, implementazione libera dei protocolli SSL/TLS (installazione su Linux). Si ricordi che la release OpenSSL 1.0.1 fino alla versione 1.0.1f presenta un pericoloso bug, Heartbleed, che consente l’accesso a dati sensibili (password, chiavi private SSL, cookies di sessione) da parte di un utente remoto.
Riporteremo di seguito un esempio su come ottenere un keystore a partire da una chiave e un certificato, azione non effettuabile direttamente con keytool. Per prima cosa generiamo una chiave e una richiesta di certificato con OpenSSL:
openssl genrsa -out externalkey.key
openssl req -new -key externalkey.key -out certsigningreq.csr
Verranno richiesti i soliti dati (stato, regione, località etc.). Generiamo quindi il certificato self-signed:
openssl x509 -req -days 360 -in certsigningreq.csr -signkey externalkey.key -out cert.crt
Passo successivo, utilizziamo ancora OpenSSL per generare un archivio leggibile da keytool in formato PKCS12:
openssl pkcs12 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in cert.crt -inkey externalkey.key -out arc_pkcs12.pfx
A questo punto siamo pronti per ottenere il keystore in formato JKS:
keytool -importkeystore -srckeystore arc_pkcs12.pfx -srcstoretype PKCS12 -destkeystore arc_jks.jks -deststoretype JKS
Utilizzando il comando -list
sul keystore generato sarà possibile osservare la presenza della voce con chiave privata. A questo punto abbiamo tutti gli elementi per fare il prossimo passo, instaurare una comunicazione sicura a livello trasporto per l’accesso a un Web service.