I was setting up my environment to write a quick API wrapper in Ruby 2.2.1. On paper, that should be an easy task: install rvm, then do rvm install 2.2, create your dotfiles, gemset, write a couple lines of code and you're testing that API.

Then the API fails with some weird certificate error, but your browser does not complain. Time to discern if your HTTP gem is the problem. A quick way to tell if Ruby itself has SSL issues is using irb to request some HTTPS site like Google or Github:

MacbookPro:~ gerard$ irb
2.2.0 :001 > require 'open-uri'; open 'https://google.com'
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:923:in `connect'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:923:in `block in connect'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/timeout.rb:74:in `timeout'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:923:in `connect'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:863:in `do_start'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/net/http.rb:852:in `start'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:318:in `open_http'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:736:in `buffer_open'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:211:in `block in open_loop'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:209:in `catch'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:209:in `open_loop'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:150:in `open_uri'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:716:in `open'
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/open-uri.rb:34:in `open'
    from (irb):1
    from /Users/gerard/.rvm/rubies/ruby-2.2.1/bin/irb:11:in `<main>'

Now, we know that the issue is on the base Ruby and not some gem. Also, we can tell that the issue has something to be with OpenSSL.

OpenSSL and OSX

Apple has deprecated usage of OpenSSL on OSX since version 10.7 Lion and migrated to their own library called Common Crypto. Honestly, based on my expexperience with OpenSSL, I can't blame them.

On my Mac computers, running OSX 10.11 El Capitan, there's still an OpenSSL installation to be found:

MacbookPro:~ gerard$ openssl version -a
OpenSSL 0.9.8zg 14 July 2015
built on: Jul 31 2015
platform: darwin64-x86_64-llvm
options:  bn(64,64) md2(int) rc4(ptr,char) des(idx,cisc,16,int) blowfish(idx)
compiler: -arch x86_64 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -fasm-blocks -O3 -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DL_ENDIAN -DMD32_REG_T=int -DOPENSSL_NO_IDEA -DOPENSSL_PIC -DOPENSSL_THREADS -DZLIB -mmacosx-version-min=10.6
OPENSSLDIR: "/System/Library/OpenSSL"

The problem here is that Certificate Authorities are not updated by Apple since they assume you'll be using Common Crypto. This is the underlying issue with Ruby when installed by RVM: the binaries were linked against this outdated OpenSSL library. Also, I assume it was compiled statically, because even manually updating certificates for OpenSSL does not fix the issue.

The fix

Fortunately, RVM gives us the option of ignoring precompiled binary versions of Ruby and just download the source code and complile it on our own system instead. But, in order for us to compile against an up-to-date CAs list, we need to update OpenSSL beforehand.

I recommend doing so by installing OpenSSL's last Homebrew package. If you already have OpenSSL installed, I recommend removing it altogether. Please notice that it's possible that just issuing an uninstall command may not suffice. We want to uninstall ALL versions of OpenSSL with uninstall --force:

MacbookPro:~ gerard$ brew uninstall openssl
Uninstalling /usr/local/Cellar/openssl/1.0.2f... (466 files, 11.9M)
openssl 1.0.2a-1, 1.0.2d_1, 1.0.2e are still installed.
Remove them all with `brew uninstall --force openssl`.
MacbookPro:~ gerard$ brew uninstall --force openssl
Uninstalling openssl... (1,392 files, 35.7M)

We can now install OpenSSL again. The brew formula is actually very well thought because it will automatically import Common Crypto's CAs into OpenSSL at installation time. This means we'll be using the very same lists Apple (and most apps) are using:

MacbookPro:~ gerard$ brew install openssl
==> Downloading https://homebrew.bintray.com/bottles/openssl-1.0.2f.el_capitan.bottle.tar.gz
Already downloaded: /Library/Caches/Homebrew/openssl-1.0.2f.el_capitan.bottle.tar.gz
==> Pouring openssl-1.0.2f.el_capitan.bottle.tar.gz
==> Caveats
A CA file has been bootstrapped using certificates from the system
keychain. To add additional certificates, place .pem files in
  /usr/local/etc/openssl/certs

and run
  /usr/local/opt/openssl/bin/c_rehash

This formula is keg-only, which means it was not symlinked into /usr/local.

Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries

Generally there are no consequences of this for you. If you build your
own software and it requires this formula, you'll need to add to your
build variables:

    LDFLAGS:  -L/usr/local/opt/openssl/lib
    CPPFLAGS: -I/usr/local/opt/openssl/include

==> Summary
🍺  /usr/local/Cellar/openssl/1.0.2f: 466 files, 11.9M

Now, it's time to reinstall Ruby, compiling it against our up-to-date OpenSSL. First, we'll remove the current Ruby installation:

MacbookPro:~ gerard$ rvm remove 2.2
ruby-2.2.1 - #removing rubies/ruby-2.2.1..
ruby-2.2.1 - #removing gems....
ruby-2.2.1 - #removing aliases
ruby-2.2.1 - #removing wrappers....
ruby-2.2.1 - #removing environments....

And then we'll install it again with the --disable-binary flag, so it compiles from source:

MacbookPro:~ gerard$ rvm install 2.2 --disable-binary
Checking requirements for osx.
Certificates in '/usr/local/etc/openssl/cert.pem' are already up to date.
Requirements installation successful.
Installing Ruby from source to: /Users/gerard/.rvm/rubies/ruby-2.2.1, this may take a while depending on your cpu(s)...
ruby-2.2.1 - #downloading ruby-2.2.1, this may take a while depending on your connection...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12.7M  100 12.7M    0     0  4548k      0  0:00:02  0:00:02 --:--:-- 4547k
ruby-2.2.1 - #extracting ruby-2.2.1 to /Users/gerard/.rvm/src/ruby-2.2.1....
ruby-2.2.1 - #applying patch /Users/gerard/.rvm/patches/ruby/2.2.1/fix_installing_bundled_gems.patch.
ruby-2.2.1 - #configuring..........................................................
ruby-2.2.1 - #post-configuration.
ruby-2.2.1 - #compiling..............................................................................................................................|
ruby-2.2.1 - #installing..........
ruby-2.2.1 - #making binaries executable..
ruby-2.2.1 - #downloading rubygems-2.4.8
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  437k  100  437k    0     0   244k      0  0:00:01  0:00:01 --:--:--  244k
No checksum for downloaded archive, recording checksum in user configuration.
ruby-2.2.1 - #extracting rubygems-2.4.8....
ruby-2.2.1 - #removing old rubygems.........
$LANG was empty, setting up LANG=en_US, if it fails again try setting LANG to something sane and try again.
ruby-2.2.1 - #installing rubygems-2.4.8......................
ruby-2.2.1 - #gemset created /Users/gerard/.rvm/gems/ruby-2.2.1@global
ruby-2.2.1 - #importing gemset /Users/gerard/.rvm/gemsets/global.gems...............................................
ruby-2.2.1 - #generating global wrappers........
ruby-2.2.1 - #gemset created /Users/gerard/.rvm/gems/ruby-2.2.1
ruby-2.2.1 - #importing gemsetfile /Users/gerard/.rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.2.1 - #generating default wrappers........
ruby-2.2.1 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
Install of ruby-2.2.1 - #complete
Ruby was built without documentation, to build it run: rvm docs generate-ri

Now, let's test an HTTPS site to check that we've got it fixed:

MacbookPro:~ gerard$ irb
2.2.1 :001 > require 'open-uri'
 => true
2.2.1 :002 > open 'https://google.com'
 => #<Tempfile:/var/folders/z8/pk4_glmj6_1bymrqlzsjh2wm0000gn/T/open-uri20160213-36174-oatt8l>
2.2.1 :003 >

And it is!