Chào mừng các bạn đến với Bài 4! Ở 3 bài học trước, chúng ta đã thu thập đủ 3 mảnh ghép nền tảng:
Bài 1: Bài toán Chữ ký số & Ý nghĩa bảo mật.
Bài 2: Cặp khóa bất đối xứng Public Key & Private Key.
Bài 3: Hàm băm SHA-256 – "Dấu vân tay" của dữ liệu.
Bây giờ là lúc chúng ta lắp ráp 3 mảnh ghép này lại với nhau để hình thành quy trình ký và xác minh chữ ký số hoàn chỉnh trong thực tế.
Quy trình chữ ký số luôn gồm 2 giai đoạn độc lập:
Người ký lấy Văn bản gốc đưa qua Hàm băm SHA-256 $\rightarrow$ Thu được Mã băm A (Hash A). ( Đây là secret_number trong mã python ở bài 2)
Người ký dùng Private Key của chính mình để mã hóa Mã băm A $\rightarrow$ Kết quả thu được chính là CHỮ KÝ SỐ (Digital Signature). ( Đây là singed_data trong mã python ở bài 2).
Người ký gửi cho Người nhận gói dữ liệu gồm: [Văn bản gốc + Chữ ký số + Public Key của Người ký].
[PHÍA NGƯỜI KÝ - ALICE]
Văn bản gốc ─────────► [ Hàm băm SHA-256 ] ─────────► Mã băm A
│
Private Key (Alice) ──────────────────────────────────────┴──► [ Mã hóa RSA ] ──► CHỮ KÝ SỐ
Khi nhận được gói dữ liệu, Người nhận tiến hành kiểm tra song song 2 nhánh:
Nhánh 1 (Tính lại mã băm): Lấy Văn bản gốc vừa nhận đưa qua Hàm băm SHA-256 $\rightarrow$ Thu được Mã băm B1. ( đây chính mã băm A )
Nhánh 2 (Giải mã chữ ký): Lấy Chữ ký số nhận được giải mã bằng Public Key của Alice $\rightarrow$ Thu được Mã băm B2.
So sánh:
Nếu $\text{Mã băm B1} == \text{Mã băm B2}$: ✅ Chữ ký HỢP LỆ! (Đúng chính chủ Alice ký và văn bản còn nguyên vẹn 100%).
Nếu $\text{Mã băm B1} \neq \text{Mã băm B2}$: ❌ Chữ ký KHÔNG HỢP LỆ! (Văn bản đã bị sửa đổi hoặc chữ ký bị giả mạo).
[PHÍA NGƯỜI NHẬN - BOB]
Nhánh 1: Văn bản gốc nhận được ────► [ Hàm băm SHA-256 ] ───────────► Mã băm B1
│
Nhánh 2: Chữ ký số nhận được ──────► [ Giải mã bằng Public Key ] ────► Mã băm B2
│
[ So sánh B1 == B2 ]
/ \
CÓ (Valid) KHÔNG (Invalid)
Bên gửi (Kế toán trưởng Alice):
Viết lệnh chuyển tiền: "Chuyển 50.000.000 VNĐ cho nhân viên A".
Máy tính của Alice tạo mã băm cho lệnh này: hash_val = 12345.
Alice bấm nút "Ký số": Máy tính dùng Private Key của Alice biến 12345 thành Chữ ký số sig = 998877.
Gửi Lệnh chuyển tiền + Chữ ký 998877 cho Ngân hàng.
Bên nhận (Hệ thống Ngân hàng):
Ngân hàng nhận Lệnh chuyển tiền + Chữ ký 998877.
Kiểm tra 1: Ngân hàng băm Lệnh chuyển tiền nhận được $\rightarrow$ Ra mã băm 12345.
Kiểm tra 2: Ngân hàng lấy Public Key của Alice giải mã Chữ ký 998877 $\rightarrow$ Trả về kết quả 12345.
Vì 12345 == 12345, Ngân hàng xác nhận: "Lệnh này chuẩn 100% do Alice ký, chuyển tiền ngay!".
Nếu Hacker sửa số tiền thành 500 triệu:
Mã băm Lệnh chuyển tiền mới sẽ biến thành 67890.
Trong khi Chữ ký cũ giải mã ra vẫn là 12345.
67890 != 12345 $\rightarrow$ Ngân hàng phát hiện gian lận và dừng giao dịch lập tức!
Dưới đây là chương trình Python mô phỏng trọn vẹn quy trình Ký & Xác minh trên mà chỉ sử dụng hashlib và các phép toán Python chuẩn:
import hashlib
# =====================================================================
# BÀI 4: LẮP RÁP QUY TRÌNH KÝ & XÁC MINH CHỮ KÝ SỐ (PURE PYTHON)
# =====================================================================
# 1. HÀM BỔ TRỢ: TÍNH MÃ BĂM VÀ CHUYỂN THÀNH SỐ NGUYÊN (SHA-256)
def get_message_hash_integer(message_str: str, modulus_n: int) -> int:
"""Tạo mã băm SHA-256 từ chuỗi và nén về số nguyên nhỏ hơn n để tính toán RSA"""
hash_bytes = hashlib.sha256(message_str.encode('utf-8')).digest()
# Chuyển 4 bytes đầu thành số nguyên
hash_int = int.from_bytes(hash_bytes[:4], byteorder='big')
# Giới hạn trong phạm vi modulus n
return hash_int % modulus_n
# =====================================================================
# BƯỚC 1: KHỞI TẠO CẶP KHÓA RSA CHO NGƯỜI KÝ (ALICE)
# =====================================================================
p, q = 61, 53
n = p * q # n = 3233
phi_n = (p - 1) * (q - 1) # phi_n = 3120
e = 17
d = pow(e, -1, phi_n) # d = 2753
public_key = (e, n) # Public Key công khai cho Bob
private_key = (d, n) # Private Key Alice giữ bí mật
print("--- 1. KHỞI TẠO CẶP KHÓA ALICE ---")
print(f"Public Key (e, n): {public_key}")
print(f"Private Key (d, n): {private_key}\n")
# =====================================================================
# BƯỚC 2: PHÍA NGƯỜI KÝ (ALICE) - TẠO CHỮ KÝ SỐ
# =====================================================================
print("--- 2. ALICE TIẾN HÀNH KÝ SỐ ---")
original_doc = "Xác nhận duyệt chi kinh phí dự án: 50.000.000 VNĐ."
print(f"Văn bản gốc: '{original_doc}'")
# a. Băm văn bản gốc thành mã băm số nguyên
hash_a = get_message_hash_integer(original_doc, n)
print(f"Mã băm A (SHA-256) của văn bản gốc: {hash_a}")
# b. Dùng Private Key (d, n) mã hóa Mã băm A -> Chữ ký số
# Công thức RSA: Signature = (Hash_A ^ d) mod n
digital_signature = pow(hash_a, d, n)
print(f"✍️ CHỮ KÝ SỐ ĐÃ TẠO (Digital Signature): {digital_signature}\n")
# =====================================================================
# BƯỚC 3: PHÍA NGƯỜI NHẬN (BOB) - XÁC MINH CHỮ KÝ SỐ
# =====================================================================
print("--- 3. BOB TIẾN HÀNH XÁC MINH ---")
# Bob nhận được: original_doc, digital_signature, và public_key của Alice
# Nhánh 1: Bob tự băm văn bản nhận được
hash_b1 = get_message_hash_integer(original_doc, n)
# Nhánh 2: Bob giải mã Chữ ký số bằng Public Key của Alice (e, n)
# Công thức RSA: Decrypted_Hash = (Signature ^ e) mod n
hash_b2 = pow(digital_signature, e, n)
print(f"Mã băm B1 (Tính từ văn bản nhận được) : {hash_b1}")
print(f"Mã băm B2 (Giải mã từ Chữ ký số) : {hash_b2}")
# So sánh 2 mã băm
if hash_b1 == hash_b2:
print("✅ KẾT QUẢ: Chữ ký HỢP LỆ! Đúng là Alice ký và văn bản không bị sửa đổi.\n")
else:
print("❌ KẾT QUẢ: Chữ ký KHÔNG HỢP LỆ!\n")
# =====================================================================
# BƯỚC 4: KỊCH BẢN TẤN CÔNG (HACKER SỬA NỘI DUNG VĂN BẢN)
# =====================================================================
print("--- 4. THỬ NGHIỆM VĂN BẢN BỊ HACKER THAY ĐỔI ---")
tampered_doc = "Xác nhận duyệt chi kinh phí dự án: 500.000.000 VNĐ." # Sửa số tiền
print(f"Văn bản bị sửa: '{tampered_doc}'")
# Bob xác minh lại văn bản đã bị sửa với chữ ký cũ
hash_tampered = get_message_hash_integer(tampered_doc, n)
print(f"Mã băm tính từ Văn bản BỊ SỬA : {hash_tampered}")
print(f"Mã băm giải mã từ Chữ ký CŨ : {hash_b2}")
if hash_tampered == hash_b2:
print("✅ KẾT QUẢ: Chữ ký HỢP LỆ!")
else:
print("❌ BÁO ĐỘNG BẢO MẬT: Văn bản đã bị thay đổi hoặc chữ ký bị giả mạo!")
| Đối tượng | Dữ liệu đầu vào | Thuật toán / Khóa sử dụng | Dữ liệu đầu ra |
| Phía Người Ký | Văn bản gốc | SHA-256 $\rightarrow$ Private Key | Chữ ký số |
| Phía Người Nhận | Chữ ký số | Public Key của người ký | Mã băm ban đầu |
| Xác minh | Mã băm từ văn bản & Mã băm từ chữ ký | Phép so sánh bằng == |
Kết quả Hợp lệ / Không hợp lệ |
Câu hỏi: Tại sao khi gửi tài liệu đã ký số trên mạng, người ký phải gửi kèm cả "Văn bản gốc" lẫn "Chữ ký số"? Nếu chỉ gửi "Chữ ký số" thôi thì người nhận có đọc được nội dung văn bản gốc không?
Đáp án:
Chữ ký số được tạo ra từ việc mã hóa Mã băm của văn bản, mà Mã băm là hàm một chiều (không dịch ngược được). Do đó, từ Chữ ký số không thể khôi phục lại Văn bản gốc.
Vì vậy, bắt buộc phải gửi kèm Văn bản gốc để người nhận vừa có thể đọc nội dung, vừa mang văn bản đó đi băm lại để đối chiếu xác minh.