Skip to content
This repository was archived by the owner on Aug 20, 2024. It is now read-only.

Commit be2baaf

Browse files
authored
Merge pull request #1 from Canmi21/master
Master
2 parents a89bf5d + 7d6f4e8 commit be2baaf

5 files changed

Lines changed: 361 additions & 0 deletions

File tree

AutoPFX.py

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import os
2+
import subprocess
3+
import tkinter as tk
4+
from tkinter import messagebox, simpledialog
5+
from datetime import datetime
6+
import hashlib
7+
import webbrowser
8+
from ruamel.yaml import YAML
9+
10+
yaml = YAML()
11+
yaml.preserve_quotes = True # 保留引号
12+
13+
# 加载配置文件
14+
def load_config():
15+
with open("Config.yaml", "r") as file:
16+
config = yaml.load(file)
17+
return config
18+
19+
# 加载密码文件
20+
def load_passwords():
21+
if os.path.exists("Password.yaml"):
22+
with open("Password.yaml", "r") as file:
23+
passwords = yaml.load(file)
24+
if passwords is None:
25+
passwords = {}
26+
else:
27+
passwords = {}
28+
return passwords
29+
30+
# 保存密码
31+
def save_password(pfx_file, password):
32+
passwords = load_passwords()
33+
34+
if os.path.exists("Password.yaml"):
35+
with open("Password.yaml", "r") as file:
36+
yaml_data = yaml.load(file)
37+
if yaml_data is None:
38+
yaml_data = {}
39+
else:
40+
yaml_data = {}
41+
42+
yaml_data.update(passwords) # 更新而不是覆盖
43+
yaml_data[pfx_file] = password # 添加新的密码项
44+
45+
with open("Password.yaml", "w") as file:
46+
yaml.dump(yaml_data, file)
47+
48+
# 删除指定 PFX 文件的密码
49+
def delete_password(pfx_file):
50+
passwords = load_passwords()
51+
52+
if pfx_file in passwords:
53+
del passwords[pfx_file]
54+
55+
with open("Password.yaml", "w") as file:
56+
yaml.dump(passwords, file)
57+
58+
# 读取 SignTool_Config.yaml 文件
59+
def load_ps_script_template():
60+
with open(os.path.join("PFX", "SignTool_Config.yaml"), "r") as file:
61+
config = yaml.load(file)
62+
return config.get("PowerShellScript", "")
63+
64+
# 替换 PowerShell 脚本中的变量
65+
def replace_ps_script_variables(template, pfx_name, pfx_subject, pfx_password):
66+
return template.format(pfx_name=pfx_name, pfx_subject=pfx_subject, pfx_password=pfx_password)
67+
68+
# 创建并执行 PowerShell 脚本
69+
def create_and_run_ps_script(ps_script):
70+
ps_script_path = os.path.join("PFX", "Build.ps1")
71+
with open(ps_script_path, "w") as file:
72+
file.write(ps_script)
73+
74+
# 执行 PowerShell 脚本
75+
subprocess.run(["powershell", "-ExecutionPolicy", "Bypass", "-File", ps_script_path], check=True)
76+
77+
# 显示主菜单
78+
def show_main_menu(window):
79+
for widget in window.winfo_children():
80+
widget.destroy()
81+
82+
label = tk.Label(window, text="Please select an option:", font=("Arial", 14))
83+
label.pack(pady=20)
84+
85+
inject_button = tk.Button(window, text="Inject PFX", command=lambda: inject_pfx(window), width=15)
86+
inject_button.pack(pady=10)
87+
88+
create_button = tk.Button(window, text="Create PFX", command=lambda: create_pfx_menu(window), width=15)
89+
create_button.pack(pady=10)
90+
91+
about_button = tk.Button(window, text="About", command=show_about, width=15)
92+
about_button.pack(pady=10)
93+
94+
# 注入 PFX 文件
95+
def inject_pfx(window):
96+
config = load_config()
97+
pfx_folder = config.get("PFX", "PFX")
98+
files = [f for f in os.listdir(pfx_folder) if f.endswith(".pfx")]
99+
100+
for widget in window.winfo_children():
101+
widget.destroy()
102+
103+
label = tk.Label(window, text="Select a PFX file to inject:", font=("Arial", 14))
104+
label.pack(pady=20)
105+
106+
if not files:
107+
messagebox.showinfo("Info", f"No PFX files found in the folder: {pfx_folder}")
108+
show_main_menu(window)
109+
else:
110+
for file in files:
111+
button = tk.Button(window, text=file, command=lambda f=file: select_pfx(window, f))
112+
button.pack(pady=5)
113+
114+
back_button = tk.Button(window, text="Back", command=lambda: show_main_menu(window), width=15)
115+
back_button.pack(pady=20)
116+
117+
# 选择 PFX 文件
118+
def select_pfx(window, file):
119+
passwords = load_passwords()
120+
if file in passwords:
121+
list_exe_files(window, file)
122+
else:
123+
password = simpledialog.askstring("Input", f"Enter password for {file}:", show='*')
124+
if password:
125+
save_password(file, password)
126+
inject_pfx(window)
127+
128+
# 列出所有 EXE 文件
129+
def list_exe_files(window, pfx_file):
130+
for widget in window.winfo_children():
131+
widget.destroy()
132+
133+
exe_files = []
134+
start_dir = os.getcwd()
135+
for root, dirs, files in os.walk(start_dir):
136+
depth = root[len(start_dir):].count(os.sep)
137+
if depth < 10: # 控制遍历深度
138+
for file in files:
139+
if file.lower().endswith(".exe"): # 忽略大小写
140+
exe_files.append(os.path.join(root, file))
141+
142+
label = tk.Label(window, text="Found .exe files:", font=("Arial", 14))
143+
label.pack(pady=20)
144+
145+
if not exe_files:
146+
no_files_label = tk.Label(window, text="No .exe files found.", font=("Arial", 12))
147+
no_files_label.pack(pady=10)
148+
else:
149+
for exe in exe_files:
150+
frame = tk.Frame(window, width=800) # 调整宽度
151+
frame.pack(fill="x", padx=10, pady=2)
152+
153+
# 计算 SHA256 哈希
154+
sha256_hash = hashlib.sha256()
155+
with open(exe, "rb") as f:
156+
for byte_block in iter(lambda: f.read(4096), b""):
157+
sha256_hash.update(byte_block)
158+
sha256_digest = sha256_hash.hexdigest()
159+
160+
exe_label = tk.Label(frame, text=exe, font=("Arial", 10), anchor="w", justify="left")
161+
exe_label.pack(side="left", fill="x", expand=True)
162+
163+
sha256_label = tk.Label(frame, text=sha256_digest, font=("Arial", 10), anchor="e", justify="right")
164+
sha256_label.pack(side="left", padx=5)
165+
166+
add_button = tk.Button(frame, text="+", width=2, command=lambda e=exe: sign_exe(window, pfx_file, e))
167+
add_button.pack(side="left", padx=5)
168+
169+
info_button = tk.Button(frame, text="i", width=2, command=lambda e=exe: show_signature_info(e))
170+
info_button.pack(side="right", padx=5)
171+
172+
back_button = tk.Button(window, text="Back", command=lambda: inject_pfx(window), width=15)
173+
back_button.pack(pady=20)
174+
175+
# 显示 EXE 文件的签名信息
176+
def show_signature_info(exe_file):
177+
config = load_config()
178+
sign_tool_path = config.get("SignToolPath")
179+
180+
if not sign_tool_path or not os.path.exists(sign_tool_path):
181+
messagebox.showerror("Error", "SignToolPath not found in Config.yaml or invalid path. Please check the configuration.")
182+
return
183+
184+
try:
185+
command = [sign_tool_path, "verify", "/pa", exe_file]
186+
result = subprocess.run(command, capture_output=True, text=True)
187+
if result.returncode == 0:
188+
messagebox.showinfo("Signature Info", result.stdout)
189+
else:
190+
messagebox.showinfo("No Signature", "No valid signature found.")
191+
except Exception as e:
192+
messagebox.showerror("Error", f"An error occurred: {str(e)}")
193+
194+
# 签名 EXE 文件
195+
def sign_exe(window, pfx_file, exe_file):
196+
config = load_config()
197+
sign_tool_path = config.get("SignToolPath")
198+
timestamp_url = config.get("TimeStampUrl")
199+
200+
if not sign_tool_path or not timestamp_url:
201+
messagebox.showerror("Error", "SignToolPath or TimeStampUrl not found in Config.yaml. Please check the configuration.")
202+
return
203+
204+
passwords = load_passwords()
205+
password = passwords.get(pfx_file)
206+
207+
try:
208+
# 直接使用 PFX 文件和密码进行签名
209+
command = [
210+
sign_tool_path,
211+
"sign",
212+
"/f", os.path.join(config["PFX"], pfx_file),
213+
"/p", password,
214+
"/tr", timestamp_url,
215+
"/td", "sha256",
216+
"/fd", "sha256",
217+
exe_file
218+
]
219+
subprocess.run(command, check=True)
220+
221+
# 获取当前时间
222+
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
223+
224+
# 签名成功提示
225+
messagebox.showinfo("Signed Successfully", f"""
226+
{os.path.basename(exe_file)}
227+
{exe_file}
228+
{pfx_file}
229+
{current_time}
230+
""")
231+
232+
except subprocess.CalledProcessError:
233+
messagebox.showerror("Error", "Signing failed. Please check the password and try again.")
234+
delete_password(pfx_file)
235+
select_pfx(window, pfx_file)
236+
237+
# 使用 PowerShell 创建 PFX 证书页面
238+
def create_pfx_menu(window):
239+
config = load_config()
240+
pfx_folder = config.get("PFX")
241+
242+
if not pfx_folder:
243+
messagebox.showerror("Error", "PFX storage path not found in Config.yaml. Please check the configuration.")
244+
return
245+
246+
for widget in window.winfo_children():
247+
widget.destroy()
248+
249+
label = tk.Label(window, text="Create a new PFX certificate:", font=("Arial", 14))
250+
label.pack(pady=20)
251+
252+
tk.Label(window, text="PFX File Name:", font=("Arial", 12)).pack(pady=5)
253+
pfx_name_entry = tk.Entry(window, width=30)
254+
pfx_name_entry.pack(pady=5)
255+
256+
tk.Label(window, text="PFX Display Name:", font=("Arial", 12)).pack(pady=5)
257+
pfx_display_name_entry = tk.Entry(window, width=30)
258+
pfx_display_name_entry.pack(pady=5)
259+
260+
tk.Label(window, text="PFX Password:", font=("Arial", 12)).pack(pady=5)
261+
pfx_password_entry = tk.Entry(window, show='*', width=30)
262+
pfx_password_entry.pack(pady=5)
263+
264+
def validate_and_create_pfx():
265+
pfx_name = pfx_name_entry.get().strip()
266+
pfx_display_name = pfx_display_name_entry.get().strip()
267+
pfx_password = pfx_password_entry.get().strip()
268+
269+
if not pfx_name or not pfx_display_name or not pfx_password:
270+
messagebox.showerror("Error", "All fields are required.")
271+
return
272+
273+
if any(char in pfx_name for char in r'\/:*?"<>|'):
274+
messagebox.showerror("Error", "PFX file name contains invalid characters.")
275+
return
276+
277+
if not os.path.exists(pfx_folder):
278+
os.makedirs(pfx_folder)
279+
280+
# 加载 PowerShell 脚本模板
281+
ps_script_template = load_ps_script_template()
282+
283+
# 替换模板中的变量
284+
ps_script = replace_ps_script_variables(ps_script_template, pfx_name, pfx_display_name, pfx_password)
285+
286+
try:
287+
# 生成并执行 PowerShell 脚本
288+
create_and_run_ps_script(ps_script)
289+
290+
# 保存密码
291+
save_password(f"{pfx_name}.pfx", pfx_password)
292+
293+
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
294+
timestamp_url = config.get("TimeStampUrl", "")
295+
296+
# 创建成功提示
297+
messagebox.showinfo("PFX Created Successfully", f"""
298+
{pfx_name}.pfx
299+
{pfx_display_name}
300+
{timestamp_url}
301+
{current_time}
302+
""")
303+
304+
show_main_menu(window)
305+
306+
except subprocess.CalledProcessError:
307+
messagebox.showerror("Error", "Failed to create the PFX file. Please check the configuration.")
308+
309+
cancel_button = tk.Button(window, text="Cancel", command=lambda: show_main_menu(window), width=15)
310+
cancel_button.pack(side="left", padx=10, pady=20)
311+
312+
create_button = tk.Button(window, text="Create", command=validate_and_create_pfx, width=15)
313+
create_button.pack(side="right", padx=10, pady=20)
314+
315+
# 关于
316+
def show_about():
317+
webbrowser.open("https://github.com/Canmi21/AutoPFX")
318+
messagebox.showinfo("About AutoPFX", """
319+
AutoPFX GUI 2024 X Github.com
320+
OpenSource Software MIT license.
321+
Copyright (C) Canmi(Canmi21), all right reserved.
322+
""")
323+
324+
# 程序入口
325+
def main():
326+
window = tk.Tk()
327+
window.title("AutoPFX Command Interface")
328+
window.geometry("800x600")
329+
330+
show_main_menu(window)
331+
332+
window.mainloop()
333+
334+
if __name__ == "__main__":
335+
main()

