require "openssl" def load(path) = OpenSSL::X509::Certificate.new(File.read(path)) def verify_chain(leaf, intermediates, root) store = OpenSSL::X509::Store.new store.add_cert(root) # trusted; store.set_default_paths would add the system roots store.purpose = OpenSSL::X509::PURPOSE_SSL_SERVER ok = store.verify(leaf, intermediates) # intermediates: untrusted candidates for the path [ok, ok ? store.chain.map { |c| c.subject.to_a.find { |n, _, _| n == "CN" }[1] } : store.error_string]end root = load("root.pem")intermediate = load("intermediate.pem"){ "good chain" => [load("leaf.pem"), [intermediate], root], "impostor root" => [load("leaf.pem"), [intermediate], load("impostor-root.pem")], "expired leaf" => [load("expired.pem"), [intermediate], root], "missing intermediate" => [load("leaf.pem"), [], root],}.each do |label, (leaf, intermediates, trusted)| ok, detail = verify_chain(leaf, intermediates, trusted) puts "#{label}: #{ok} #{ok ? detail.join(' -> ') : "(#{detail})"}"end How it works
store.add_cert(root)adds a trust anchor;store.set_default_pathswould add the system roots instead.store.purpose = OpenSSL::X509::PURPOSE_SSL_SERVERrequires certificates fit for a TLS server along the path.store.verify(leaf, intermediates)builds the path through the untrusted intermediates and returnstrueorfalse.- After a successful check,
store.chainis the path:api.example.com → ECDSA.com Howto Issuing CA 1 → ECDSA.com Howto Root CA.
Gotchas
- After a failure, read
store.error_string(andstore.errorfor the number):unable to get local issuer certificatefor both the look-alike root and the missing intermediate,certificate has expiredfor the expired leaf. verifyreturnsfalserather than raising, so a forgotten check of the return value accepts every chain.store.time = Time.utc(2025, 2, 1)validates at another moment — the expired leaf then verifiestrue— which suits old signatures, never as a workaround for an expired certificate.
Related recipes
- How to verify a certificate chain against your own trust store in PHP
- How to verify a certificate chain against your own trust store in Python
- How to verify a certificate chain against your own trust store with OpenSSL
- How to check SSL certificate expiration date in Ruby
- How to decode an X.509 certificate in Ruby