2026年1月15日 星期四

非對稱式加密

非對稱式加密 

非對稱式加密(Asymmetric Encryption)最常見的是 RSA 演算法。它與對稱式最大的不同在於:

  1. 公鑰 (Public Key):用來加密,可以公開給任何人。

  2. 私鑰 (Private Key):用來解密,必須由自己嚴格保管。

1. 安裝套件

同樣使用 cryptography 函式庫

程式在右側會顯示「金鑰對」,包含私鑰與公鑰,並允許你分別儲存。

2. 程式的關鍵 

  • 金鑰對 (Key Pair):點擊「生成」後會產生 BEGIN PRIVATE KEYBEGIN 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()


沒有留言:

張貼留言

CRC(循環冗餘檢查)

  CRC(循環冗餘檢查) 完整的 CRC(循環冗餘檢查)逐步演示器 。它支援自定義 生成多項式 與 資料位元流 ,並透過「下一步」按鈕展示二進制長除法(XOR 運算)的過程。 二進制長除法模擬 : CRC 的核心是模二除法(Modulo-2 Division),實務上就是不斷進...