Skip to content
ecdsa.com

Public key from a certificate · Ruby

How to extract the public key from an X.509 certificate in Ruby

To extract the public key from an X.509 certificate in Ruby, read cert.public_key from OpenSSL::X509::Certificate and write it with public_to_pem: a "BEGIN PUBLIC KEY" SubjectPublicKeyInfo. This recipe also computes the key's SHA-256 fingerprint and base64 pin, and checks a private key with check_private_key.

Tested with openssl (Ruby gem) 4.0.2 (Ruby 4.0.7, OpenSSL 3.5.7).

extract.rb — SPKI PEM, fingerprint, pin and the key-pair check
require "base64"
require "openssl"
cert = OpenSSL::X509::Certificate.new(File.binread("cert.pem")) # PEM or DER
public_key = cert.public_key # an OpenSSL::PKey::EC (or ::RSA, ...) holding only the public half
puts public_key.public_to_pem # SubjectPublicKeyInfo, "BEGIN PUBLIC KEY"
digest = OpenSSL::Digest::SHA256.digest(public_key.public_to_der)
puts "SPKI SHA-256: #{digest.unpack1('H*').scan(/../).join(':')}"
puts "SPKI pin (base64): #{[digest].pack('m0')}"
# Does key.pem belong to this certificate?
private_key = OpenSSL::PKey.read(File.read("key.pem"))
puts "key.pem matches: #{cert.check_private_key(private_key)}"

How it works

  1. cert.public_key returns an OpenSSL::PKey (here an EC key) holding only the public half, ready for verify.
  2. public_to_pem writes the SubjectPublicKeyInfo PEM and public_to_der its DER, for every key type.
  3. SHA-256 of the SPKI DER is the key fingerprint (45:ca:a7:e3:…:7b:5f), and its base64 (Rcqn43J0…e18=) the SPKI pin.
  4. cert.check_private_key(key) returns true for the certificate's own key and false for any other (a freshly generated P-256 key in the run).

Gotchas

  • OpenSSL::Digest::SHA256.hexdigest(cert.to_der) is the certificate fingerprint (cdb1a140…), not the key's; it changes at every renewal.
  • Use public_to_pem / public_to_der rather than to_pem / to_der: on a key object that also holds a private part, the latter export the private key.
  • check_private_key compares key pairs, it does not verify the certificate — trust still needs an OpenSSL::X509::Store.

Related recipes