HtmlUnit mit https und Client-Zertifikaten

HtmlUnit ist eine Java-Bibliothek, welche eine Art „headless Webbrowser“ zur Verfügung stellt – Seitenabrufe, Suche nach Elementen auf Seiten und anschließende Aktionen lassen sich hiermit programmieren (beispielsweise für Integrationstest das Ausfüllen eines Login-Formulars und anschließende Klicken des Login-Buttons). Unglücklicherweise bietet HtmlUnit keine geradlinige Unterstützung für https – insbesondere bei der Verwendung von Client-Zertifikaten ist man schlichtweg aufgeschmissen.

Unter der Haube verwendet das aktuelle HtmlUnit zum Aufbau von Verbindungen die Apache HttpComponents; in früheren Versionen war es Apache HttpClient – hier konnte man mit dem hier beschriebenen Trick die Clientzertifikate „unterschieben“. Nach einigem Graben in den Untiefen der Bibliothek habe ich nun einen Weg gefunden, mit dem es auch in der aktuellen Version möglich ist.

Hierzu wird die HttpWebConnection-Klasse von HtmlUnit erweitert:

import com.gargoylesoftware.htmlunit.HttpWebConnection;
import com.gargoylesoftware.htmlunit.WebClient;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import java.security.KeyStore;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;

public class KeystoreHttpWebConnection extends HttpWebConnection {
  KeystoreHttpWebConnection(WebClient webClient) {
   super(webClient);
  }
  public void setKeyStore(KeyStore keystore, String keystorePassword, KeyStore truststore)
      throws NoSuchAlgorithmException, KeyManagementException,
      KeyStoreException, UnrecoverableKeyException {
    SchemeRegistry schemeRegistry = getHttpClient().getConnectionManager().getSchemeRegistry();
    SSLSocketFactory socketFactory = new SSLSocketFactory(keystore, keystorePassword, truststore);
    schemeRegistry.register(new Scheme("https", 443, socketFactory));
  }
}

Durch registrieren eines Protokollschemas, welches bereits vorhanden ist, wird dieses durch das neue ersetzt – so bekommt man eine SSLSocketFactory mit dem eigenen KeyStore in die Kette gehängt. Beim Benutzen von HtmlUnit muß nun diese eigene WebConnection-Klasse verwendet werden:

WebClient webClient = new WebClient(...);
KeystoreHttpWebConnection webConnection = new KeystoreHttpWebConnection(webClient);
webConnection.setKeyStore(keystore, keypass, truststore);
webClient.setWebConnection(webConnection);

Et voilà :-) Wenn man sich nun noch damit abfindet, daß die Java KeyStore-Klassen absolute Grütze sind und man lieber direkt die Hilfsmethoden von Bouncy Castle verwendet, kommt man relativ zügig ans gewünschte Ziel ;-)