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

EC to_jwk #466

Closed
dimaqq opened this issue Nov 22, 2019 · 6 comments
Closed

EC to_jwk #466

dimaqq opened this issue Nov 22, 2019 · 6 comments

Comments

@dimaqq
Copy link
Contributor

dimaqq commented Nov 22, 2019

I've notices that RSA keys have a to_jwk() function in pyjwt, but EC keys do not.

I've cobbled together a dirty conversion routine:
(shared under MIT license)

import sys
import json
from base64 import urlsafe_b64encode
import cryptography.hazmat.backends.openssl.backend
import cryptography.hazmat.primitives.serialization


def toBase64url(v: int):
    return (
        urlsafe_b64encode(v.to_bytes(100, "big").lstrip(b"\0"))
        .decode("ascii")
        .rstrip("=")
    )


def jwk_format(public_key: str, key_id: str = 1) -> dict:
    """JSON Web Key format for a public key."""
    key = cryptography.hazmat.primitives.serialization.load_pem_public_key(
        public_key.encode("ascii"), cryptography.hazmat.backends.openssl.backend
    )

    JWK_CURVE_NAMES = {"secp256r1": "P-256"}

    return {
        "epk": {
            "kty": "EC",
            "crv": JWK_CURVE_NAMES[key.public_numbers().curve.name],
            "x": toBase64url(key.public_numbers().x),
            "y": toBase64url(key.public_numbers().y),
            "kid": key_id,
        }
    }

if __name__ == "__main__":
    json.dump(jwk_format(open(sys.argv[1]).read()), sys.stdout, indent=2)

Given this input:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0vYjQm7oEHaJTjZ9Hh35aTd20h/U
wiz3RbYsoaGGjXGXu/d+9k7HdfsD4RjpjZFIOdurtnY0JBZkSrhZCipyaw==
-----END PUBLIC KEY-----

It produces the output:

{
  "epk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "0vYjQm7oEHaJTjZ9Hh35aTd20h_Uwiz3RbYsoaGGjXE",
    "y": "l7v3fvZOx3X7A-EY6Y2RSDnbq7Z2NCQWZEq4WQoqcms",
    "kid": 1
  }
}

If someone wants to drop this into the codebase, that would be great!

@leonsmith
Copy link
Contributor

I don't mind opening a pull request for this? I'm just a bit confused as to why 100 in the v.to_bytes(100, "big").lstrip(b"\0")

looks like 100 is an arbitrary number to make sure it will fit but then we strip the leading null bytes?

@dimaqq
Copy link
Contributor Author

dimaqq commented Feb 7, 2022

The value for 100 simply needs to be “large enough”. It could actually be computed using log or first-bit-set or hacked like I did — use a value that’s “large enough”… at least for the kind of elliptic curve kinda/parameters that I was using (es256).
to_bytes requires buffer size, but maybe there’s some Python magic that I’m not aware of.

@dimaqq
Copy link
Contributor Author

dimaqq commented Feb 7, 2022

Here’s a summary of EC p bit sizes:
https://www.johndcook.com/blog/2019/02/15/elliptic-curve-names/

@leonsmith
Copy link
Contributor

Made a first stab at this...

#732

@auvipy
Copy link
Collaborator

auvipy commented Feb 10, 2022

thanks

@dimaqq
Copy link
Contributor Author

dimaqq commented May 24, 2022

Should be fixed in #732

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

No branches or pull requests

4 participants