Export public key to base64 in Ruby

2.8k views Asked by At

I have a public key in PEM format that was generated with:

ecdsa_public_key = OpenSSL::PKey::EC.new ecdsa_private_key
ecdsa_public_key.private_key = nil
ecdsa_public_key.to_pem

I have to read the PEM string and get a base64 url encoded string. How can I do that in Ruby?

ecdsa_public_key = OpenSSL::PKey.read pem_string
ecdsa_public_key.to_base64 # pseudo code...

BTW I have to do this for the WebPush protocol, which states:

you must add your VAPID public key to the Crypto-Key header as a base64 url encoded string with p256ecdsa= prepended to it.

3

There are 3 answers

4
matt On BEST ANSWER

The PEM string actually is base 64 encoded (at least partially), but I don’t think it’s what you want here, it includes other details and I think you want the “raw” public key data.

Here’s one way you can get your key into the format I think you want. It’s a bit long winded but I don’t think Ruby’s OpenSSL bindings provide a more direct method (you’ll need to require "base64" first):

# Assuming the key is in ecdsa_public_key
Base64.urlsafe_encode64(ecdsa_public_key.public_key.to_bn.to_s(2), padding: false)

This calls public_key to get the underlying OpenSSL::PKey::EC::Point, then converts that to an OpenSSL::BN in the correct format, and converts that to a binary string. Finally this string is base64 encoded.

4
kitz On

You could try

  require 'base64'
  Base64.encode64(ecdsa_public_key)

to convert to base64

0
JR Conlin On

Sorry that this is a bit late.

I'm not terribly familiar with Ruby, so I can't offer code examples for what to do, but I can try to describe the process of VAPID. (Also, my apologies if I go into needless detail, since I figure that others might stumble across this.)

In short, VAPID is a Javascript Web Token (JWT). You create a ECDSA key pair using your favorite ECDSA generation method specifically for VAPID.

e.g.

openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem
openssl ec -in vapid_private.pem -pubout -out vapid_public.pem

A "PEM" is a formatted file that includes a standard header line, a footer line, and a set of long strings of crap. (Not sure if that's the technical term for them, but yeah, I'm going to go with that.) Those long strings of crap are Base64 representations of the key data saved in specific formats.

Honestly, I'd STRONGLY encourage using a library where and when possible. jwt.io has a number of Ruby libraries you could use, as well as libraries for other languages. As for the "Crypto-Key:" header, there's a bit of good news/other news.

  1. You could just take the Long Strings of Crap from your vapid-public.pem file, append them together and specify them as the 'p256ecdsa=' key.
  2. The VAPID protocol is changing soon https://datatracker.ietf.org/doc/html/draft-ietf-webpush-vapid-02

The change effectively gets rid of the Crypto-Key p256ecdsa component. Instead, the Authorization key becomes:

Authorization: vapid t=JWT containing VAPID info,k=VAPID Public key

e.g.

vapid t=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtYWlsdG86d2VicHVzaF9vcHNAY2F0ZmFjdHMuZXhhbXBsZS5jb20iLCJleHAiOjE0ODc0NDM3MTR9.mlLOWYMt-6aM3NB6b6_Msf8LqRKCuHd1Vfdp_fuJ3eqsQoID8lit305hIfNubTbvfACucuCygF3qB4scDbuHvg,k=EJwJZq_GN8jJbo1GGpyU70hmP2hbWAUpQFKDByKB81yldJ9GTklBM5xqEwuPM7VuQcyiLDhvovthPIXx-gsQRQ

I'm of mixed opinion about this. It does reduce the need for a separate header, but also shortens the amount of info you can shove into your claims before you run out of header room.