From 413cdf8cc2c6f9df46cd176f00e62cb27df1ac75 Mon Sep 17 00:00:00 2001 From: bcjang Date: Thu, 12 Mar 2026 14:17:39 +0900 Subject: [PATCH] web>app --- .claude/settings.local.json | 11 +++ .serena/.gitignore | 2 + .serena/project.yml | 135 ++++++++++++++++++++++++++ =41.0.0 | 3 + README.md | 30 +++--- __pycache__/app.cpython-312.pyc | Bin 6075 -> 8315 bytes app.py | 61 +++++++++++- main.py | 33 ++++++- requirements.txt | 1 + templates/index.html | 163 ++++++++++++++++++++++++-------- 10 files changed, 385 insertions(+), 54 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 .serena/.gitignore create mode 100644 .serena/project.yml create mode 100644 =41.0.0 diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..f509f12 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,11 @@ +{ + "permissions": { + "allow": [ + "mcp__serena__activate_project", + "mcp__serena__list_dir", + "mcp__serena__get_symbols_overview", + "mcp__serena__find_symbol", + "Bash(python -c \"\nfrom app import generate_key\nr = generate_key\\('jwt_rs256'\\)\nprint\\('RS256 keypair:', r['keypair'], r['algorithm'], r['bits'], 'bit'\\)\nprint\\('private key starts with:', r['key'][:27]\\)\nprint\\('public key starts with:', r['public_key'][:26]\\)\n\ne = generate_key\\('jwt_es256'\\)\nprint\\('ES256 keypair:', e['keypair'], e['algorithm'], e['bits'], 'bit'\\)\nprint\\('private key starts with:', e['key'][:27]\\)\n\nh = generate_key\\('jwt_hs256'\\)\nprint\\('HS256:', h['keypair'], h['algorithm'], h['bits'], 'bit'\\)\n\")" + ] + } +} diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 0000000..2e510af --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1,2 @@ +/cache +/project.local.yml diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 0000000..43b2d10 --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,135 @@ +# the name by which the project can be referenced within Serena +project_name: "key-generator" + + +# list of languages for which language servers are started; choose from: +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# java julia kotlin lua markdown +# matlab nix pascal perl php +# php_phpactor powershell python python_jedi r +# rego ruby ruby_solargraph rust scala +# swift terraform toml typescript typescript_vts +# vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- python + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: "utf-8" + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: + +# whether to use project's .gitignore files to ignore files +ignore_all_files_in_gitignore: true + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + +# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# Below is the complete list of tools for convenience. +# To make sure you have the latest list of tools, and to view their descriptions, +# execute `uv run scripts/print_tool_overview.py`. +# +# * `activate_project`: Activates a project by name. +# * `check_onboarding_performed`: Checks whether project onboarding was already performed. +# * `create_text_file`: Creates/overwrites a file in the project directory. +# * `delete_lines`: Deletes a range of lines within a file. +# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `execute_shell_command`: Executes a shell command. +# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. +# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). +# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. +# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. +# * `initial_instructions`: Gets the initial instructions for the current project. +# Should only be used in settings where the system prompt cannot be set, +# e.g. in clients you have no control over, like Claude Desktop. +# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. +# * `insert_at_line`: Inserts content at a given line in a file. +# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. +# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). +# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). +# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). +# * `read_file`: Reads a file within the project directory. +# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. +# * `remove_project`: Removes a project from the Serena configuration. +# * `replace_lines`: Replaces a range of lines within a file with new content. +# * `replace_symbol_body`: Replaces the full definition of a symbol. +# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `search_for_pattern`: Performs a search for a pattern in the project. +# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. +# * `switch_modes`: Activates modes by providing a list of their names +# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. +# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. +# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. +# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. +excluded_tools: [] + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default) +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +fixed_tools: [] + +# list of mode names to that are always to be included in the set of active modes +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this setting overrides the global configuration. +# Set this to [] to disable base modes for this project. +# Set this to a list of mode names to always include the respective modes for this project. +base_modes: + +# list of mode names that are to be activated by default. +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# This setting can, in turn, be overridden by CLI parameters (--mode). +default_modes: + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +# time budget (seconds) per tool call for the retrieval of additional symbol information +# such as docstrings or parameter information. +# This overrides the corresponding setting in the global configuration; see the documentation there. +# If null or missing, use the setting from the global configuration. +symbol_info_budget: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] diff --git a/=41.0.0 b/=41.0.0 new file mode 100644 index 0000000..9f0cb12 --- /dev/null +++ b/=41.0.0 @@ -0,0 +1,3 @@ + +[notice] A new release of pip is available: 25.0.1 -> 26.0.1 +[notice] To update, run: python.exe -m pip install --upgrade pip diff --git a/README.md b/README.md index 68a7ab2..29acf0c 100644 --- a/README.md +++ b/README.md @@ -13,19 +13,21 @@ python main.py ## 지원 키 타입 -| 타입 | 비트 | 포맷 | -|------|------|------| -| JWT Secret (HS256) | 256-bit | Hex | -| JWT Secret (HS384) | 384-bit | Hex | -| JWT Secret (HS512) | 512-bit | Hex | -| JWT Secret (Base64URL) | 256-bit | Base64URL | -| API Key `sk-...` | 256-bit | Base64URL | -| Operation Key `ops-...` | 192-bit | Base64URL | -| Random Hex 256-bit | 256-bit | Hex | -| Random Hex 512-bit | 512-bit | Hex | -| Alphanumeric | 256-bit | A-Za-z0-9 | -| UUID v4 | 128-bit | UUID | -| Custom | 자유 | 직접 선택 | +| 타입 | 비트 | 포맷 | 비고 | +|------|------|------|------| +| JWT Key Pair (RS256) | 2048-bit | PEM | RSA 비대칭 키 쌍 (Private + Public) | +| JWT Key Pair (ES256) | 256-bit | PEM | EC P-256 비대칭 키 쌍 (Private + Public) | +| JWT Secret (HS256) | 256-bit | Hex | HMAC 대칭 키 | +| JWT Secret (HS384) | 384-bit | Hex | HMAC 대칭 키 | +| JWT Secret (HS512) | 512-bit | Hex | HMAC 대칭 키 | +| JWT Secret (Base64URL) | 256-bit | Base64URL | | +| API Key `sk-...` | 256-bit | Base64URL | | +| Operation Key `ops-...` | 192-bit | Base64URL | | +| Random Hex 256-bit | 256-bit | Hex | | +| Random Hex 512-bit | 512-bit | Hex | | +| Alphanumeric | 256-bit | A-Za-z0-9 | | +| UUID v4 | 128-bit | UUID | | +| Custom | 자유 | 직접 선택 | | --- @@ -36,6 +38,7 @@ python main.py - **대량 생성** 체크박스 활성화 시 최대 20개 한번에 생성, **Copy All**로 전체 복사 - **Custom** 타입 선택 시 바이트 수(8~512)와 출력 포맷 직접 지정 - 모든 키는 Python `secrets` 모듈(암호학적 난수) 사용 +- **RS256 / ES256** 선택 시 Private Key와 Public Key를 각각 개별 복사 가능 (웹 UI) --- @@ -44,3 +47,4 @@ python main.py - Python 3.8+ - customtkinter 5.2+ - pyperclip 1.9+ +- cryptography 41.0+ (RS256 / ES256 키 쌍 생성) diff --git a/__pycache__/app.cpython-312.pyc b/__pycache__/app.cpython-312.pyc index 4f7112fc142270609b2d76dcaf2cff68312b6957..889f28703a7a1ac5b725278c7cce2e9fb1dfb908 100644 GIT binary patch delta 3921 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 b-L%oZBOC3bOP6=_iLDt{?F`?v(ry16^r+Vg delta 2068 zcmb7ENo*Tc7@qNLw#SZ_Y;HCuElK01j%k+e6t|^NO4}g1P#l4A{3h|l_SkweR+BIx zK}4cO9FoqZ5=AOPNJZ%Z2nR$jy~CjtgwWi8N+=f;lmiu9_}{a~i4X@E$^ZMe?|=XH z{8#c`qW^2Z-^;-F(Xr1}UzlNjA%gP-YRu+CcBTK!LeZP@7Uh)8GA>4OE522>YEStQ zhj=6)5lOdrBXtxqX{cy&V(4-sfSrSrV#Xr5H$;1qi|vFfUt(39J|njNo!egE(p+UC`w_|R~^pJ6cZPmo(X zN6;wRfA_#DSB=Utv+OJzJG4&3^VUUI+&aXau)cBit(n}IiH25~egm+8`Y zu<^ueUaB_ul3P_-iVN;xzt=ry?;4-TvKrK@Bu;I6oQisnJ9n5ry)F?}J zxsjk6v_AmjrkI2O4b#2@*2i?e&wSvTm$a_{?)_izaW|6d@OgmyDEySs1f|k%y2atP z&TZDnme%3sfO*%0{5QN!FJ1FYvZ!InZFD-;t;Ri5UnZvwP{J_e5TH498Bqsg^f=t~ zPH0q{wb$d3BrRBLcjtg)GBBSYnUBe%(44?9Mz4c0>uoj#SZTmtK;c{B1t!*belC1T z%WI{pT6bQV?KWo1%HeJ-0}_*jv5ycpA>?guk_Y@0CQCNQ>G?R_v^l<1*5j~U!N9Mq zYCR4ljx-a12go{s#zdQg;5JX9Q?<=csD^Hf1x1@Oa<(T^m?~k_$Q5Hv)|Ftiu@6wT zY?ShfmY$e36dk**WT;<6O16X)Yce#nK(4TbN<~E@HV1KlFGKX07k7aLlg{C8;v;5D z+N3&V%Op^`b|W`gG;9uMuHr%BBPkjQjPM?ibn^e!?R-f*Ks35Q(@FmqnMZQl&7)t8 ze=i?@z#qqF)<#-BW}^uLB3oK5o4jyZdv$Pe>v3z>m5ZRX1qI_0USEbW#@rZogt6UI zijkhyOBx;_BByEwlm`JsB6?2EWR{>u7!Gfwva7VG-I7yyiVTNzvX)olg2)aFBAI}ATUw_z^@QeO|qzJI7P&|-x z$BCE(0@c+MJ%?W>>iS0D7>Krn6}?h0bo?qvI$2X^;??$twpWv$s1)*80jHI<6CFu* zfwq|Lb_CO9m2w$9S5PNxK3B|S@d0qfbVXey9UlS77A6ZBJ&(y>FQyZxMbR$1(`hYJ zRMKgi%aqG>6=)T-Mw>@!wmK7Az@>_z;IaKA2 str: + 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 priv_pem + "\n" + pub_pem + + 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 priv_pem + "\n" + pub_pem + raw = secrets.token_bytes(byte_length) if fmt == "hex": return raw.hex() @@ -301,8 +331,9 @@ class App(ctk.CTk): self._key_box.insert("1.0", key) self._key_box.configure(state="disabled") + fmt_display = {"rsa_keypair": "RS256", "ec_keypair": "ES256"}.get(fmt, fmt.upper()) self._meta_label.configure( - text=f"{entry[0]} · {bits}-bit · {fmt.upper()} · {len(key)} chars" + text=f"{entry[0]} · {bits}-bit · {fmt_display} · {len(key)} chars" ) # Reset copy button diff --git a/requirements.txt b/requirements.txt index eb15e38..b344acd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ customtkinter>=5.2.0 pyperclip>=1.9.0 +cryptography>=41.0.0 diff --git a/templates/index.html b/templates/index.html index b0bcc6f..74fdf60 100644 --- a/templates/index.html +++ b/templates/index.html @@ -375,6 +375,18 @@ margin-top: 10px; } + /* Keypair display */ + .keypair-section { margin-bottom: 16px; } + .keypair-section:last-child { margin-bottom: 0; } + .keypair-section-label { + font-size: 0.72rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-muted); + margin-bottom: 6px; + } + /* Icon SVG */ svg { display: inline-block; vertical-align: middle; } @@ -389,7 +401,7 @@ Key Generator -

JWT Secret · API Key · Operation Key · UUID · Random Hex

+

JWT Secret · RS256 · ES256 · API Key · Operation Key · UUID · Random Hex

@@ -463,6 +475,35 @@
+ + +