2025年11月7日 星期五

RFID Authentication (認證機制)

 RFID Authentication (認證機制) 

MIFARE 1K 卡片選擇的流程,Authentication (認證) 是讀卡器 (PCD) 在讀取或寫入卡片資料前最關鍵的安全步驟。

我將分為兩部分來解釋:首先說明 MIFARE 1K 的認證機制,然後提供一個使用 Python Tkinter 模擬該流程的程式碼架構。


第一部分:MIFARE 1K 認證流程 (Authentication) 說明

MIFARE Classic 1K 卡片將其記憶體分為 16 個扇區 (Sectors),每個扇區包含 4 個區塊 (Blocks)。每個扇區的最後一個區塊稱為扇區尾部 (Sector Trailer),用於儲存該扇區的兩個密鑰:Key AKey B,以及存取控制位元 (Access Bits)。

認證流程的目的是:讀卡器必須證明它知道存取該扇區所需的正確密鑰。

1. 認證的必要條件

  • 選擇卡片: 必須先完成前一步驟(防衝突與 SAK 判斷),單一卡片已被選定。

  • 指定目標: 讀卡器必須指定要存取哪一個扇區中的哪一個區塊

  • 提供密鑰: 讀卡器必須使用該扇區的正確 Key AKey B 進行認證。

2. 認證的標準三步握手 (Three-Pass Handshake) 流程

為了確保安全性,MIFARE Classic 採用一種類似 Challenge-Response 的三步握手機制,密鑰本身不會在空中傳輸:

步驟動作主體傳輸內容 (抽象化)目的
1. 請求認證讀卡器 (PCD) -> 卡片 (PICC)認證命令 (AUTH) + Key Type (A 或 B) + Block Address + UID讀卡器發送請求,並使用提供的 Key 對數據進行加密計算。
2. 卡片挑戰讀卡器 (PCD) <- 卡片 (PICC)Nonce 1 (卡片發出的隨機數)卡片向讀卡器發出挑戰,並用其內部儲存的 Key A/B 對 Nonce 1 進行加密計算。
3. 讀卡器響應讀卡器 (PCD) -> 卡片 (PICC)Nonce 2 (讀卡器發出的隨機數) + 加密計算結果讀卡器用其知道的 Key 對 Nonce 1 進行相同計算,並將結果及 Nonce 2 傳回給卡片。
4. 認證完成讀卡器 (PCD) <- 卡片 (PICC)加密計算結果 (卡片版本)卡片比較讀卡器的結果與自己的計算結果。如果一致,卡片回覆一個加密後的響應,並進入認證成功狀態。

只有在認證成功後,讀卡器才能根據扇區尾部的存取控制位元 (Access Bits) 對該扇區的資料區塊進行讀取或寫入操作。


第二部分:Python Tkinter 認證流程模擬






import tkinter as tk

from tkinter import ttk

import random


class MifareAuthenticationSimulator:

    def __init__(self, master):

        self.master = master

        master.title("MIFARE 1K 認證流程 (Tkinter 模擬)")


        # 模擬正確密鑰 (Key A)

        self.CORRECT_KEY = "FFFFFFFFFFFF"

        

        # 狀態變數

        self.status_text = tk.StringVar()

        self.log_text = tk.StringVar()

        self.card_access_status = tk.StringVar()


        # 初始化介面

        self.create_widgets()

        self.reset_simulator()


    def create_widgets(self):

        # 輸入區塊:密鑰和區塊地址

        input_frame = ttk.LabelFrame(self.master, text="認證參數 (模擬)")

        input_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew")


        ttk.Label(input_frame, text="密鑰 (Key A/B, 12字元HEX):").grid(row=0, column=0, padx=5, pady=5, sticky='w')

        self.key_entry = ttk.Entry(input_frame, width=20)

        self.key_entry.grid(row=0, column=1, padx=5, pady=5, sticky='w')

        self.key_entry.insert(0, "FFFFFFFFFFFF") # 預設輸入正確密鑰


        ttk.Label(input_frame, text="區塊地址 (0-63, 模擬):").grid(row=1, column=0, padx=5, pady=5, sticky='w')

        self.block_entry = ttk.Entry(input_frame, width=20)

        self.block_entry.grid(row=1, column=1, padx=5, pady=5, sticky='w')

        self.block_entry.insert(0, "4") # 模擬區塊 4 (扇區 1 的第一個資料區塊)


        # 操作按鈕

        self.auth_button = ttk.Button(self.master, text="執行認證 (Authentication)", command=self.start_authentication)

        self.auth_button.grid(row=1, column=0, padx=10, pady=5, sticky="ew")

        

        # 流程紀錄

        log_frame = ttk.LabelFrame(self.master, text="三步握手流程紀錄")

        log_frame.grid(row=2, column=0, padx=10, pady=5, sticky="ew")

        self.log_label = ttk.Label(log_frame, textvariable=self.log_text, wraplength=450, justify=tk.LEFT, font=('Arial', 10, 'italic'))

        self.log_label.grid(row=0, column=0, padx=5, pady=5, sticky='w')


        # 最終結果

        result_frame = ttk.LabelFrame(self.master, text="認證結果")

        result_frame.grid(row=3, column=0, padx=10, pady=5, sticky="ew")

        self.result_label = ttk.Label(result_frame, textvariable=self.card_access_status, font=('Arial', 12, 'bold'), foreground='black')

        self.result_label.grid(row=0, column=0, padx=5, pady=5, sticky='w')

        

        # 重置按鈕

        self.reset_button = ttk.Button(self.master, text="重置", command=self.reset_simulator)

        self.reset_button.grid(row=4, column=0, padx=10, pady=5, sticky="ew")


    def reset_simulator(self):

        self.log_text.set("請輸入密鑰和區塊地址,然後點擊 '執行認證'。")

        self.card_access_status.set("當前狀態:未認證")

        self.result_label.config(foreground='black')

        self.auth_button.config(state=tk.NORMAL)

        

    def generate_nonce(self):

        # 模擬產生 4 byte 隨機數 (Nonce)

        return f"{random.getrandbits(32):08X}"


    def start_authentication(self):

        # 獲取使用者輸入

        key_input = self.key_entry.get().upper()

        block_input = self.block_entry.get()

        

        self.log_text.set("")

        self.card_access_status.set("認證進行中...")

        self.result_label.config(foreground='orange')

        self.auth_button.config(state=tk.DISABLED)


        log = ""

        

        # --- 步驟 1: 讀卡器請求認證 ---

        log += f"1. 讀卡器 (PCD) 發送認證請求:\n"

        log += f"   - 參數:使用 Key A, 區塊地址 {block_input}, 卡片 UID。\n"

        

        # --- 步驟 2: 卡片發出挑戰 (Nonce 1) ---

        nonce1 = self.generate_nonce()

        log += f"2. 卡片 (PICC) 回覆挑戰:\n"

        log += f"   - 傳輸:發送隨機數 Nonce 1 ({nonce1})。\n"

        

        # --- 步驟 3: 讀卡器響應與密鑰檢查 ---

        log += f"3. 讀卡器執行密鑰計算並回覆:\n"

        

        is_key_correct = (key_input == self.CORRECT_KEY)


        if is_key_correct:

            # 密鑰正確 - 模擬成功響應

            log += f"   - 內部:讀卡器使用正確密鑰 ({key_input}) 對 Nonce 1 進行加密計算。\n"

            log += f"   - 傳輸:發送 Nonce 2 和加密計算結果給卡片。\n"

            

            # --- 步驟 4: 卡片驗證 ---

            log += f"4. 卡片內部驗證:\n"

            log += f"   - 結果:讀卡器的計算結果與卡片內部計算結果一致。\n"

            self.card_access_status.set("認證成功!現在可以讀寫區塊資料。")

            self.result_label.config(foreground='green')

        else:

            # 密鑰錯誤 - 模擬失敗響應

            log += f"   - 內部:讀卡器使用錯誤密鑰 ({key_input}) 進行計算。\n"

            log += f"   - 傳輸:卡片收到結果後,發現計算不符。\n"

            

            self.card_access_status.set("認證失敗!密鑰不匹配或流程出錯。")

            self.result_label.config(foreground='red')

            

        self.log_text.set(log)

        self.auth_button.config(state=tk.NORMAL)



if __name__ == "__main__":

    root = tk.Tk()

    app = MifareAuthenticationSimulator(root)

    # 設置視窗大小,讓內容更容易呈現

    root.geometry("500x500") 

    root.mainloop()


  • 輸入參數: 視窗上方有兩個模擬輸入欄位:

    • 密鑰 (Key A/B): 預設為正確的密鑰 FFFFFFFFFFFF

    • 區塊地址: 預設為 4(模擬存取扇區 1 中的一個資料區塊)。

  • 成功認證: 保持密鑰為預設值,點擊「執行認證」,流程紀錄會顯示成功的四個步驟,最終結果顯示綠色的「認證成功」。

  • 失敗認證: 將密鑰修改為任意錯誤的值 (例如 111111111111),點擊「執行認證」,流程紀錄會顯示到步驟 3,但最終結果顯示紅色的「認證失敗」。

  • 沒有留言:

    張貼留言

    ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite

     ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite  ESP32 VS Code 程式 ; PlatformIO Project Configuration File ; ;   Build op...