Detecting SSL details/expiry for an arbitrary URL?

Just wondering if anyone has ever created anything like this and could help me get started on it?

That is, some way to create a function like get_ssl_expiry_date(url) or get_ssl_details(url) for any URL.

I’m trying to create a simple uptime and SSL monitor for fun to help me detect when my sites are down or SSL is going to expire. But I’m not really sure where to start on detecting the SSL details. Ideally I’d make use of a library like Httpoison or similar but I’m not sure if that would be enough.

Thanks for any help!

You may be interested in this blog. The referenced site_encrypt library contains a get_cert function which may give you some ideas.

1 Like

Most HTTP clients do not expose the SSL socket to the caller, and to inspect the server’s certificate you don’t need to send an HTTP request at all, so I would recommend you just go with :ssl directly.

For example, using x509 to inspect the certificate:

with {:ok, sock} <- :ssl.connect(to_charlist(hostname), 443, []),
     {:ok, cert_der} <- :ssl.peercert(sock),
     :ssl.close(sock),
     {:ok, cert} <- X509.Certificate.from_der(cert_der),
     {:Validity, _not_before, not_after} <- X509.Certificate.validity(cert) do
  X509.DateTime.to_datetime(not_after)
end

Note that this does not enable certificate verification, so in theory someone could launch a MitM attack and present a spoofed certificate. Not sure that’s a real concern here (it should be if you were to use the SSL connection for anything non-trivial), but you may want to add additional ssl options to address that…

(I just remember I proposed almost exactly that snippet before: Get SSL expiry date from an http request using Mint / Finch / Mojito)

5 Likes

Thanks, that’s just what I was hoping to learn! I had also created this function below, which I won’t be using because it’s much more brittle than using Mint like you mentioned. But just in case someone is looking for an alternative in the future:

def check_ssl_expiry(url) do
    {result, 0} = System.cmd("sh", ["-c", "echo | openssl s_client -connect #{url}:443 2>/dev/null | openssl x509 -noout -dates |awk -F= '/^notAfter/ { print $2; exit }'"])
    {:ok, expiry_datetime} = result
    |> String.replace("\n", "")
    |> String.split # remove extra whitespaces
    |> Enum.join(" ") # because sometimes it adds extra for some reason
    |> Timex.parse("{Mshort} {D} {h24}:{m}:{s} {YYYY} {Zabbr}")

    expiry_datetime
  end
1 Like