Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No success trying to import an ECDSA public key from a JWKS #542

Closed
kplattret opened this issue Jan 9, 2023 · 2 comments
Closed

No success trying to import an ECDSA public key from a JWKS #542

kplattret opened this issue Jan 9, 2023 · 2 comments

Comments

@kplattret
Copy link

kplattret commented Jan 9, 2023

I'm trying to import an ECDSA public key from a JWKS using jwt (2.6.0) and it's consistently failing. I have tried with different JWKS values and I'm getting the same results each time. Below is a summary of what I'm doing, in a Pry console.

First I retrieve the relevant JWK value from the JWKS:

expected_kid = "45fc75cf-5649-4134-84b3-192c2c78e990"
jwks_json = "{\"keys\":[{\"kty\":\"RSA\",\"alg\":\"RS512\",\"kid\":\"db2e0bb4-e33d-4fc6-a8c5-645e70331127\",\"n\":\"piC9AfXk1FyhocC77L42j8zWhy-jOeuxdvZCRYACPbU5DB-4zd5JLpTFuYPY5LweqT8tmBbqgXMN_35EQifHrLbBB5a5p-0z3QlVhqMP_K4nu65uIVzC-wjKBcTctqY_jFzSotFZnDNCZEUXcGrTzQifOCy1OD7zT0jm7-1ceWs375joA-KnyJj5rVKBmYrLWa6E2ukLhNBHFzO2N7Aai2w6UFmwfG27viV3_aNkDVC6vkEtZx4oZuCtQ6DKmbiQBTiS1-41wU7QN5PTuA9YUlhOU9yiwnCAG9MDWuJHT7691iLvIIgRctuSQ4HDDg49M39Un4DBt51KCoZsAwxtNw\",\"e\":\"AQAB\"},{\"kty\":\"EC\",\"alg\":\"ES512\",\"kid\":\"45fc75cf-5649-4134-84b3-192c2c78e990\",\"crv\":\"P-521\",\"x\":\"oLuW4nFqoUuR8d7FVsUoI62libYdQlWGtLStnqdudLY92bQ0ra5eZAbkunrGTR9-w9mr4o2Etyb6pC7YB2-23WM\",\"y\":\"AX1cjkGMiltikPrkX49qwuJDdcETaTsj-kyFP8jsF9W5XAB3Z4tBiQtc72DQnJYeKyAV_T6qZTtFKFr-Tp4iu-j7\"}]}"
jwks_hash = JSON.parse(jwks_json, symbolize_names: true)
jwk = jwks_hash[:keys].find { |key| key[:kid] == expected_kid }
# => {:kty=>"EC", :alg=>"ES512", :kid=>"45fc75cf-5649-4134-84b3-192c2c78e990", :crv=>"P-521", :x=>"oLuW4nFqoUuR8d7FVsUoI62libYdQlWGtLStnqdudLY92bQ0ra5eZAbkunrGTR9-w9mr4o2Etyb6pC7YB2-23WM", :y=>"AX1cjkGMiltikPrkX49qwuJDdcETaTsj-kyFP8jsF9W5XAB3Z4tBiQtc72DQnJYeKyAV_T6qZTtFKFr-Tp4iu-j7"}

Then I try to import the public key from the JWK and I get this error:

JWT::JWK::EC.import(jwk).public_key
# OpenSSL::PKey::EC::Point::Error: EC_POINT_bn2point: invalid encoding

Following the work-round suggested in this issue, I replace line 139 of the JWT::JWK::EC.create_ec_key method with OpenSSL::BN.new([0x04, x_octets, y_octets].pack(''), 2).

I try to import the public key again and at first it looks like it might work, but the key is deemed invalid:

public_key = JWT::JWK::EC.import(jwk).public_key
# => #<OpenSSL::PKey::EC::Point:0x00000001013fba18 @group=#<OpenSSL::PKey::EC::Group:0x00000001013fb9f0>>
public_key.check_key
# NoMethodError: undefined method `check_key' for #<OpenSSL::PKey::EC::Point:0x00000001013fba18 @group=#<OpenSSL::PKey::EC::Group:0x00000001013fb9f0>>

# Use `.keypair` which implements `.check_key`
keypair = JWT::JWK::EC.import(jwk).keypair
keypair.check_key
# OpenSSL::PKey::ECError: EVP_PKEY_public_check: point at infinity

I used .keypair instead of .public_key here, following the suggestion in this issue.

Am I doing something wrong here? Any help would be very much appreciated, please. 🙏

@anakinj
Copy link
Member

anakinj commented Jan 29, 2023

A little disclaimer: Im not an EC expert :)

It looks like the X value in the key is missing something, the encoded value is one char shorter than the y value. Is this JWK published publicly somewhere?

Also switching the x value to a valid encoded number makes the error different:

JWT::JWK::EC.import(
  kty: 'EC',
  crv: 'P-521',
  x: 'oLuW4nFqoUuR8d7FVsUoI62libYdQlWGtLStnqdudLY92bQ0ra5eZAbkunrGTR9-w9mr4o2Etyb6pC7YB2-23WM',
  y: 'AX1cjkGMiltikPrkX49qwuJDdcETaTsj-kyFP8jsF9W5XAB3Z4tBiQtc72DQnJYeKyAV_T6qZTtFKFr-Tp4iu-j7'
) => EC_POINT_bn2point: invalid encoding
JWT::JWK::EC.import(
  kty: 'EC',
  crv: 'P-521',
  x: 'AafgvMTsTt3-Z7g663VUz0CwW7GN8x83YV-dFwylobgjUKaGRuCgCvpOj5OP1mDPm7CHwGhI-yKUtMDYNfu7O_Uz',
  y: 'AX1cjkGMiltikPrkX49qwuJDdcETaTsj-kyFP8jsF9W5XAB3Z4tBiQtc72DQnJYeKyAV_T6qZTtFKFr-Tp4iu-j7'
) =>  EC_POINT_bn2point: point is not on curve

@kplattret
Copy link
Author

kplattret commented Jun 13, 2023

Hi @anakinj, thanks for your reply and sorry I'm only replying now. This was indeed a padding issue with the JWK. After trying a few things, we figured out that all we needed was to apply zero-padding to the non-encoded value, which we achieved this way:

private def apply_zero_padding_as_needed(jwk)
  valid_jwk = jwk.clone

  %i(x y).each do |elem|
    coords = Base64.urlsafe_decode64(valid_jwk[elem])
    length = coords.length
    diff = EXPECTED_COORDS_LENGTH - length # 66 bytes

    valid_jwk[elem] = Base64.urlsafe_encode64(("\x00" * diff) + coords) if diff > 0
  end

  valid_jwk
end

The error was neither with Ruby's openssl nor with the jwt gem. 👍

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

No branches or pull requests

2 participants