Child pages
  • Accessing HTTPS sites with Java tools
Skip to end of metadata
Go to start of metadata

This how-to article does not attempt to explain how SSL encryption in the HTTPS protocol works, how encryption and certificates are managed in the JVM, or any other technical details of how the encryption process works. Instead, it describes the steps required to get your JVM-based tools working again. There are a lot of good technical articles on the Internet that explain all of the gory details, so feel free to dip in if you're so inclined!

You'll often want to access your XNAT site with tools implemented at least in part in Java or JVM-based languages such as Groovy or Scala. If your XNAT site(s) are secured with SSL in the form of HTTPS, you may run into one of a number of different error messages that indicate that your JVM can't connect to the server because of issues with the encryption keys from the server used to establish the secure connection. One example is the SSLHandshakeException:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
        at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1902)
        <snip>
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
        ...

There's also SSLPeerUnverifiedException:

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at sun.security.ssl.SSLSessionImpl.getPeerCertificates(Unknown Source)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:339)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:123)

What these exceptions are both saying in different ways is that your JVM is trying to establish the channel communication with the server but doesn't recognize the SSL encryption key from the server as a valid or trusted key or having come from a valid or trusted certificate authority (CA). Regardless of the cause, it can be very frustrating to be blocked from using your preferred tools because of this issue.

The way to fix this problem is pretty straightforward: get the certificate from the site (making sure that you really do trust the site and the certificate issuer!) and install the certificate in your JVM's trusted certificate store.

Step-by-step guide

You'll need to have a couple tools available before you start:

  • OpenSSL. This is available from the OpenSSL site, but a version is included by default on most *nix-based systems, such as OS X, Ubuntu, CentOS, RHEL, etc.
  • Java JVM. All you need is the JRE, but the JDK also works. For tools with embedded JVMs, you'll need to know the location of that JVM, since each JVM usually has its own certificate store.

Once you have these tools:

  1. Retrieve the site's SSL certificate using the openssl command:

    openssl s_client -showcerts -connect server.domain.tld:443 < /dev/null > server.domain.tld.cer

    Note that server.domain.tld is just the fully-qualified domain name, e.g. www.yahoo.com, and not the HTTPS address! The port 443 is the default post for the HTTPS protocol. If your server uses another port, substitute that in place of 443.

  2. Once the openssl command has completed, open the output file server.domain.tld.cer. At the beginning of this file, you'll see something like this (this shows the certificate from the Wikipedia site; for major sites like this, you don't usually need to add the certificate manually, but this is just to illustrate what these look like):

    CONNECTED(00000003)
    ---
    Certificate chain
     0 s:/C=US/ST=California/L=San Francisco/O=Wikimedia Foundation, Inc./CN=*.wikipedia.org
       i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
    -----BEGIN CERTIFICATE-----
    MII...
    <many lines of incomprehensible gibberish>
    xxxxxxxxxxxxxxx=
    -----END CERTIFICATE-----
  3. You only need the contents of this starting with the line containing BEGIN CERTIFICATE and ending with END CERTIFICATE, so go ahead and delete everything before that first line and after the second and save the file. It should look like this now:

    -----BEGIN CERTIFICATE-----
    MII...
    <many lines of incomprehensible gibberish>
    xxxxxxxxxxxxxxx=
    -----END CERTIFICATE-----
  4. Once you have the certificate from the target site, you can import it with the Java keytool command. The syntax for importing a certificate is:

    keytool -import -trustcacerts -alias server.domain.tld -file server.domain.tld.cer -keystore cacerts

    You'll be prompted for a password. The default Java keystore password is changeit. The value cacerts is the name of the Java keystore into which you want to import the key. By default, unless you specify another keystore, the one that your JVM will use is located under your JAVA_HOME folder at jre/lib/security/cacerts for a JDK and at lib/security/cacerts in a JRE.

And that's it! Once you've imported the certificate into your JVM's keystore, you should be able to use your Java-based tools to access the sites that use that certificate.

If your tools continue to fail after importing the certificate, you most likely have a tool that's somehow using another JDK or JRE, such as an embedded JRE. You'll need to determine the location of the JVM being used in order to find the certificate store for that particular machine.