Last Friday I was trying out some new code that one of mycolleagues wrote to help automate some of the work involved in releasing newversions of the TrustKeeper Scan engine. One of the many things the code did was send emails. I hate writingboilerplate emails, so I was excited to put it to use and save myself some precioustime. Unfortunately, when I ran the codefor the first time, it crashed with the following error when trying to connect to our Exchange Server...
Now, this error is pretty self-explanatory and having spenttime working with other Ruby libraries that utilize OpenSSL, this basicallymeans that we're failing to verify the certificate of the server we'reconnecting to. The interesting part tome, was that when I visit this URL with Chrome and other web browsers, theysuccessfully verify the certificate provided. Weird huh?
In this blog post, I'll explain some of the digging around Ihad to do to get to the bottom of this issue and some other interesting bits Ifound along the way.
A Gem, Inside a Gem, Inside a Gem, Inside a Gem
First of all, Ruby Gems are pretty cool because you can usethem as building blocks to build something bigger badder and meaner. One of the tricky aspects of having such astructure like this is tracking down who's responsible for an error when yourun into problems.
In our case, we were using the Ruby Viewpoint gem. The Viewpoint gem provides a thin layer ontop of Microsoft Exchange Web Service (EWS) and lets you do all kinds of funthings with Exchange, including sending emails. After getting the above error, I was able to track the failure down throughthe gem dependency chain down to it's source, which turned out to be just a couplegems deep.
- Layer 1: Viewpoint Gem - A light layer for talking Exchange Web Services
- Layer 2: Handsoap Gem - A library for creating SOAP clients in Ruby
- Layer 3: httpclient - A library for http protocol communication
- Layer 4: OpenSSL - A library that interfaces with native OpenSSL
Certificate Verification Nuances in Ruby OpenSSL
So to get right down to it, we're basically trying toestablish an SSL wrapped socket with the target service. We can do this quite easily using Ruby OpenSSL: