lördag 11 december 2010

Java Client certificates playtime

Using different frameworks to do client authenion and trusting all server certificates to do integration testing. I don't want to have to go in and change the security setting files or run with command line properties. An example zip file containing a maven/eclipse project which incorporates a jetty configuration and server/client certificate generation to test everything(see bottom of this post).


Host validation
Allow certificate not to match host - create an alltrusting hostverifier:
public class AcceptAnyHostVerifier implements HostnameVerifier,
  X509HostnameVerifier {
 @Override
 public final void verify(String host, String[] cns, String[] subjectAlts) {
  System.out.println("AcceptAnyHostVerifier");

 }

 @Override
 public boolean verify(String arg0, SSLSession arg1) {
  System.out.println("AcceptAnyHostVerifier");
  return true;
 }

 @Override
 public void verify(String host, SSLSocket ssl) throws IOException {
  System.out.println("AcceptAnyHostVerifier");

 }

 @Override
 public void verify(String host, X509Certificate cert) throws SSLException {
  System.out.println("AcceptAnyHostVerifier");

 }

}

Certificate validation
Trusting all certificates:

public class TrustAnyCertificateTrustManager implements X509TrustManager {

 @Override
 public void checkClientTrusted(X509Certificate[] arg0, String arg1)
   throws CertificateException {
  
 }

 @Override
 public void checkServerTrusted(X509Certificate[] arg0, String arg1)
   throws CertificateException {
  
 }

 @Override
 public X509Certificate[] getAcceptedIssuers() {
  return null;
 }

}

Keystores, PKCS12 & Certificates
Here are some interesting ways to play around with keystores.

PCKS12
A good thing to know is that the keystore API treats .pfx files as keystores out of the box in late versions of J2SE.
So the following works perfectly fine:
public KeyStore createJavaKeystoreFromPKCS12File(String pkcs12File,
   String password) throws KeyStoreException,
   NoSuchAlgorithmException, CertificateException, IOException,
   UnrecoverableKeyException {

  KeyStore pkcs12store = KeyStore.getInstance("PKCS12");
  InputStream keyInput = new FileInputStream(pkcs12File);

  pkcs12store.load(keyInput, password.toCharArray());
  keyInput.close();

  return pkcs12store;
 }

Keystore
Getting an in-memory representation of a keystore:
public KeyStore getJavaKeystore(String path, String password)
 throws KeyStoreException, NoSuchAlgorithmException,
 CertificateException, IOException, UnrecoverableKeyException {
  KeyStore javaKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
  InputStream keyInput = new FileInputStream(path);
  javaKeystore.load(keyInput, password.toCharArray());
  keyInput.close();
  return javaKeystore;
 }

Certificates
Creating an in-memory keystore from a certificate file(generated by for example a ca or openssl):
/**
  * Creates a truststore from a given certificate file
  * 
  * @param fileName
  * @param keyStorePassword
  * @return a truststore containing the certificates in the certificatefile.
  * @throws KeyStoreException
  * @throws NoSuchAlgorithmException
  * @throws CertificateException
  * @throws IOException
  */
 public KeyStore createTrustStoreFromCertFile(String fileName,
   String keyStorePassword) throws KeyStoreException,
   NoSuchAlgorithmException, CertificateException, IOException {
  return createTrustStoreFromCerts(getCertificatesFromCertFile(fileName),
    keyStorePassword);
 }

 /**
  * Gets the certificates in the CA chain from a certificate file
  * 
  * @param filename
  * @return an array containing all the certificates in the CA chain fo the
  *         certificate file
  * @throws NoSuchAlgorithmException
  * @throws CertificateException
  * @throws IOException
  * @throws KeyStoreException
  */
 public X509Certificate[] getCertificatesFromCertFile(String filename)
   throws NoSuchAlgorithmException, CertificateException, IOException,
   KeyStoreException {

  // Import the trusted certificate

  FileInputStream fis = new FileInputStream(filename);
  BufferedInputStream bis = new BufferedInputStream(fis);

  CertificateFactory cf = CertificateFactory.getInstance("X.509");

  ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
  while (bis.available() > 0) {
   X509Certificate cert = (X509Certificate) cf
     .generateCertificate(bis);
   certList.add(cert);
  }
  return (X509Certificate[]) certList.toArray();
 }

 /**
  * 
  * @param certs
  * @param keyStorePassword
  * @return an inmemory keystore(truststore) containing the certificates sent
  *         in
  * @throws KeyStoreException
  * @throws NoSuchAlgorithmException
  * @throws CertificateException
  * @throws IOException
  */
 public KeyStore createTrustStoreFromCerts(X509Certificate[] certs,
   String keyStorePassword) throws KeyStoreException,
   NoSuchAlgorithmException, CertificateException, IOException {
  KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

  // Create an empty keystore that will be the truststore
  java.io.FileInputStream kFis = null;
  ks.load(kFis, keyStorePassword.toCharArray());
  for (int i = 0; i < certs.length; i  ) {
   ks.setCertificateEntry(Integer.toString(i), certs[i]);
  }
  return ks;
 }

