HtmlUnit mit https und Client-Zertifikaten

HtmlU­nit ist eine Ja­va-Bi­blio­thek, wel­che eine Art „head­less Web­brow­ser“ zur Ver­fü­gung stellt – Sei­ten­ab­ru­fe, Suche nach Ele­men­ten auf Sei­ten und an­schlie­ßen­de Ak­tio­nen las­sen sich hier­mit pro­gram­mie­ren (bei­spiels­wei­se für In­te­gra­ti­ons­test das Aus­fül­len eines Log­in-For­mu­lars und an­schlie­ßen­de Kli­cken des Log­in-But­tons). Un­glück­li­cher­wei­se bie­tet HtmlU­nit keine ge­rad­li­ni­ge Un­ter­stüt­zung für https – ins­be­son­de­re bei der Ver­wen­dung von Cli­ent-Zer­ti­fi­ka­ten ist man schlicht­weg auf­ge­schmis­sen.

Unter der Haube ver­wen­det das ak­tu­el­le HtmlU­nit zum Auf­bau von Ver­bin­dun­gen die Apa­che Http­Com­po­n­ents; in frü­he­ren Ver­sio­nen war es Apa­che Http­Cli­ent – hier konn­te man mit dem hier be­schrie­be­nen Trick die Cli­ent­zer­ti­fi­ka­te „un­ter­schie­ben“. Nach ei­ni­gem Gra­ben in den Un­tie­fen der Bi­blio­thek habe ich nun einen Weg ge­fun­den, mit dem es auch in der ak­tu­el­len Ver­si­on mög­lich ist.

Hier­zu wird die HttpWeb­Con­nec­tion-Klas­se von HtmlU­nit er­wei­tert:

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 re­gis­trie­ren eines Pro­to­koll­sche­mas, wel­ches be­reits vor­han­den ist, wird die­ses durch das neue er­setzt – so be­kommt man eine SSLSo­cket­Fac­to­ry mit dem ei­ge­nen KeySto­re in die Kette ge­hängt. Beim Be­nut­zen von HtmlU­nit muß nun diese ei­ge­ne Web­Con­nec­tion-Klas­se ver­wen­det wer­den:

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 ab­fin­det, daß die Java KeySto­re-Klas­sen ab­so­lu­te Grüt­ze sind und man lie­ber di­rekt die Hilfs­me­tho­den von Boun­cy Cast­le ver­wen­det, kommt man re­la­tiv zügig ans ge­wünsch­te Ziel ;-)