Config.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# AutoPFX MIT license 2024
2+
# https://github.com/Canmi21/AutoPFX/
3+
# Copyright (C) Canmi(Canmi21), all right reserved.
4+
5+
PFX: PFX
6+
#PFX Location.
7+
8+
SignToolPath: C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe
9+
#SignTool.exe
10+
11+
TimeStampUrl: "http://timestamp.digicert.com"
12+
#Server
13+
14+
#OpenSSL: C:\Program Files\OpenSSL-Win64\bin\openssl.exe
15+
#Location

PFX/Build.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "test" -CertStoreLocation "Cert:\CurrentUser\My"
2+
$certPath = Join-Path -Path $PSScriptRoot -ChildPath "test.pfx"
3+
$certPassword = ConvertTo-SecureString -String "963852" -Force -AsPlainText
4+
Export-PfxCertificate -Cert $cert -FilePath $certPath -Password $certPassword
5+
Write-Host "PFX certificate generated at: $certPath"

PFX/SignTool_Config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
PowerShellScript: |
2+
$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "{pfx_subject}" -CertStoreLocation "Cert:\CurrentUser\My"
3+
$certPath = Join-Path -Path $PSScriptRoot -ChildPath "{pfx_name}.pfx"
4+
$certPassword = ConvertTo-SecureString -String "{pfx_password}" -Force -AsPlainText
5+
Export-PfxCertificate -Cert $cert -FilePath $certPath -Password $certPassword
6+
Write-Host "PFX certificate generated at: $certPath"

Password.yaml

Whitespace-only changes.

0 commit comments

Comments
 (0)