56 lines
1.7 KiB
Python
56 lines
1.7 KiB
Python
"""Ed25519 key pair generation and JWT signing for QWeather API."""
|
|
from cryptography.hazmat.primitives.asymmetric import ed25519
|
|
from cryptography.hazmat.primitives import serialization
|
|
import jwt
|
|
import time
|
|
|
|
|
|
def generate_key_pair() -> tuple:
|
|
"""Generate an Ed25519 key pair.
|
|
|
|
Returns:
|
|
(private_key_pem: str, public_key_pem: str)
|
|
"""
|
|
private_key = ed25519.Ed25519PrivateKey.generate()
|
|
public_key = private_key.public_key()
|
|
|
|
private_pem = private_key.private_bytes(
|
|
encoding=serialization.Encoding.PEM,
|
|
format=serialization.PrivateFormat.PKCS8,
|
|
encryption_algorithm=serialization.NoEncryption(),
|
|
).decode("utf-8")
|
|
|
|
public_pem = public_key.public_bytes(
|
|
encoding=serialization.Encoding.PEM,
|
|
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
).decode("utf-8")
|
|
|
|
return private_pem, public_pem
|
|
|
|
|
|
def sign_jwt(private_key_pem: str, credential_id: str, project_id: str,
|
|
expiry_seconds: int = 3600) -> str:
|
|
"""Sign a JWT for QWeather API authentication.
|
|
|
|
Args:
|
|
private_key_pem: Ed25519 private key in PEM format.
|
|
credential_id: QWeather credential ID (kid).
|
|
project_id: QWeather project ID (sub).
|
|
expiry_seconds: Token validity in seconds (max 86400 per QWeather).
|
|
|
|
Returns:
|
|
Signed JWT string.
|
|
"""
|
|
now = int(time.time())
|
|
payload = {
|
|
"sub": project_id,
|
|
"iat": now,
|
|
"exp": now + min(expiry_seconds, 86400),
|
|
}
|
|
headers = {
|
|
"alg": "EdDSA",
|
|
"kid": credential_id,
|
|
}
|
|
token = jwt.encode(payload, private_key_pem, algorithm="EdDSA", headers=headers)
|
|
return token
|