Skip to content

Instantly share code, notes, and snippets.

@WilliamNHarvey
Last active September 12, 2024 17:35
Show Gist options
  • Save WilliamNHarvey/0e37f84a86e66f9acb7ac8c68b0f996b to your computer and use it in GitHub Desktop.
Save WilliamNHarvey/0e37f84a86e66f9acb7ac8c68b0f996b to your computer and use it in GitHub Desktop.
Generate an instance of OpenSSL::PKey::RSA from `n` and `e` in OpenSSL 3 on Ruby
# Given n and e in typical encoding, like that found on a jwks well-known.
# For example for google, from https://www.googleapis.com/oauth2/v3/certs
n = "t0VFy4n4MGtbMWJKk5qfCY2WGBja2WSWQ2zsLziSx9p1QE0QgXtr1x85PnQYaYrAvOBiXm2mrxWnZ42MxaUUu9xyykTDxsNWHK--ufchdaqJwfqd5Ecu-tHvFkMIs2g39pmG8QfXJHKMqczKrvcHHJrpTqZuos1uhYM9gxOLVP8wTAUPNqa1caiLbsszUC7yaMO3LY1WLQST79Z8u5xttKXShXFv1CCNs8-7vQ1IB5DWQSR2um1KV4t42d31Un4-8cNiURx9HmJNJzOXbTG-vDeD6sapFf5OGDsCLO4YvzzkzTsYBIQy_p88qNX0a6AeU13enxhbasSc-ApPqlxBdQ"
e = "AQAB"
rsa = create_rsa_key(n, e)
def create_rsa_key(n, e)
data_sequence = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(base64_to_long(n)),
OpenSSL::ASN1::Integer(base64_to_long(e))
])
asn1 = OpenSSL::ASN1::Sequence(data_sequence)
OpenSSL::PKey::RSA.new(asn1.to_der)
end
def base64_to_long(data)
decoded_with_padding = Base64.urlsafe_decode64(data) + Base64.decode64("==")
decoded_with_padding.to_s.unpack("C*").map do |byte|
byte_to_hex(byte)
end.join.to_i(16)
end
def byte_to_hex(int)
int < 16 ? "0" + int.to_s(16) : int.to_s(16)
end
@eggei
Copy link

eggei commented Oct 2, 2023

OMG, I love you. Thanks so much for putting this up here. ❤️ I was having all kinds of issues finding the right way to do this in OpenSSL v3 and I was in the death spiral of "copy -> past -> fail -> again"...

@jstanley0
Copy link

jstanley0 commented Feb 23, 2024

Thank you! I ran into this problem and was shocked that there is no PKey::RSA constructor that will do this for you.

I tested this in an environment with OpenSSL 1.x and verified that I get the same results as with set_key so this looks good.

I noticed, however, that data_sequence.to_der and asn1.to_der are identical, and this surprises me.

OpenSSL::ASN1.decode(asn1) shows there is just one Sequence there, not a Sequence in a Sequence. You need to pass an array to the Sequence constructor on line 13 to make that happen. But then the RSA constructor rejects it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment