import secrets import hashlib import hmac import base64 import uuid import os import string from cryptography.hazmat.primitives.asymmetric import rsa, ec from cryptography.hazmat.primitives import serialization from flask import Flask, render_template, request, jsonify app = Flask(__name__) KEY_CONFIGS = { "jwt_rs256": { "label": "JWT Key Pair (RS256)", "description": "RSA 2048-bit 비대칭 키 쌍 (PEM)", "bytes": None, "format": "rsa_keypair", }, "jwt_es256": { "label": "JWT Key Pair (ES256)", "description": "EC P-256 비대칭 키 쌍 (PEM)", "bytes": None, "format": "ec_keypair", }, "jwt_hs256": { "label": "JWT Secret (HS256)", "description": "HMAC-SHA256용 JWT 시크릿 키", "bytes": 32, "format": "hex", }, "jwt_hs384": { "label": "JWT Secret (HS384)", "description": "HMAC-SHA384용 JWT 시크릿 키", "bytes": 48, "format": "hex", }, "jwt_hs512": { "label": "JWT Secret (HS512)", "description": "HMAC-SHA512용 JWT 시크릿 키", "bytes": 64, "format": "hex", }, "jwt_base64": { "label": "JWT Secret (Base64URL)", "description": "Base64URL 인코딩 JWT 시크릿 (256-bit)", "bytes": 32, "format": "base64url", }, "api_key": { "label": "API Key", "description": "sk- 접두사 포함 API 키", "bytes": 32, "format": "api_key", }, "op_key": { "label": "Operation Key", "description": "ops- 접두사 포함 운영 키", "bytes": 24, "format": "op_key", }, "hex_256": { "label": "Random Hex (256-bit)", "description": "순수 랜덤 Hex 문자열", "bytes": 32, "format": "hex", }, "hex_512": { "label": "Random Hex (512-bit)", "description": "순수 랜덤 Hex 문자열 (512-bit)", "bytes": 64, "format": "hex", }, "alphanumeric": { "label": "Alphanumeric Key", "description": "영숫자 조합 랜덤 키", "bytes": 32, "format": "alphanumeric", }, "uuid_v4": { "label": "UUID v4", "description": "표준 UUID v4", "bytes": 16, "format": "uuid", }, "custom": { "label": "Custom Length", "description": "직접 바이트 수 지정", "bytes": None, "format": "hex", }, } def generate_key(key_type: str, custom_bytes: int = 32, custom_format: str = "hex") -> dict: config = KEY_CONFIGS.get(key_type) if not config: raise ValueError(f"Unknown key type: {key_type}") fmt = custom_format if key_type == "custom" else config["format"] if fmt == "rsa_keypair": private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) priv_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ).decode() pub_pem = private_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, ).decode() return { "key": priv_pem, "public_key": pub_pem, "keypair": True, "type": key_type, "label": config["label"], "bits": 2048, "length": len(priv_pem), "algorithm": "RS256", } if fmt == "ec_keypair": private_key = ec.generate_private_key(ec.SECP256R1()) priv_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ).decode() pub_pem = private_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, ).decode() return { "key": priv_pem, "public_key": pub_pem, "keypair": True, "type": key_type, "label": config["label"], "bits": 256, "length": len(priv_pem), "algorithm": "ES256", } byte_length = config["bytes"] if config["bytes"] is not None else custom_bytes raw = secrets.token_bytes(byte_length) if fmt == "hex": key = raw.hex() elif fmt == "base64url": key = base64.urlsafe_b64encode(raw).rstrip(b"=").decode() elif fmt == "api_key": encoded = base64.urlsafe_b64encode(raw).rstrip(b"=").decode() key = f"sk-{encoded}" elif fmt == "op_key": encoded = base64.urlsafe_b64encode(raw).rstrip(b"=").decode() key = f"ops-{encoded}" elif fmt == "alphanumeric": alphabet = string.ascii_letters + string.digits key = "".join(secrets.choice(alphabet) for _ in range(byte_length)) elif fmt == "uuid": key = str(uuid.uuid4()) else: key = raw.hex() return { "key": key, "keypair": False, "type": key_type, "label": config["label"], "bits": byte_length * 8, "length": len(key), "algorithm": fmt.upper(), } @app.route("/") def index(): return render_template("index.html", key_configs=KEY_CONFIGS) @app.route("/api/generate", methods=["POST"]) def api_generate(): data = request.get_json() or {} key_type = data.get("type", "jwt_hs256") custom_bytes = int(data.get("custom_bytes", 32)) custom_format = data.get("custom_format", "hex") if custom_bytes < 8: custom_bytes = 8 elif custom_bytes > 512: custom_bytes = 512 try: result = generate_key(key_type, custom_bytes, custom_format) return jsonify({"success": True, "data": result}) except Exception as e: return jsonify({"success": False, "error": str(e)}), 400 @app.route("/api/generate/bulk", methods=["POST"]) def api_generate_bulk(): data = request.get_json() or {} key_type = data.get("type", "jwt_hs256") count = min(int(data.get("count", 5)), 20) custom_bytes = int(data.get("custom_bytes", 32)) custom_format = data.get("custom_format", "hex") try: results = [generate_key(key_type, custom_bytes, custom_format) for _ in range(count)] return jsonify({"success": True, "data": results}) except Exception as e: return jsonify({"success": False, "error": str(e)}), 400 if __name__ == "__main__": app.run(debug=True, host="0.0.0.0", port=5050)