URLConnection
How to do http authentication(both preemptive-basic and using client-certificate) with java.net.URLConnection.


Setup
Setting up the context for trusting everything and providing client certificate on authorization request from the server.
  // Add the keystore containing the client cert to the context.
  KeyManagerFactory factory = KeyManagerFactory
  .getInstance(KeyManagerFactory.getDefaultAlgorithm());
  KeyStore clientKeystore = getJavaKeystore(this.clientKeystoreFile,
    this.clientKeystorePassword);
  factory.init(clientKeystore, clientKeystorePassword.toCharArray());
  KeyManager[] keyManagers = factory.getKeyManagers();
  
  //Trust all certs
  X509TrustManager tm = new TrustAnyCertificateTrustManager();

   SSLContext sc = SSLContext.getInstance("SSL");
      Object trustAllCerts;
   sc.init(keyManagers, new TrustManager[] { tm }, new java.security.SecureRandom());
    defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
    defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
      HttpsURLConnection.setDefaultHostnameVerifier(new AcceptAnyHostVerifier());
 }

Http request
Doing a request that will do basic authentication on a http level and also authenticate with a client certificate in case the server requests it(requires the setup above).
public void testPostWithClientAuthHttps() throws Exception
 {
   
   
 String data = URLEncoder.encode("name1", "UTF-8")   "="
      URLEncoder.encode("value1", "UTF-8");
  data  = "&"   URLEncoder.encode("name2", "UTF-8")   "="
      URLEncoder.encode("value2", "UTF-8");

  URL url = getUrl();
  URLConnection conn = url.openConnection();

  conn.setDoOutput(true);
  OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
  wr.write(data);
  wr.flush();

  // Get the response
  BufferedReader rd = new BufferedReader(new InputStreamReader(conn
    .getInputStream()));
  String line;
  while ((line = rd.readLine()) != null) {
   System.out.println(line);
  }
  wr.close();
  rd.close();
  
 }

public URL getUrl() throws MalformedURLException {
  return new URL(protocol   "://"   server   ":"   port   uri);
 }


HttpClient(4.0.3)
How to do authentication with HttpClient(both preemptive-basic and using client-certificate).

(Look above for some of the methods used)
public void httpGetWithClientCertAndBasicAuth() throws Exception {

  System.out.println("URI:"   getUri());
  HttpGet httpGet = new HttpGet(getUri());

  HttpClient httpClient = getClientCertAuthenticateHttpClient();

  // Preemptive authentication, don't wait for 401 challenge.
  BasicScheme bs = new BasicScheme();
  httpGet.addHeader(bs.authenticate(new UsernamePasswordCredentials(
    username, password), httpGet));

  HttpResponse response = httpClient.execute(httpGet);
  System.out.println(response.getStatusLine());
  BasicResponseHandler handler = new BasicResponseHandler();
  System.out.println(handler.handleResponse(response));
 }

