Fancy Key- and Truststores in HCL Domino

Have you ever tried, to access HTTPS Resources outside with Java (in Agents or xPages) in HCL-Domino. Of course you can add your certificates to the cacerts-file within the Domino JVM. But with the next update it maybe will be gone for good, because the installer decided to replace it with his own.

But you can use some fancy Java-Tricks and create your own truststores and keystores and use them in your Java-Code.

The key Item here is the javax.net.ssl.SSLSocketFactory. Create an instace of this and pass it to your HttpsURLConnection.

First, you need am Method, which creates a javax.net.ssl.SSLSocketFactory:

/**
 * Create an Instacce of javax.net.ssl.SSLSocketFactory with a given keystore and trusstore
 * 
 * @param trustFileInputStream   a InputStream to a Java-Keystore File containing the trusted certifcates
 * @param tustPassword           the passwort for the Truststore File
 * @param keyFileInputStream     a InputStream to a Java-Keystore-File containing the client-certificate
 * @param keyPassword            the passwort for hte Keystore-File
 * 
 * @return a javax.net.ssl.SSLSocketFactory Instance configured with the provided key- and truststore.
 * 
 */
public static SSLSocketFactory getFactory(InputStream trustFileInputStream, String trustPassword, InputStream keyFileInputStream, String keyPassword)
              throws Exception {

  // the keystore holds my ClientCertificates, it is only needed if you need Client-Certificate authentication
  // I choose the PKCS12 File-Format (private key protected with a passphrase)
  KeyStore keyStore = KeyStore.getInstance("PKCS12");
  keyStore.load(keyFileInputStream, keyPassword.toCharArray());

  KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
  keyManagerFactory.init(keyStore, keyPassword.toCharArray());

  KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

  // the truststore holds all the certificates you want to trust, i.e. the certifcate from the lets-encrypt-ca
  // I choose the JKS [JavaKeyStore] Format, the same formate as the famous cacerts-File
  KeyStore trustStore = KeyStore.getInstance("JKS");
  trustStore.load(trustFileInputStream, trustPassword.toCharArray());

  TrustManagerFactory trustManagerFactory = TrustManagerFactory
                  .getInstance(TrustManagerFactory.getDefaultAlgorithm());
  trustManagerFactory.init(trustStore);

  TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

  // please do not use any TLS-Version below 1.2, if possible use 1.3 :)
  SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
  sslContext.init(keyManagers, trustManagers, new SecureRandom());

  return sslContext.getSocketFactory();
}

Then some code to put the SSLSocketFactory into use. I hope you are familiar with HCL-Domino Java programming, there are some things you will need to now.

At first I get some attachments holding the files needed (ClientCertificate and TrustStore) and the passwords for them. All these things are stored in a Notes Document and are finally passed to the method from above.

Then we create an javax.net.HttpsUrlConnection and simply POST some (here not printed) content to an java.net.Url

RichTextItem rti_cert = (RichTextItem) profile_doc.getFirstItem("ClientCertificate");
EmbeddedObject eo_cert = (EmbeddedObject) rti_cert.getEmbeddedObjects().get(0);

RichTextItem rti_trust = (RichTextItem) profile_doc.getFirstItem("TrustStore");
EmbeddedObject eo_trust = (EmbeddedObject) rti_trust.getEmbeddedObjects().get(0);
               
InputStream trustfileInputStream = eo_trust.getInputStream();
InputStream keyFileInputStream = eo_cert.getInputStream();
               
String client_cert_password = profile_doc.getItemValueString("ClientCertPassword");
String truststore_password = profile_doc.getItemValueString("TrustStorePasswort");                
               
SSLSocketFactory ssl_socket_factory = SSLUtils.getFactory(trustfileInputStream,truststore_password, keyFileInputStream, client_cert_password);
       
System.out.println("Create HTTPS Connection to " + url.toString());
HttpsURLConnection conn;
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssl_socket_factory);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
conn.setRequestMethod("POST");
System.out.println("Writing Content to output stream");
OutputStream os = conn.getOutputStream();
os.write(content.getBytes());
os.close();
System.out.println("Writing Content to output stream done");
               
eo_cert.recycle();
eo_cert = null;
rti_cert.recycle();
rti_cert = null;        
               
eo_trust.recycle();
eo_trust = null;
rti_trust.recycle();
rti_trust = null; 

I hope this post has some information for you to use.

StringInputStream StringOutputStream

Nach einigen Überlegungen und Tests sind mir meine alten StringBufferInputStream und StringBufferOutputStream Konstrukte von ein paar Posts früher irgenwie buggy vorgekommen. Vor allem gab und gibts Schwierigkeiten mit Sonderzeichen – dieses Thema hatte ich bis heute eigentlich total ignoriert.

Nunja, aufgeben darf man ja nie – daher hab ich einen neuen Ansatz versucht, der besser funktioniert – und man muss auch nicht mehr den Umweg über einen StringBuffer machen.

Read more

StringBufferInputStream

UPDATE: siehe auch den neuen Post StrinInputStream StringOutputStream der einige Schwächen dieser Lösung hier beseitigt.

Da programmiert man vor sich hin und vor sich hin, man will ein Java Objekt, oder besser gesagt ein Bean in ein XML-Format bringen (bzw. serialisieren).

Es gibt ja diese wunderbare Library die auch Java RMI verwendet, aber leider kann die nur in Streams schreiben, bzw. von Streams lesen.

Will man aber das XML (warum auch immer) als String haben, kann man den Umweg über ein File gehen – das ist eher doof – oder sich einen StringBuffer-Stream schreiben.

Hört sich vielleicht ein wenig kompliziert an, aber eigentlich sind die Java Streams recht einfache Konstrukte.

Da wäre diese zwei Streamchen zu erwähnen:

Will man nun eigene Streams schreiben, implementiert man einfach seine eigene Java Klasse die von dem jeweiligen Stream erbt.

So sieht dann mein StringBufferInputStream aus:

Read more