Last week I needed to verify an SSL certificate’s trust with Ruby and wasn’t able to find a complete answer anywhere. After much poking around, I came up with the following code:

def trusted?(fullchain_pem)
  certs = fullchain_pem.split(/(?=-----BEGIN)/).map do |pem|
    OpenSSL::X509::Certificate.new(pem)
  end
  store = OpenSSL::X509::Store.new
  store.set_default_paths
  store.verify(certs.first, certs[1..-1])
end

This code:

  1. takes a certificate with its intermediary certs as a PEM-format string,
  2. splits it into individual PEM strings and creates an OpenSSL::X509::Certificate object for each one, and
  3. verifies the chain using an OpenSSL::X509::Store with the operating system’s default trust store.

I needed this for an automated deployment script, which checks whether the existing certificate is trusted and should be kept. Why would there ever be an untrusted cert? When the app is initially deployed, a self-signed certificate is generated if one does not yet exist, allowing nginx (and anything else requiring a cert) to start. Alternatively, during initial setup and testing, a “staging” cert might be obtained, e.g. from Let’s Encrypt’s staging environment.

Once we’re ready for the new production environment to go live, we need to obtain a real certificate from Let’s Encrypt. The code above is used for determining whether the certificate we already have can be keept or needs to be replaced.