public HttpClient getClientCertAuthenticateHttpClient() throws Exception {
  SSLContext ctx = SSLContext.getInstance("TLS");

  // Accepts any cert.
  X509TrustManager tm = new TrustAnyCertificateTrustManager();

  // Add the keystore containing the client cert to the context.
  KeyManagerFactory factory = KeyManagerFactory
  .getInstance(KeyManagerFactory.getDefaultAlgorithm());
  KeyStore clientKeystore = getJavaKeystore(this.clientKeystoreFile,
    this.clientKeystorePassword);
  factory.init(clientKeystore, clientKeystorePassword.toCharArray());
  KeyManager[] keyManagers = factory.getKeyManagers();
  ctx.init(keyManagers, new TrustManager[] { tm }, null);

  SSLSocketFactory sf = new SSLSocketFactory(ctx);

  // Allow any host even if cert does not match hostname
  sf.setHostnameVerifier(new AcceptAnyHostVerifier());

  Scheme certScheme = new Scheme(protocol, sf, port);

  HttpClient httpClient = new DefaultHttpClient();


  httpClient.getConnectionManager().getSchemeRegistry().register(
    certScheme);
  return httpClient;
 }

Defaults
To setup httpclient to use the insecure trustmanager, hostnameverifier and your keystore by default programatically:
protected void setUp() throws Exception{
  System.out.println("Setup...");
  SSLContext ctx; 
  ctx = SSLContext.getInstance("TLS");

  // Accepts any cert.
  X509TrustManager tm = new TrustAnyCertificateTrustManager();

  // Add the keystore containing the client cert to the context.
  KeyManagerFactory factory = KeyManagerFactory
    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
  KeyStore clientKeystore = getJavaKeystore(this.clientKeystoreFile,
    this.clientKeystorePassword);
  factory.init(clientKeystore, "feathertrail".toCharArray());
  KeyManager[] keyManagers = factory.getKeyManagers();
  ctx.init(keyManagers, new TrustManager[] { tm }, null);
  //Stowe away the default context
  
  defaultContext = SSLContext.getDefault();
  SSLContext.setDefault(ctx); 
  
 }
/**
  * Creates an httpclient that uses the default trustmanager
  * of the jvm.
  * 
  * @return a very naive httpclient with the keystore specified in the
  *         constructor.
  * @throws Exception
  */
 public HttpClient getHttpClient() throws Exception {

  SSLContext ctx = SSLContext.getDefault();
  SSLSocketFactory sf = new SSLSocketFactory(ctx);
  // Allow any host even if cert does not match hostname
  sf.setHostnameVerifier(new AcceptAnyHostVerifier());

  Scheme certScheme = new Scheme(protocol, sf, port);

  HttpClient httpClient = new DefaultHttpClient();

  httpClient.getConnectionManager().getSchemeRegistry().register(
    certScheme);
  return httpClient;
 }

 public void httpGetWithClientCertAndBasicAuth() throws Exception {

  System.out.println("URI:"   getUri());
  HttpGet httpGet = new HttpGet(getUri());

  HttpClient httpClient = getHttpClient();

  // Preemptive authentication, don't wait for 401 challenge.
  BasicScheme bs = new BasicScheme();
  httpGet.addHeader(bs.authenticate(new UsernamePasswordCredentials(
    username, password), httpGet));

  HttpResponse response = httpClient.execute(httpGet);
  System.out.println(response.getStatusLine());
  BasicResponseHandler handler = new BasicResponseHandler();
  System.out.println(handler.handleResponse(response));
 }

HtmlUnit
HtmlUnit uses HttpClient for connectivity, so basically the challenge is to get a hold of the classes needed to setup HttpClient correctly.

HttpWebConnection
Start by extending HttpWebConnection and override getHttpClient that trusts all certs on all hosts and returns the clients certificate when requested.

