Diverso tempo fa ho sviluppato un WebServices xml-rpc ad uso interno per il quale ho preso la via dell’autenticazione con l’utilizzo dei certificati e sinceramente in rete non ci sono molte risorse, o almeno non le ho trovate, che possano aiutare durante la fase di sviluppo.
In questo periodo ho dovuto crearne un altro basato sullo stesso sistema di autenticazione quindi ho deciso di scrivere questa mini guida cosi da incrementare l’informazione su questo argomento.

Livello:
Medio

Per chi:
Ha esperienza nello sviluppo di WebServices

Scenario:
Sviluppare un WebServices che esegua l’autenticazione dei client con l’utilizzo dei certificati

Iniziamo con la generazione dei certificati(chiave pubblica e private) che serviranno al nostro server, per fare questo utilizziamo il tool messo a disposizione dalla SUN keytool che serve per la gestione di un database di chiavi criptate e certificati.

$ keytool -genkeypair -dname 
  "cn=Cristian Porta, ou=Crx, o=crx, c=IT" 
  -alias server 
  -keypass password 
  -keystore ~/.cert/keystore 
  -storepass password

Cosi generato il nostro certificato, che e’ stato inserito nel database ~/.cert/keystore, ha una validita’ di 90 giorni, impostazione di default, se vogliamo aumentare questo periodo bastera’ aggiungere il parametro -validity [ngiorni].
Per avere una descrizione dettagliata dei parametri consiglio comunque di consultare il man keytool visto che in questa sede cerco solo di dare la traccia per preparare il sistema senza entrare troppo nello specifico.

Ora creiamo un oggetto semplice che servira’ a “erogare” il servizio, una somma :)

public class Utils {
  public Utils() { }
 
  public int sum(int x, int y) {
    System.out.println("execute sum");
    return x + y ;
  }
}

Fatto questo sviluppiamo il WebServices, al quale aggiungeremo come handler l’oggetto Utils, che rimarra’ in attesa di connessioni e richieste dall’esterno, prima di tutto e’ necessario scaricare le librerie xmlrpc dal sito Apache e importarle nel classpath del nostro ambiente di sviluppo.

import org.apache.xmlrpc.secure.SecureWebServer;
import org.apache.xmlrpc.secure.SecurityTool;
 
public class Server {
 
  private static final int WS_PORT = 9966;
 
  public Server() { }
 
  public static void main(String[] args) {
    Server.startUp();
  }
 
  public static void startUp() {
 
    SecurityTool.setKeyStore("/home/crx/.cert/keystore");
    SecurityTool.setKeyStorePassword("password");
    SecureWebServer secureWebServer = new SecureWebServer(WS_PORT);
    secureWebServer.addHandler("utilities", new Utils());
    secureWebServer.start();
    System.out.println("server started");
  }
}

Ora esportiamo il certificato per il client in questo modo:

$ keytool -export -alias server 
  -keystore keystore 
  -keypass password 
  -storepass password 
  -rfc -file crx.csr

e lo importiamo nei trust

$ keytool -import -alias server 
  -keystore ~/.cert/truststore 
  -file crx.csr
 
Enter keystore password:
Owner: CN=Cristian Porta, OU=Crx, O=crx, C=IT
Issuer: CN=Cristian Porta, OU=Crx, O=crx, C=IT
Serial number: 46dffc59
Valid from: Thu Sep 06 13:10:49 GMT 2007 until: Wed Dec 05 13:10:49 GMT 2007
Certificate fingerprints:
         MD5:  4E:BC:DA:6A:7D:0A:93:9B:81:D7:E4:4D:76:06:EC:3A
         SHA1: 72:32:3B:74:9E:FA:62:23:40:86:A5:90:CF:2C:EE:AA:B8:E0:A6:8D
         Signature algorithm name: SHA1withDSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

Bene, ora creiamo il client che dovra’effettuare la richiesta al server ma che dovra’, prima di poterla fare, autenticarsi

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Vector;
 
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
 
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.secure.SecureXmlRpcClient;
import org.apache.xmlrpc.secure.SecurityTool;
 
public class Client {
 
  public Client() { }
 
  public static void main(String[] args) {
    SecurityTool.setTrustStore("/home/crx/.cert/truststore" );
    SecurityTool.setKeyStore("/home/crx/.cert/truststore" );
    //SecurityTool.setKeyStorePassword("password");	
 
    SecureXmlRpcClient client = null;
    try {
      client = new SecureXmlRpcClient("127.0.0.1", 9966);
      client.setup();
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (Exception e) {
      e.printStackTrace();
    }
 
    HostnameVerifier hv = new HostnameVerifier() {
      public boolean verify(String arg0, SSLSession arg1) {
        //System.out.println("host: "+arg0+" , "+arg1.getPeerHost());
        return true;
      }
    };
 
    HttpsURLConnection.setDefaultHostnameVerifier(hv);
 
    Vector params = new Vector();
    params.add(0, new Integer(Integer.parseInt(args[0])));
    params.add(1, new Integer(Integer.parseInt(args[1])));
 
    Object result = null;
    try {
      result = client.execute("utilities.sum", params);
      System.out.println(result);
    } catch (XmlRpcException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Ora, in ordine, eseguiamo il server

$ java Server
server started

e il client

$ java Client 7 8

fatto qeesto, se tutto e’ stato fatto correttamente, nell’output del server troveremo

$ java Server
server started
executed sum

mentre sul client avremo il risultato

$ java Client 7 8
15

Spero di non aver tralasciato nulla, come detto in precedenza non sono entrato nello specifico di tutti parametri di keytool e delle classi SecureXmlRpcClient, SecurityTool, SecureWebServer e classi del package javax.net.ssl ma spero almeno di aver dato una traccia su cui lavorare e sviluppare la propria applicazione, logico che poi bisogna studiare :)

Ciao e alla prossima,
Cristian.