#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Location: ./mcpgateway/utils/generate_keys.py
Copyright 2025
SPDX-License-Identifier: Apache-2.0
Authors: Madhav Kandukuri
Utility to generate Ed25519 key pairs for JWT or signing use.
Safely writes PEM-formatted private and public keys to disk.
"""
# Future
from __future__ import annotations
# Standard
# Logging setup
import logging
from pathlib import Path
# Third-Party
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
logger = logging.getLogger(__name__)
def generate_ed25519_keypair(private_path: Path, public_path: Path) -> None:
"""Generate an Ed25519 key pair and save to PEM files.
Args:
private_path: Path to save the private key PEM file.
public_path: Path to save the public key PEM file.
"""
private_key = ed25519.Ed25519PrivateKey.generate()
public_key = private_key.public_key()
private_bytes = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
public_bytes = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
private_path.write_bytes(private_bytes)
public_path.write_bytes(public_bytes)
print(f"✅ Ed25519 key pair generated:\n Private: {private_path}\n Public: {public_path}")
# ---------------------------------------------------------------------------
# Simplified generator: return private key PEM only
# ---------------------------------------------------------------------------
def generate_ed25519_private_key() -> str:
"""Generate an Ed25519 private key and return PEM string.
Returns:
str: PEM-formatted Ed25519 private key.
"""
private_key = ed25519.Ed25519PrivateKey.generate()
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
).decode()
return private_pem
# ---------------------------------------------------------------------------
# Helper: derive public key from private PEM
# ---------------------------------------------------------------------------
def derive_public_key_from_private(private_pem: str) -> str:
"""Derive the public key PEM from a given Ed25519 private key PEM string.
Args:
private_pem: PEM-formatted Ed25519 private key string.
Returns:
str: PEM-formatted Ed25519 public key.
Raises:
RuntimeError: If the public key cannot be derived.
"""
try:
private_key = serialization.load_pem_private_key(private_pem.encode(), password=None)
public_key = private_key.public_key()
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
return public_pem.decode()
except Exception as e:
logger.error(f"Error deriving public key from private PEM: {e}")
raise RuntimeError("Failed to derive public key from private PEM") from e
def main() -> None:
"""Command-line interface to generate Ed25519 private key PEM."""
private_pem = generate_ed25519_private_key()
print("Ed25519 private key generated successfully.\n")
print(private_pem)
if __name__ == "__main__":
main()