public class AcceptAnyCertAndHostHttpWebConnection extends HttpWebConnection {

 private String clientKeystorePassword;
 private String clientKeystoreFile;
 String protocol;
 private int port;

 public AcceptAnyCertAndHostHttpWebConnection(WebClient webClient,
   String clientKeystoreFile, String clientKeystorePassword,
   String protocol, int port) {
  this(webClient);
  this.clientKeystoreFile = clientKeystoreFile;
  this.clientKeystorePassword = clientKeystorePassword;
  this.port = port;
  this.protocol = protocol;
 }

 public AcceptAnyCertAndHostHttpWebConnection(WebClient webClient) {
  super(webClient);
 }

 public KeyStore getJavaKeystore(String path, String password)
   throws KeyStoreException, NoSuchAlgorithmException,
   CertificateException, IOException, UnrecoverableKeyException {
  KeyStore javaKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
  InputStream keyInput = new FileInputStream(path);
  javaKeystore.load(keyInput, password.toCharArray());
  keyInput.close();
  return javaKeystore;
 }

 @Override
 protected synchronized AbstractHttpClient getHttpClient() {
  AbstractHttpClient httpClient = super.getHttpClient();

  SSLContext ctx;
  try {
   ctx = SSLContext.getInstance("TLS");
   X509TrustManager tm = new TrustAnyCertificateTrustManager();
   // Add the keystore containing the client cert to the context.
   KeyManagerFactory factory = KeyManagerFactory
     .getInstance(KeyManagerFactory.getDefaultAlgorithm());
   KeyStore clientKeystore = getJavaKeystore(clientKeystoreFile,
     clientKeystorePassword);
   factory.init(clientKeystore, clientKeystorePassword.toCharArray());
   KeyManager[] keyManagers = factory.getKeyManagers();
   ctx.init(keyManagers, new TrustManager[] { tm }, null);

   SSLSocketFactory sf = new SSLSocketFactory(ctx);
   sf.setHostnameVerifier(new AcceptAnyHostVerifier());

   Scheme certScheme = new Scheme(protocol, sf, port);

   httpClient.getConnectionManager().getSchemeRegistry().register(
     certScheme);
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return httpClient;
 }

Http request
Making a get request using the HttpWebConnection above:
WebClient wc = new WebClient();

  wc.setUseInsecureSSL(true);

  // Setup basic authentication
  DefaultCredentialsProvider cp = new DefaultCredentialsProvider();

  cp.addCredentials(username, password, null, -1, realm);

  wc.setCredentialsProvider(cp);

  WebRequest wr = new WebRequest(getUrl(), HttpMethod.POST);

  NameValuePair xmlInput = new NameValuePair("name", "value");

  LinkedList<NameValuePair> valuePairs = new LinkedList<NameValuePair>();

  valuePairs.add(xmlInput);

  wr.setRequestParameters(valuePairs);

  Page page = wc.getPage(wr);

  System.out.println("Content: "
      page.getWebResponse().getContentAsString());


Example
I put together a sample project that uses maven to setup a jetty server that provides
https connectivity on two different ports, where one requires client certificate authentication.
The project also contains a web-app with a servlet that is provided on two different url's where one is protected by basic authentication connected to a realm configured in the jetty server.
The client connection examples are implemented as JUnit tests.
The project is configured so maven creates the client keystore and the server key- and truststore on jetty:run.

Download
The example is available here.

Usage

  1. Unzip the file.
  2. mvn jetty:run -DgenerateCerts=true
  3. mvn test
  4. mvn eclipse:eclipse (and import the project into eclipse)

fredag 10 december 2010

Doing Syntax highlighting for Java in blogger

Do a google search on "Using SyntaxHigLighter in blogger". Read more about SyntaxHighLighter.
Basically after having inserted the scripts correctly format code by using an html encoder - Postable is good. Then surround it by:
<pre class="brush: java">
Your code here.
</pre>