Wednesday, 16 January 2013

Tech: How to Fix 'SSLPeerUnverifiedException: peer not authenticated' Exception in Groovy / Java

This is the first in a series of tech posts on the NerdAbility blog. We are aiming to blog any useful tips / gotchas we come across when developing with various technologies. We will still be posting tech recruitment insights and NerdAbility news, so stay tuned!

How to Fix 'SSLPeerUnverifiedException: peer not authenticated' Exception in Java / Groovy


When developing with web services in Java you may come across the need to connect to a HTTPS URL, for example when creating a REST client. In some cases there will be an issue with the type of certificate the web server is using, resulting in a SSLPeerUnverifiedException.

To solve this you could previously export the servers SSL certificate via firefox / chrome and load this directly into the cacerts keystore (jvm's default trusted keystore). In recent versions of firefox / chrome this feature seems to have disappeared.  In this post we will show you how to grab the certificate using command line tools and then load it into the cacerts keystore. Finally we give an example of connecting to a HTTPS URL with Groovy using RESTClient.

Please note this guide is for Linux / Mac users. Windows users may be able to follow along using cygwin, but we have not tested this. If you are using an alternative trusted keystore in your application, use this instead of cacerts in the examples.

Prerequisites: Before loading any key into your cacerts keystore, please verify you are happy with the certificate and its authenticity, and issuer. You can do this by using a tool like this one.

Disclaimer: Follow this guide at your own risk, we can not be held liable / accountable for any damage or issues caused to you or your systems.

Step 1: Download and Store the Certificate


To download and store the certificate run the following command, changing $ADDRESS for the sites address. For example https://www.facebook.com would become facebook.com:

echo -n | openssl s_client -connect $ADDRESS:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > /tmp/$ADDRESS.cert

To check the certificate was grabbed, you can run:

cat /tmp/$ADDRESS.cert

This will output the certificate and you should see something like:

-----BEGIN CERTIFICATE-----
*DATA*
-----END CERTIFICATE-----


Step 2: Load this into the default keystore for the JVM CACERTS


First of all you need to locate the cacerts keystore for the JRE you are using. To find out the version of java run the following command:

java -version

This should give you something similar to:

java version "1.6.0_11" Java(TM) SE Runtime Environment (build 1.6.0_11-b03) Java HotSpot(TM) 64-Bit Server VM (build 11.0-b16, mixed mode)

Next take the Java version number from the previous output, in this case 1.6.0_11 and use locate to find the cacerts keystore for this Java install:

locate cacerts | grep "1.6.0_11"

The output should give you something similar to:

/usr/lib/jvm/jre1.6.0_11/lib/security/cacerts

Now you have enough information to import the key into the keystore. Run the following command, replacing the $ADDRESS with the address variable you used earlier, the $ALIAS with a name for the certificate i.e. facebook. Replace the $PATH variable with the path to the cacert (the output from the locate command we just ran). Also we have added the -storepass argument, passing the default password for the cacerts keystore. You will want to change this, if you have not already, and should be prompted to do so.

sudo keytool -importcert -alias "$ALIAS" -file /tmp/$ADDRESS.cert -keystore $PATH/cacerts -storepass changeit

Once you run this you will be shown the certificate and prompted to confirm you want to import the certificate:

Trust this certificate? [no]:  yes
Certificate was added to keystore

Now you should have the certificate ready for use in your application, providing it is configured to use the default keystore and runs on the JVM we configured the certificate for!

Step 3: Test It!


Here is some example Groovy code using RESTClient:



14 comments:

  1. Its also possible to override the default behaviour for ssl handling by creating a custom TrustManager. This alleviates the potential nightmare that you might bump into when moving code through different environments etc. See SSLContext init for more.

    ReplyDelete
    Replies
    1. Hey Michael, any chance you could elaborate on this? My organization has so many servers they face the nightmare you describe. I would like to create my own TrustManager, but looking at the interface (http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/TrustManager.html) there are no methods to implement. Is it expected that creating an empty class that implements TrustManager will alleviate the need for peer authentication?

      Context:
      I'm trying to fix a few operational scripts written in groovy that use HttpBuilder and throw this error. Authenticaion fails because the scripts are hitting individual server nodes by IP (not by hitting apache or a load balancer). My understanding is that the SSL cert's hostname does not match the IP address and this causes the error. Creating self signed certs to fake the match is not a viable solution for this problem.

      Delete
    2. Provided example : https://gist.github.com/patelm5/8820842

      Delete
  2. Thanks, that was exactly what I was looking for :-)

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Thanks for the openssl command, I too used to use FF to do the same thing.

    If you want to avoid adding extra certificates to your JDK you could create a copy and use that instead, e.g.

    cp $JAVA_HOME/lib/security/cacerts /tmp

    then specify the copy when running your app;

    java -Djavax.net.ssl.trustStore=/tmp/cacerts com.test.Main

    ReplyDelete
  5. Hi,
    Your code was exceptionally useful. My legal department is asking whether you could license this code snippet to us or put forth some expression about it being public domain. In the event that you attach an Apache 2.0 or BSD style license, that would be simplest.
    Much thanks to you!
    ~~~~~~~~~~~~~~~~~~>>
    top hidden object games

    ReplyDelete
  6. Thanks, it really helped to resolve my issue.

    ReplyDelete
  7. The information you posted here is useful to make my career better keep updates..If anyone want to become an oracle certified professional reach FITA, which offers Best Oracle Training in Chennai with years of experienced professionals.

    ReplyDelete
  8. Hi, This is Jamuna from Chennai. I am a technology freak. I have read your blog, its really useful for me. Recently I did Java Course in Chennai at a leading Java Institutes in Chennai. This is really helpful for me to make a bright career in IT industry.



    ReplyDelete
  9. This is excellent information. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me...
    Android Training in Chennai
    Ios Training in Chennai

    ReplyDelete
  10. You have shared useful information. Thanks for sharing your valuable knowledge with us.
    Oracle dba training | Oracle dba training syllabus

    ReplyDelete