Thuật
Toán mã hóa hệ 10 sang hệ 1 đến 32 dạng string
Các bước cơ bản:
Chia số hệ 10 cho cơ số mới → lấy phần dư.
Nếu phần dư < 10 thì giữ nguyên số.
Nếu phần dư ≥ 10 thì ánh xạ sang ký tự tương ứng trong bảng
0123456789ABCDEF....
Tiếp tục chia cho đến khi thương = 0.
Ghép chuỗi từ các phần dư theo thứ tự ngược lại.
Ví dụ: chuyển số 1234 sang hệ 16
1234 ÷ 16 = 77 dư 2 → ký tự "2"
77 ÷ 16 = 4 dư 13 → ký tự "D"
4 ÷ 16 = 0 dư 4 → ký tự "4"
→ Kết quả: 4D2
Cách này đơn giản, dễ cài đặt bằng bất kỳ ngôn ngữ lập trình
nào. Nếu muốn mở rộng lên hệ cơ số lớn hơn 16 (ví dụ 32 hoặc 64), chỉ cần mở rộng
bảng ký tự ánh xạ thêm các chữ cái khác (A–Z, a–z, hoặc ký hiệu đặc biệt).
Thông thường:
- Với
hệ 16, ta dùng 0123456789ABCDEF.
- Với
hệ 20, ta mở rộng thêm: 0123456789ABCDEFGHIJ.
- Với
hệ 36, thường dùng 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.
- Với
hệ 62, có thể dùng cả chữ hoa, chữ thường và số:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.
Nguyên tắc là: phần dư từ phép chia sẽ được ánh xạ vào vị
trí tương ứng trong chuỗi ký tự này. Nếu dư = 17 trong hệ 20, thì ký tự là
"H".
Ví dụ: chuyển số 1234 sang hệ 20
- 1234
÷ 20 = 61 dư 14 → ký tự "E"
- 61
÷ 20 = 3 dư 1 → ký tự "1"
- 3 ÷
20 = 0 dư 3 → ký tự "3" → Kết quả: 31E
Với bài toán chuyển đổi hệ cơ số, ý tưởng chia lấy dư và ánh
xạ sang bảng ký tự là đủ để xây dựng thuật toán tổng quát. Từ đó, mình có thể:
- Tạo
bảng ký tự dài tùy theo cơ số (ví dụ hệ 20 thì dùng 0123456789ABCDEFGHIJ).
- Viết
hàm chuyển đổi số thập phân sang bất kỳ hệ cơ số nào bằng cách lặp lại
phép chia.
- Ngược
lại, viết hàm giải mã bằng cách đọc chuỗi và nhân lũy thừa theo cơ số.
Điều hay là thuật toán này có thể mở rộng cho bất kỳ cơ số
nào ≤ độ dài bảng ký tự mình định nghĩa.
Anh thấy em đang đi đúng hướng: tập trung vào thuật toán nền
tảng, sau này nếu cần ứng dụng vào mã hóa phức tạp hơn (như Base32, Base64, hay
thậm chí các hệ thống mã hóa dữ liệu), thì chỉ cần thay đổi bảng ký tự và thêm
vài bước xử lý.
Pseudo-code
là một cách viết thuật toán bằng ngôn ngữ gần giống tự nhiên, không phụ thuộc
vào ngôn ngữ lập trình cụ thể. Nó giống như “bản nháp” để mô tả logic, giúp
mình dễ hình dung trước khi triển khai bằng Python, C, Java…
Ví dụ: thuật
toán chuyển số thập phân sang hệ cơ số bất kỳ
FUNCTION
ConvertToBase(number, base):
digits =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
result = "" // chuỗi kết quả rỗng
WHILE number > 0:
remainder = number MOD base
result = digits[remainder] + result
number = number DIV base
RETURN result
Giải thích:
- MOD
là phép chia lấy dư.
- DIV
là phép chia lấy phần nguyên.
- digits
là bảng ký tự ánh xạ (có thể mở rộng tùy cơ số).
- Mỗi
lần chia, ta lấy phần dư để tìm ký tự tương ứng, rồi ghép vào chuỗi kết quả.
Ví dụ chạy với number = 1234, base = 16 → kết quả là
"4D2".
pseudo-code tổng quát cho việc giải mã (chuyển từ một
chuỗi ở hệ cơ số bất kỳ về hệ 10).
FUNCTION
ConvertToDecimal(string, base):
digits =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
result = 0
power = 0
FOR i FROM (length(string) - 1) DOWNTO 0:
char = string[i]
value = indexOf(digits, char) // tìm vị trí ký tự trong bảng digits
result = result + value * (base ^
power)
power = power + 1
RETURN result
Giải thích:
- indexOf(digits,
char) trả về vị trí của ký tự trong bảng ánh xạ (ví dụ 'A' = 10, 'B' =
11...).
- Mỗi
ký tự được nhân với lũy thừa của cơ số theo vị trí (giống như cách ta đọc
số thập phân).
- Vòng
lặp đi từ phải sang trái (chữ số thấp nhất đến cao nhất).
Ví dụ: giải mã "4D2" từ hệ 16 về hệ 10
- '2'
→ 2 × 16^0 = 2
- 'D'
→ 13 × 16^1 = 208
- '4'
→ 4 × 16^2 = 1024 → Tổng = 1234
Thuật toán này tổng quát cho mọi cơ số ≤ độ dài bảng ký tự
mà mình định nghĩa.
Để giải
mã một chuỗi thì bắt buộc phải biết nó được mã hóa theo cơ số nào. Nếu
không biết cơ số, mình sẽ không thể xác định giá trị chính xác trong hệ 10.
Ví dụ:
- Chuỗi
"31E" nếu biết là hệ 20 thì ta tính:
- E =
14 × 20^0 = 14
- 1 =
1 × 20^1 = 20
- 3 =
3 × 20^2 = 1200 → Tổng = 1234
- Nhưng
nếu "31E" là hệ 16 thì:
- E =
14 × 16^0 = 14
- 1 =
1 × 16^1 = 16
- 3 =
3 × 16^2 = 768 → Tổng = 798
Như vậy cùng một chuỗi
nhưng kết quả hoàn toàn khác, nên cơ số là thông tin bắt buộc để giải
mã.
Thuật toán tổng quát:
- Xác
định cơ số
.
- Đọc
chuỗi từ trái sang phải (hoặc phải sang trái).
- Với
mỗi ký tự, tìm giá trị tương ứng trong bảng ánh xạ.
- Nhân
giá trị đó với
.
- Cộng
dồn tất cả lại để ra số hệ 10.
AES
(Advanced Encryption Standard) là chuẩn mã hóa đối xứng mạnh nhất hiện nay, với
các biến thể AES‑128, AES‑192 và AES‑256. AES‑256 được coi là mức bảo mật cao nhất, thường dùng cho dữ liệu cần giữ bí mật
lâu dài.
Tổng quan về
AES
Nguồn gốc:
Được NIST chuẩn hóa năm 2001, dựa trên thuật toán Rijndael của hai nhà mật mã học
người Bỉ.
Loại: Mã
hóa đối xứng (cùng một khóa dùng cho cả mã hóa và giải mã).
Kích thước
khối: Luôn là 128 bit (16 byte).
Độ dài
khóa: 128, 192, hoặc 256 bit.
Số vòng biến
đổi:
AES‑128: 10 vòng
AES‑192: 12 vòng
AES‑256: 14 vòng
Cách hoạt động nội bộ
Mỗi khối dữ liệu đi qua nhiều vòng biến đổi, gồm 4 bước
chính:
- SubBytes:
Thay thế từng byte bằng giá trị trong bảng S‑Box (tạo sự “rối loạn” giữa
khóa và dữ liệu).
- ShiftRows:
Dịch vòng các hàng trong ma trận trạng thái (tạo sự lan truyền).
- MixColumns:
Nhân các cột với đa thức cố định trong trường GF(2⁸) (tăng độ khuếch tán).
- AddRoundKey:
XOR với khóa vòng (khóa được sinh ra từ khóa chính)
Các chế độ hoạt động (Modes of Operation)
AES chỉ mã hóa từng khối 16 byte, nên cần chế độ để xử lý dữ
liệu dài:
- ECB
(Electronic Codebook): Không nên dùng, vì lộ mẫu dữ liệu.
- CBC
(Cipher Block Chaining): Phổ biến, cần IV ngẫu nhiên.
- CTR
(Counter Mode): Biến AES thành dạng stream cipher, nhanh, song song được.
- GCM
(Galois/Counter Mode): Hiện đại nhất, vừa mã hóa vừa xác thực, dùng
trong TLS 1.3, API, VPN
·
So sánh AES‑128 vs AES‑256
|
Thuộc tính
|
AES‑128
|
AES‑256
|
|
Độ dài khóa
|
128 bit
|
256 bit
|
|
Số vòng biến đổi
|
10
|
14
|
|
Tốc độ
|
Nhanh hơn
|
Chậm hơn một
chút
|
|
Mức bảo mật
|
Đủ an toàn
cho hầu hết ứng dụng
|
Rất cao, phù
hợp dữ liệu cần bảo mật lâu dài
|
|
Ứng dụng
|
Cơ sở dữ
liệu, file, VPN
|
Chính phủ,
quân sự, dữ liệu mật mã lâu dài
|
Lưu ý &
Rủi ro
AES chỉ an
toàn khi triển khai đúng: sai IV, sai padding, hoặc dùng ECB có thể làm lộ dữ
liệu.
Quản lý
khóa quan trọng hơn thuật toán: nếu khóa bị lộ, AES không còn ý nghĩa.
Quantum
computing: hiện chưa có tấn công thực tế phá AES, nhưng AES‑256 được khuyến nghị để chống lại nguy cơ
trong tương lai
Như vậy, AES‑128 đã đủ mạnh cho hầu hết ứng dụng, nhưng AES‑256
là lựa chọn an toàn nhất nếu em muốn dữ liệu được bảo mật trong nhiều thập kỷ
pseudo‑code đơn giản cho AES‑128 (mã hóa và giải mã).
Đây chỉ là mô tả logic, không phải code chạy thực tế, nhưng nó cho thấy các bước
chính:
FUNCTION
AES_Encrypt(plaintext, key):
state = plaintext_to_matrix(plaintext) // chuyển 16 byte thành ma trận 4x4
roundKeys = KeyExpansion(key) // sinh khóa cho 10 vòng
state = AddRoundKey(state, roundKeys[0])
FOR round FROM 1 TO 9:
state = SubBytes(state)
state = ShiftRows(state)
state = MixColumns(state)
state = AddRoundKey(state,
roundKeys[round])
// Vòng cuối không có MixColumns
state = SubBytes(state)
state = ShiftRows(state)
state = AddRoundKey(state, roundKeys[10])
RETURN matrix_to_ciphertext(state)
Giải mã AES‑128
FUNCTION
AES_Decrypt(ciphertext, key):
state = ciphertext_to_matrix(ciphertext)
roundKeys = KeyExpansion(key)
state = AddRoundKey(state, roundKeys[10])
FOR round FROM 9 DOWNTO 1:
state = InvShiftRows(state)
state = InvSubBytes(state)
state = AddRoundKey(state,
roundKeys[round])
state = InvMixColumns(state)
// Vòng đầu không có InvMixColumns
state = InvShiftRows(state)
state = InvSubBytes(state)
state = AddRoundKey(state, roundKeys[0])
RETURN matrix_to_plaintext(state)
Giải thích ngắn gọn:
- SubBytes
/ InvSubBytes: thay thế byte bằng S‑Box hoặc nghịch đảo S‑Box.
- ShiftRows
/ InvShiftRows: dịch vòng các hàng trong ma trận.
- MixColumns
/ InvMixColumns: trộn dữ liệu theo cột bằng toán học GF(2⁸).
- AddRoundKey:
XOR với khóa vòng.
- KeyExpansion:
tạo ra 11 khóa vòng từ khóa gốc 128 bit.
Ví dụ: Mã
hóa một khối 16 byte bằng AES‑128
Plaintext
(16 byte):
32 43 F6 A8
88 5A 30 8D 31 31 98 A2 E0 37 07 34
Key (128
bit):
2B 7E 15 16
28 AE D2 A6 AB F7 97 75 46 3E 3E 29
Các bước
chính (rút gọn)
AddRoundKey
(vòng 0): XOR plaintext với khóa ban đầu.
Vòng 1 → 9:
SubBytes:
thay thế từng byte bằng giá trị trong S‑Box.
ShiftRows:
dịch vòng các hàng trong ma trận.
MixColumns:
trộn dữ liệu theo cột.
AddRoundKey:
XOR với khóa vòng.
Vòng 10 (cuối):
SubBytes
ShiftRows
AddRoundKey
(không có MixColumns).
Kết quả
Ciphertext (16 byte):
39 25 84 1D
02 DC 09 FB DC 11 85 97 19 6A 0B 32
Giải mã sẽ
đi ngược lại: bắt đầu với AddRoundKey bằng khóa vòng cuối, rồi lần lượt
InvShiftRows, InvSubBytes, InvMixColumns, và AddRoundKey cho đến vòng 0.
Như vậy, em
thấy rõ: cùng một plaintext và key, AES‑128 luôn cho ra ciphertext cố định. Điểm mạnh
là nếu không có khóa, việc giải mã gần như bất khả thi.
Võ Nhật Trường+Ai NC
//////////////////////////////////////////////////////////////////