Files
key-generator/app.py
2026-03-12 14:17:39 +09:00

221 lines
6.6 KiB
Python

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)