Java CertificateException in Domino 9 when trying to access HTTPS URL

When we upgraded our Domino development server from 8.5.3 to 9 the HTTPS connections from Java code to sites having GoDaddy certificate stopped working. Connections to servers having DigiCert certificate work fine. This happens in both agents and XPages.

Here is an XPage sample code:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:this.beforePageLoad>
             <![CDATA[#{javascript:new java.net.URL("https://www.sslshopper.com/").openStream();]]>
    </xp:this.beforePageLoad>
</xp:view>

I've also tried with UrlConnection. Here is the exception:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: 3659
    com.ibm.jsse2.o.a(o.java:15)
    com.ibm.jsse2.SSLSocketImpl.a(SSLSocketImpl.java:460)
    com.ibm.jsse2.kb.a(kb.java:294)
    com.ibm.jsse2.kb.a(kb.java:533)
    com.ibm.jsse2.lb.a(lb.java:55)
    com.ibm.jsse2.lb.a(lb.java:581)
    com.ibm.jsse2.kb.s(kb.java:11)
    com.ibm.jsse2.kb.a(kb.java:394)
    com.ibm.jsse2.SSLSocketImpl.a(SSLSocketImpl.java:44)
    com.ibm.jsse2.SSLSocketImpl.h(SSLSocketImpl.java:496)
    com.ibm.jsse2.SSLSocketImpl.a(SSLSocketImpl.java:528)
    com.ibm.jsse2.SSLSocketImpl.startHandshake(SSLSocketImpl.java:505)
    com.ibm.net.ssl.www2.protocol.https.c.afterConnect(c.java:83)
    com.ibm.net.ssl.www2.protocol.https.d.connect(d.java:31)
    sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1184)
    com.ibm.net.ssl.www2.protocol.https.b.getInputStream(b.java:40)
    java.net.URL.openStream(URL.java:1022)

...

java.security.cert.CertificateException: 3659
com.ibm.domino.napi.ssl.DominoX509TrustManager.checkServerTrusted(DominoX509TrustManager.java:98)
    com.ibm.jsse2.lb.a(lb.java:468)
    com.ibm.jsse2.lb.a(lb.java:581)
    com.ibm.jsse2.kb.s(kb.java:11)
    com.ibm.jsse2.kb.a(kb.java:394)
    com.ibm.jsse2.SSLSocketImpl.a(SSLSocketImpl.java:44)
    com.ibm.jsse2.SSLSocketImpl.h(SSLSocketImpl.java:496)
    com.ibm.jsse2.SSLSocketImpl.a(SSLSocketImpl.java:528)
    com.ibm.jsse2.SSLSocketImpl.startHandshake(SSLSocketImpl.java:505)
    com.ibm.net.ssl.www2.protocol.https.c.afterConnect(c.java:83)
    com.ibm.net.ssl.www2.protocol.https.d.connect(d.java:31)
    sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1184)
    com.ibm.net.ssl.www2.protocol.https.b.getInputStream(b.java:40)
    java.net.URL.openStream(URL.java:1022)

I imported the GoDaddy certificates to domino_path\jvm\lib\security\cacerts keystore according to these instructions:

http://drcs.ca/blog/adding-godaddy-intermediate-certificates-to-java-jdk/

But that did not help and I also imported gd-class2-root.crt with no results. I also tried renaming the cacerts file and copying the one from 8.5.3 server but it did not help either. I've been booting HTTP and Domino server after these changes.

Of course I could use Java code that does not care about certificates but I believe that's not a great solution for production. Also we have code in a lot of different places (including JARs) which make HTTPS connection to this URL.

Update 1

This is in error-log-0.xml:

Certificate with subject CN=www.sslshopper.com, OU=Domain Control Validated, O=www.sslshopper.com, issued by SERIALNUMBER=07969287, CN=Go Daddy Secure Certification Authority, OU=http://certificates.godaddy.com/repository, O="GoDaddy.com, Inc.", L=Scottsdale, ST=Arizona, C=US, is not trusted. Validation failed with error 3659.

I think this message is quite clear. I also noticed that System.getProperty("javax.net.ssl.trustStore") returns null but that happens also in 8.5.3 server where this works. I tried setting the trustStore with setProperty but the error is still the same.

Update 2

It works with Simon's code that uses createSocket. But all our code use java.net.URL, UrlConnection, HttpsUrlConnection or Apache HTTP Client. Some of these are provided by 3rd party as a JAR. We cannot change all those to use createSocket.

Any ideas? Thanks.


Solution 1:

So for Domino 9, as the new feature OpenSocial was added, a lot of functionality was changed around certificates to make it easier to maintain.

As such there is a new API "com.ibm.domino.napi.ssl.DominoX509TrustManager". What this API does is now check the Domino certificates for the related trusted certificate which is also cross certified against the server certificate.

It checks first in the CACERTS. If it can't find this, then it will check the Domino certificates.

So in order to get the code above to work you need to do the following.

  1. Open the Domino Administrator client and select the ""People and Groups -> Certificates".

    people and groups tab, certificates view

  2. In the actions menu select "Import Internet Certificates".

    menu setting

  3. Select the related certificate file to import using the file dialog. It may ask you for the file structure when importing, so select the correct structure if needed. It should appear in the view once completed.

    View showing certificate imported.

  4. Open the document and from the actions menu select "Create Cross Certificate".

    menu option of create cross certificate

  5. A dialog pops up. You select the certificate and click OK.

    cross certificate dialog

  6. Next dialog make sure you have it set correctly for your server. ie. The right server and certifier. Click Cross Certify when done.

    Issue cross certificate dialog

This should create a cross certificate in the view:

view showing cross certificate

At this point you need to restart the HTTP process with the following commands.

tell http quit 
load http