Created
August 19, 2025 11:24
-
-
Save akrabat/b69c23ab935caa5cd27f257aa0d775f5 to your computer and use it in GitHub Desktop.
Convert public keys published in JWKS to PEM format
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script --quiet | |
| # /// script | |
| # dependencies = [ | |
| # "cryptography", | |
| # ] | |
| # /// | |
| """Convert JWK keys to PEM format. | |
| This script reads .well-known/jwks.json and outputs PEM encoded versions | |
| of the public keys in that file. | |
| This file should be set as executable (chmod +x jwk-to-pem.py). | |
| Usage: | |
| curl -s https://example.com/.well-known/jwks.json | jwk-to-pem.py | |
| uv run jwk-to-pem.py jwks.json | |
| uv run jwk-to-pem.py < jwks.json | |
| Requirements: | |
| - uv (https://github.com/astral-sh/uv) | |
| - cryptography library | |
| Author: | |
| Rob Allen <[email protected]> | |
| Copyright 2025 | |
| License: | |
| MIT License - https://opensource.org/licenses/MIT | |
| """ | |
| import json | |
| import base64 | |
| import sys | |
| from cryptography.hazmat.primitives import serialization | |
| from cryptography.hazmat.primitives.asymmetric import rsa | |
| def base64url_decode(data): | |
| """Decode base64url to bytes""" | |
| # Add padding if needed | |
| padding = 4 - len(data) % 4 | |
| if padding != 4: | |
| data += '=' * padding | |
| # Replace URL-safe chars | |
| data = data.replace('-', '+').replace('_', '/') | |
| # Decode | |
| return base64.b64decode(data) | |
| def jwk_to_pem(jwk_key): | |
| """Convert JWK to PEM format""" | |
| if jwk_key['kty'] != 'RSA': | |
| raise ValueError("Only RSA keys are supported") | |
| # Decode the modulus (n) and exponent (e) to int | |
| n = int.from_bytes(base64url_decode(jwk_key['n']), 'big') | |
| e = int.from_bytes(base64url_decode(jwk_key['e']), 'big') | |
| # Create RSA public key | |
| public_key = rsa.RSAPublicNumbers(e, n).public_key() | |
| # Serialize to PEM | |
| pem = public_key.public_bytes( | |
| encoding=serialization.Encoding.PEM, | |
| format=serialization.PublicFormat.SubjectPublicKeyInfo | |
| ) | |
| return pem.decode() | |
| def main(): | |
| if len(sys.argv) > 2: | |
| print("Usage: jwk_to_pem.py [jwks.json]") | |
| print("If no file is provided, reads from stdin") | |
| sys.exit(1) | |
| if len(sys.argv) == 2 and sys.argv[1] != '-': | |
| # Read from file | |
| with open(sys.argv[1], 'r') as f: | |
| jwks = json.load(f) | |
| else: | |
| # Read from stdin | |
| jwks = json.load(sys.stdin) | |
| # Convert each key | |
| for i, key in enumerate(jwks['keys']): | |
| kid = key.get('kid', f'key-{i}') | |
| print(f"# Key {i} (kid: {kid})") | |
| print(jwk_to_pem(key)) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment