Certificate bananza…

Helped a collegue yesterday for a few hours configuring his system with TLS certificates and showed code to enable authentication using client certificates. All easy enough if you know how… 🙂 Start by creating keys and certificates for a server and a person and create a Domino KYR keystore using the kyrtool from IBM. The below commands were all executed on Linux.

# change dir
cd /local/notesdata/

# generate key and self-signed cert for server
openssl genrsa -out server.key 4096
cat server.key
openssl req -new -sha256 -key server.key -out server.csr
openssl x509 -req -days 3650 -sha256 -in server.csr -signkey server.key -out server.pem

# use kyrtool to generate kyr-file for Domino (all calls to startup below is actually
# /opt/ibm/domino/bin/tools/startup
startup /opt/ibm/domino/bin/kyrtool =/local/notesdata/notes.ini
startup /opt/ibm/domino/bin/kyrtool =/local/notesdata/notes.ini create
          -k ./server.kyr -p password
cat server.key > server.txt
cat server.pem  >> server.txt
cat server.txt
startup /opt/ibm/domino/bin/kyrtool =/local/notesdata/notes.ini
          verify ./server.txt
startup /opt/ibm/domino/bin/kyrtool =/local/notesdata/notes.ini
          import all -k ./server.kyr -i ./server.txt
startup /opt/ibm/domino/bin/kyrtool =/local/notesdata/notes.ini
          show keys -k ./server.kyr
startup /opt/ibm/domino/bin/kyrtool =/local/notesdata/notes.ini
          show certs -k ./server.kyr

# generate PKCS#12 for a user
openssl genrsa -out person.key 4096
openssl req -new -sha256 -key person.key -out person.csr
openssl x509 -req -days 3650 -sha256 -in person.csr -CA server.pem
          -CAkey server.key -out person.pem -CAcreateserial
openssl x509 -in person.pem -text -noout
openssl pkcs12 -export -out person.p12 -inkey person.key -in person.pem -certfile server.pem

Import the the person.p12 file on a person document in Domino Directory. Now copy server.kyr and server.sth to the Domino data dirctory and create an Internet Sites document using the keystore. Also edit the Internet Site document to allow certificate based authentication. The code below uses the PKCS#12 file to do an authenticated request using certificates as authentication.

package demo.intravision.certauth;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;


public class Main {

   public static void main(String[] args) {
      try {
         new Main().run();

      } catch (Throwable t) {
         t.printStackTrace();
      }
   }

   public void run() throws Exception {
      // get url and open connection
      URL url = new URL("https://secure.krynn.local/testauth.nsf/username.json?open&login");
      HttpURLConnection con = (HttpURLConnection)url.openConnection();

      // apply SSL context (sets up the certificate to use for authentication etc)
      this.applySSLContext(con);

      // read from url
      BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
      StringBuilder b = new StringBuilder();
      String line = null;
      while (null != (line = reader.readLine())) {
         b.append(line).append('n');
      }

      // show data
      System.out.println(b.toString());

      // disconnect
      con.disconnect();
   }

   private void applySSLContext(HttpURLConnection con) throws Exception {
      // password
      final char[] password = "password".toCharArray();

      // input stream to pkcs12 file
      InputStream certificateInputStream = new FileInputStream("/Users/lekkim/Downloads/aran.p12");

      // load key and key manager
      KeyStore ks = KeyStore.getInstance("PKCS12");
      ks.load(certificateInputStream, password);
      KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
      kmf.init(ks, password);

      // get trust manager
      X509TrustManager customTrustMgr = this.getTrustManager();

      // build ssl context
      SSLContext sc = SSLContext.getInstance("TLSv1.2");
      sc.init(kmf.getKeyManagers(), null == customTrustMgr ? null : new TrustManager[]{customTrustMgr}, null);

      // set socket facory if https
      if (con instanceof HttpsURLConnection) {
         HttpsURLConnection httpsCon = (HttpsURLConnection)con;
         httpsCon.setSSLSocketFactory(sc.getSocketFactory());
      }
   }

   private X509TrustManager getTrustManager() throws Exception {
      // returning null uses the trust from the JVM cacerts keystore
      if ("1".equals("2")) return null;

      // define trust manager
      X509TrustManager customTrustMgr = new X509TrustManager() {
          @Override
          public X509Certificate[] getAcceptedIssuers() {
             return new X509Certificate[]{};
          }

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

          @Override
          public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
          }
      };
      return customTrustMgr;
   }
}