Skip to content
ecdsa.com

PEM ⇄ DER · Ruby

How to convert a certificate and a private key between PEM and DER in Ruby

To convert a certificate between PEM and DER in Ruby, read it with OpenSSL::X509::Certificate.new (which accepts both) and write it with to_der or to_pem. For private keys, OpenSSL::PKey.read reads either form and private_to_der / private_to_pem write PKCS#8. This recipe converts a certificate, a key and its public key, and checks the round trip byte for byte.

Tested with openssl (Ruby gem) 4.0.2 (Ruby 4.0.7, OpenSSL 3.5.7) and OpenSSL 3.5.7 (reads the DER output).

convert.rb — certificate, private key and public key, PEM ⇄ DER
require "openssl"
# Certificate: the constructor reads PEM or DER; to_der and to_pem write either
cert = OpenSSL::X509::Certificate.new(File.binread("cert.pem"))
File.binwrite("cert.der", cert.to_der)
File.write("cert-again.pem", OpenSSL::X509::Certificate.new(File.binread("cert.der")).to_pem)
# Private key: private_to_der / private_to_pem write PKCS#8 for every key type
key = OpenSSL::PKey.read(File.binread("key.pem")) # PEM or DER; pass a password for encrypted keys
File.binwrite("key.der", key.private_to_der)
File.write("key-again.pem", OpenSSL::PKey.read(File.binread("key.der")).private_to_pem)
# Public key: SubjectPublicKeyInfo DER
File.binwrite("pub.der", key.public_to_der)

How it works

  1. OpenSSL::X509::Certificate.new(File.binread(path)) takes PEM or DER; to_der writes the 563-byte DER, to_pem the PEM — identical to the original file.
  2. OpenSSL::PKey.read loads any key type from PEM or DER (pass a password as the second argument for encrypted keys); private_to_der writes PKCS#8 DER (138 bytes here).
  3. public_to_der writes the SubjectPublicKeyInfo — the public key in the form other libraries import.
  4. Use File.binread / File.binwrite for DER, so no encoding conversion touches the bytes.

Gotchas

  • key.to_der and key.to_pem on an EC key write SEC1, not PKCS#8: 121 bytes, version INTEGER 1, and -----BEGIN EC PRIVATE KEY----- in PEM. Use private_to_der / private_to_pem for PKCS#8.
  • OpenSSL::X509::Certificate.new reads one certificate; for a bundle, OpenSSL::X509::Certificate.load(text) returns all of them (2 in the run), each with its own to_der.

Related recipes