Skip to content

Instantly share code, notes, and snippets.

@seansummers
Forked from jonlundy/conv.py
Last active October 26, 2021 12:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seansummers/0a13325f4ea7a1a7e6e247771cce80df to your computer and use it in GitHub Desktop.
Save seansummers/0a13325f4ea7a1a7e6e247771cce80df to your computer and use it in GitHub Desktop.
JWKS parsing
#!/usr/bin/env python3
#
# openssl asn1parse -noout -out private_key.der -genconf <(python jwks2asn1.py private_key.json)
# openssl rsa -in private_key.der -inform der > private_key.pem
#
# rfc7517.3 : urlsafe_b64decode
# rfc7517.A.1 : ints are big-indian
#
import base64
import dataclasses
import json
import operator
import pathlib
import sys
@dataclasses.dataclass
class Jwks:
n: int
e: int
d: int
p: int
q: int
dp: int
dq: int
qi: int
def __post_init__(self):
for field in dataclasses.fields(self):
name, type_ = field.name, field.type
value = getattr(self, name)
if not isinstance(value, type_):
if isinstance(value, str):
value = base64.urlsafe_b64decode(f"{value}==")
if isinstance(value, bytes):
value = int.from_bytes(value, byteorder="big")
setattr(self, name, type_(value))
@classmethod
def from_jwks(cls, jwks_file: pathlib.Path):
fields = (_.name for _ in dataclasses.fields(cls))
return cls(*operator.itemgetter(*fields)(json.loads(jwks_file.read_text())))
def asasn1(self):
prefix = [
"asn1=SEQUENCE:private_key",
"[private_key]",
"version=INTEGER:0",
] + [
f"{field.name}=INTEGER:0X{getattr(self, field.name):X}"
for field in dataclasses.fields(self)
]
return "\n".join(prefix)
print(Jwks.from_jwks(pathlib.Path(sys.argv[1])).asasn1())
#!/usr/bin/env python3
#
# openssl asn1parse -noout -out private_key.der -genconf <(python tiny_jwks2asn1.py private_key.json)
# openssl rsa -in private_key.der -inform der > private_key.pem
#
# rfc7517.3 : urlsafe_b64decode
# rfc7517.A.1 : ints are big-indian
#
import base64
import json
import pathlib
import sys
asn1_header = ["asn1=SEQUENCE:private_key", "[private_key]", "version=INTEGER:0"]
fields = ("n", "e", "d", "p", "q", "dp", "dq", "qi")
def parse(value: str) -> int:
data = base64.urlsafe_b64decode(f"{value}==")
return int.from_bytes(data, byteorder="big")
jwks = json.loads(pathlib.Path(sys.argv[1]).read_text())
print(
"\n".join(
asn1_header + [f"{field}=INTEGER:0X{parse(jwks[field]):X}" for field in fields]
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment