非對稱式加密
非對稱式加密(Asymmetric Encryption)最常見的是 RSA 演算法。它與對稱式最大的不同在於:
公鑰 (Public Key):用來加密,可以公開給任何人。
私鑰 (Private Key):用來解密,必須由自己嚴格保管。
1. 安裝套件
同樣使用 cryptography 函式庫
程式在右側會顯示「金鑰對」,包含私鑰與公鑰,並允許你分別儲存。
2. 程式的關鍵
金鑰對 (Key Pair):點擊「生成」後會產生
BEGIN PRIVATE KEY與BEGIN PUBLIC KEY兩段文字。加密流程:程式會抓取右側的公鑰來加密左側文字。加密後的結果以 Hex (十六進位) 格式顯示,因為 RSA 加密後的二進制數據無法直接顯示為文字。
解密流程:程式會抓取右側的私鑰來解密左側的 Hex 字串。
安全性 (OAEP Padding):使用了
OAEP填充方式,這是目前 RSA 加密最推薦且安全的做法。
3. 重要的限制(知識點)
RSA 加密有一個長度限制:
對稱式加密(AES)可以加密無限大的檔案。
非對稱式加密(RSA)只能加密非常小的數據(通常不超過幾百個位元組)。
實際應用中:通常是用 RSA 加密「AES 的金鑰」,再用 AES 加密「大檔案」。這被稱為「混合加密」。
import tkinter as tk
from tkinter import messagebox, filedialog
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
class AsymmetricApp:
def __init__(self, root):
self.root = root
self.root.title("RSA 非對稱式加密工具")
self.root.geometry("900x550")
self.private_key = None
self.public_key = None
self.create_widgets()
def create_widgets(self):
# --- 左側:文字輸入/輸出區 ---
left_frame = tk.Frame(self.root, padx=10, pady=10)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
tk.Label(left_frame, text="原始文字 / 處理結果:", font=('Arial', 10, 'bold')).pack(anchor=tk.W)
self.txt_input = tk.Text(left_frame, height=20, width=40)
self.txt_input.pack(fill=tk.BOTH, expand=True)
# --- 中間:控制按鈕 ---
mid_frame = tk.Frame(self.root, padx=10, pady=10)
mid_frame.pack(side=tk.LEFT, fill=tk.Y)
tk.Button(mid_frame, text="生成 RSA 金鑰對 >>", command=self.generate_keys, bg="#e8f5e9").pack(fill=tk.X, pady=5)
tk.Button(mid_frame, text="用公鑰加密", command=self.encrypt_action, bg="#e1f5fe").pack(fill=tk.X, pady=5)
tk.Button(mid_frame, text="用私鑰解密", command=self.decrypt_action, bg="#fff9c4").pack(fill=tk.X, pady=5)
tk.Button(mid_frame, text="清除文字", command=lambda: self.txt_input.delete(1.0, tk.END)).pack(fill=tk.X, pady=20)
# --- 右側:金鑰管理區 ---
right_frame = tk.Frame(self.root, padx=10, pady=10)
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
# 私鑰區
tk.Label(right_frame, text="Private Key (私鑰 - 解密用):", fg="red").pack(anchor=tk.W)
self.txt_priv = tk.Text(right_frame, height=10, width=50, font=('Courier New', 8))
self.txt_priv.pack(fill=tk.X, pady=2)
tk.Button(right_frame, text="儲存私鑰", command=lambda: self.save_key("private")).pack(anchor=tk.E)
# 公鑰區
tk.Label(right_frame, text="Public Key (公鑰 - 加密用):", fg="blue").pack(anchor=tk.W)
self.txt_pub = tk.Text(right_frame, height=6, width=50, font=('Courier New', 8))
self.txt_pub.pack(fill=tk.X, pady=2)
tk.Button(right_frame, text="儲存公鑰", command=lambda: self.save_key("public")).pack(anchor=tk.E)
def generate_keys(self):
"""生成 RSA 私鑰與公鑰"""
self.private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
self.public_key = self.private_key.public_key()
# 顯示私鑰 (PEM 格式)
priv_pem = self.private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
).decode()
# 顯示公鑰 (PEM 格式)
pub_pem = self.public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode()
self.txt_priv.delete(1.0, tk.END)
self.txt_priv.insert(tk.END, priv_pem)
self.txt_pub.delete(1.0, tk.END)
self.txt_pub.insert(tk.END, pub_pem)
def get_keys_from_text(self):
"""從文字框中讀取金鑰"""
try:
priv_data = self.txt_priv.get(1.0, tk.END).strip().encode()
pub_data = self.txt_pub.get(1.0, tk.END).strip().encode()
p_key = serialization.load_pem_private_key(priv_data, password=None)
u_key = serialization.load_pem_public_key(pub_data)
return p_key, u_key
except Exception as e:
messagebox.showerror("錯誤", "金鑰格式不正確!請確認右側金鑰內容。")
return None, None
def encrypt_action(self):
"""使用公鑰加密"""
_, pub_key = self.get_keys_from_text()
if not pub_key: return
try:
message = self.txt_input.get(1.0, tk.END).strip().encode()
ciphertext = pub_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# 轉換為 hex 顯示
self.txt_input.delete(1.0, tk.END)
self.txt_input.insert(tk.END, ciphertext.hex())
except Exception as e:
messagebox.showerror("加密失敗", f"內容太長或金鑰錯誤: {e}")
def decrypt_action(self):
"""使用私鑰解密"""
priv_key, _ = self.get_keys_from_text()
if not priv_key: return
try:
hex_input = self.txt_input.get(1.0, tk.END).strip()
ciphertext = bytes.fromhex(hex_input)
plaintext = priv_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
self.txt_input.delete(1.0, tk.END)
self.txt_input.insert(tk.END, plaintext.decode())
except Exception as e:
messagebox.showerror("解密失敗", "請確保輸入的是 Hex 加密字串且私鑰正確。")
def save_key(self, key_type):
content = self.txt_priv.get(1.0, tk.END) if key_type == "private" else self.txt_pub.get(1.0, tk.END)
if not content.strip(): return
file_path = filedialog.asksaveasfilename(defaultextension=".pem", initialfile=f"{key_type}.pem")
if file_path:
with open(file_path, "w") as f:
f.write(content.strip())
messagebox.showinfo("成功", "金鑰已儲存")
if __name__ == "__main__":
root = tk.Tk()
app = AsymmetricApp(root)
root.mainloop()


沒有留言:
張貼留言