Connessione in HTTPS con mutua autenticazione client/server da un client Java

Java Tech 0 2218
In questo articolo si fa riferimento al post precedente “HTTPS e mutua autenticazione client/server su Tomcat con certificato X509” (in fondo il link, [Riferimento 1]).

A differenza dell’articolo precedente, in questo vogliamo mostrare come connetterci a un server HTTPS (supportando la mutua autenticazione) da un client Java, e non da un browser.

Si presuppone quindi di avere un server HTTPS (Tomcat/Java come nel post citato, oppure qualsiasi altra piattaforma, come ad es. IIS/.NET) già configurato con un certificato X509 a scopo autenticazione del server (che viene ovviamente utilizzato anche per la cifratura).

Naturalmente ci serve la JDK (nella nostra prova utilizziamo la JDK 1.6), un certificato X509 ad uso autenticazione del client (che in questo caso è una classe Java) e un keystore di tipo TrustStore dove mettiamo il certificato pubblico del server (affinchè il server venga riconosciuto e autenticato dal nostro client).

Scriviamo la nostra classe Java, che chiamiamo “TestHTTPSClient”.
La URL del server al quale ci vogliamo connettere è https://server.domain.local/

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;

public class TestHTTPSClient {

    public void connectToServerInHTTPS() throws Exception {
        SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        URL url = new URL("https://server.domain.local/");
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setSSLSocketFactory(sslsocketfactory);
        InputStream inputstream = conn.getInputStream();
        InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        String line = null;
        while ((line = bufferedreader.readLine()) != null) {
            System.out.println("Ricevuto " + line);
        }
    }

    public static void main(String[] args) throws Exception {
        new TestHTTPSClient().connectToServerInHTTPS();
    }
}

Compiliamo la classe:

javac TestHTTPSClient.java

Supponiamo di avere un file chiamato “client.p12” che contiene il certificato che dobbiamo utilizzare allo scopo “Client Authentication” (che ci è stato fornito dalla nostra Certification Authority o che ci siamo autogenerati in modalità self-signed, vedi lo Step 3b descritto in [Riferimento 1]), protetto dalla password “pass123”. Si presuppone che tale certificato sia considerato “trusted” sul server HTTPS al quale ci vogliamo collegare.

Poi ci serve anche il keystore di tipo TrustStore che contiene il certificato pubblico del server “server.domain.local” che vogliamo far considerare come “trusted” dal nostro client. Supponiamo di avere il file “ServerDomainLocal.cer” che contiene il certificato pubblico del nostro server.
Eseguiamo il seguente comando per creare il keystore:

keytool -import -alias ServerDomainLocal -keystore MyTrustStore.jks -trustcacerts -file ServerDomainLocal.cer

Alla richiesta interattiva di inserimento della password (che protegge il keystore creato) digitiamo “trust123

Adesso possiamo eseguire la classe che abbiamo appena creato, passando tutti i parametri necessari (System Property, che sostanzialmente sono i puntamenti al keystore e al certificato client citati sopra) al funzionamento del SSL.
Il comando da eseguire è il seguente:

java -Djavax.net.ssl.keyStoreType=pkcs12 -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.keyStore=client.p12 -Djavax.net.ssl.trustStore=MyTrustStore.jks -Djavax.net.ssl.keyStorePassword=pass123 -Djavax.net.ssl.trustStorePassword=trust123 TestHTTPSClient

In output (sullo standard output della shell che stiamo utilizzando), ci verrà presentato il contenuto (in formato HTML o altro) che il server ha prodotto come risposta alla nostra richiesta HTTPS.

Solo una considerazione finale sulle caratteristiche del certificato che certifica il server.

All’interno del certificato, il server può essere identificato fondamentalmente in due modi:

  1. con un Host Name;
  2. con un indirizzo IP.

Tale informazione è inserita tipicamente nel campo “Subject”. Nel nostro caso il campo potrebbe contenere il seguente valore:

CN = server.domain.local
O = Mia Organizzazione
L = Roma
C = IT

Il problema nasce se dovessimo certificare un server attraverso il suo indirizzo IP (che dovrebbe essere meno tipico, ma può accadere), e non con il suo hostname. In questo caso è necessario che nel certificato venga popolato il campo “Subject Alternative Name”. In questo campo, ad esempio, potremmo inserire il seguente valore:

CN = 192.168.0.1
O = Mia Organizzazione
L = Roma
C = IT

Questo avviene perchè Java, in particolare nel nostro caso come client HTTPS, applica “strettamente” la RFC 2818 (vedi [Riferimento 2]) che nel paragrafo 3.1 recita testualmente:

In some cases, the URI is specified as an IP address rather than a hostname. In this case,  the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.

Alcuni browser per la navigazione Internet potrebbero invece “tollerare” l’assenza del campo “Subject Alternative Name” ed accontentarsi del solo campo “Subject”, cosa che non fa Java.

Nel documento “Java Secure Socket Extension” (vedi [Riferimento 2]), al paragrafo “Setting the Assigned HostNameVerifier”, viene descritta la fase di verifica del HostName, e ci si riferisce esplicitamente alle regole definite nella RFC 2818.

Alcuni riferimenti:

  1. "HTTPS e mutua autenticazione client/server su Tomcat..."
    link [Riferimento 1]
  2. RFC 2818
    link [Riferimento 2]
  3. "Java Secure Socket Extension" (JSSE)
    link [Riferimento 3]

About the author / 

Salvatore Di Loro

Related Posts

Leave a reply

Your email address will not be published. Required fields are marked *

Twitter @sdiloro

Instagram

Flickr