AES 核心特性
- 對稱加密:加密解密使用相同金鑰
- 分組長度:128 bits (16 bytes)
- 金鑰長度:128 / 192 / 256 bits
- 輪數:10 / 12 / 14 輪 (依金鑰長度)
- 核心運算:有限域 GF(2⁸) 上的替代 (SubBytes)、置換 (ShiftRows, MixColumns)、輪金鑰加 (AddRoundKey)
.NET 內建實作 (生產環境首選)
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public static class AesHelper
{
///
/// AES 加密 (CBC 模式,PKCS7 填充)
///
public static string Encrypt(string plainText, byte[] key, byte[] iv)
{
using (var aes = Aes.Create())
{
aes.Key = key; // 16/24/32 bytes
aes.IV = iv; // 16 bytes
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var encryptor = aes.CreateEncryptor())
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var sw = new StreamWriter(cs, Encoding.UTF8))
{
sw.Write(plainText);
}
return Convert.ToBase64String(ms.ToArray());
}
}
///
/// AES 解密
///
public static string Decrypt(string cipherTextBase64, byte[] key, byte[] iv)
{
byte[] cipherBytes = Convert.FromBase64String(cipherTextBase64);
using (var aes = Aes.Create())
{
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var decryptor = aes.CreateDecryptor())
using (var ms = new MemoryStream(cipherBytes))
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
using (var sr = new StreamReader(cs, Encoding.UTF8))
{
return sr.ReadToEnd();
}
}
}
///
/// 從密碼衍生金鑰與 IV (PBKDF2)
///
public static (byte[] Key, byte[] IV) DeriveKeyAndIV(string password, byte[] salt, int keySize = 256)
{
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100_000, HashAlgorithmName.SHA256))
{
byte[] key = pbkdf2.GetBytes(keySize / 8);
byte[] iv = pbkdf2.GetBytes(16); // IV 固定 16 bytes
return (key, iv);
}
}
}
// 使用範例
class Program
{
static void Main()
{
string password = "MyStrongPassword123!";
byte[] salt = new byte[16];
using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(salt);
var (key, iv) = AesHelper.DeriveKeyAndIV(password, salt, 256);
string original = "機密資料:信用卡號 1234-5678-9012-3456";
string encrypted = AesHelper.Encrypt(original, key, iv);
string decrypted = AesHelper.Decrypt(encrypted, key, iv);
Console.WriteLine($"原文:{original}");
Console.WriteLine($"密文:{encrypted}");
Console.WriteLine($"解密:{decrypted}");
Console.WriteLine($"Salt (需儲存):{Convert.ToBase64String(salt)}");
}
}
AES 核心數學原理速覽 (理解實作細節)
有限域 GF(2⁸) 運算
- 元素:0x00 ~ 0xFF (256 個位元組值)
- 加法:XOR (^) 運算
- 乘法:模不可約多項式
x⁸ + x⁴ + x³ + x + 1 (0x11B) 的多項式乘法
- 關鍵乘法常數:0x01, 0x02, 0x03, 0x09, 0x0B, 0x0D, 0x0E
四大輪函數
- SubBytes:透過 S-Box (替代盒) 進行非線性位元組替代,提供混淆性
- ShiftRows:狀態矩陣列向左循環位移 (第 0 列 0 位、第 1 列 1 位...),提供擴散性
- MixColumns:列向量在 GF(2⁸) 上與固定矩陣相乘,跨列混合
- AddRoundKey:狀態與輪金鑰 XOR
金鑰擴展
- 將原始金鑰擴展為 (Nr+1) × 16 bytes 輪金鑰陣列
- 使用 RotWord (字循環)、SubWord (S-Box 替代)、Rcon (輪常數)
- 確保每輪金鑰差異大,抵抗關聯攻擊
手工實作 AES (教學用,非生產建議)
// 完整手工實作極長,此處展示核心結構
public class AesManual
{
private readonly byte[,] _sBox = new byte[16,16]; // 標準 S-Box
private readonly byte[,] _invSBox = new byte[16,16];
private readonly byte[,] _rCon = new byte[11,4]; // Rcon[1..10]
private byte[,] _w; // 輪金鑰陣列 [4, 4*(Nr+1)]
private int _nr; // 輪數
public AesManual(byte[] key)
{
// 1. 依金鑰長度設定 Nr, Nk
// 2. 建構 S-Box, InvSBox (仿射變換 + 乘法逆元)
// 3. 建構 Rcon
// 4. KeyExpansion() 填充 _w
}
private byte GF_Mul(byte a, byte b)
{
// 俄羅斯農夫乘法 (位元級實作)
byte result = 0;
while (b > 0)
{
if ((b & 1) != 0) result ^= a;
bool hiBit = (a & 0x80) != 0;
a <<= 1;
if (hiBit) a ^= 0x1B; // 模 x^8+x^4+x^3+x+1
b >>= 1;
}
return result;
}
public void Encrypt(byte[] input, byte[] output)
{
// 1. 載入 State[4,4]
// 2. AddRoundKey (第 0 輪)
// 3. for round = 1 to Nr-1: SubBytes, ShiftRows, MixColumns, AddRoundKey
// 4. 最後一輪: SubBytes, ShiftRows, AddRoundKey (無 MixColumns)
// 5. 輸出
}
public void Decrypt(byte[] input, byte[] output)
{
// 逆序操作:InvShiftRows, InvSubBytes, AddRoundKey, InvMixColumns
// InvMixColumns 使用係數 0x0E, 0x0B, 0x0D, 0x09
}
}
模式選擇指南
| 模式 | IV 需求 | 平行化 | 適用場景 | 警示 |
| CBC | 必要 (隨機) | 解密可平行 | 通用資料加密 | 需 PKCS7 填充,防填充神谕攻擊 |
| GCM | 必要 (Nonce) | 加解密可平行 | 需認證加密 (AEAD) | .NET Core 3.1+ 支援 AesGcm,**強烈推薦** |
| CTR | 必要 (Counter) | 完全平行 | 串流加密、隨機存取 | 不可重複 Nonce,無完整性保護 |
| ECB | 無 | 完全平行 | **嚴禁用於機密資料** | 相同明文產生相同密文,洩漏模式 |
現代最佳實踐:AES-GCM (認證加密)
// .NET Core 3.1 / .NET 5+ 專用
public static class AesGcmHelper
{
public static (byte[] CipherText, byte[] Tag) Encrypt(byte[] plainText, byte[] key, byte[] nonce)
{
var cipherText = new byte[plainText.Length];
var tag = new byte[16]; // 128-bit 標籤
using (var aes = new AesGcm(key))
{
aes.Encrypt(nonce, plainText, cipherText, tag);
}
return (cipherText, tag);
}
public static byte[] Decrypt(byte[] cipherText, byte[] tag, byte[] key, byte[] nonce)
{
var plainText = new byte[cipherText.Length];
using (var aes = new AesGcm(key))
{
aes.Decrypt(nonce, cipherText, tag, plainText);
}
return plainText;
}
}
關鍵安全檢查清單
- ✅ 永不硬編碼金鑰:使用 PBKDF2/Argon2 從密碼衍生,或 KMS/HSM 管理。
- ✅ 每次加密產生新隨機 IV/Nonce:使用
RandomNumberGenerator.Create()。
- ✅ 金鑰輪換機制:定期更換主金鑰,保留舊金鑰解密歷史資料。
- ✅ 使用認證加密 (GCM/CCM):同時保證機密性與完整性,防篡改。
- ✅ 防計時攻擊:比較標籤/金鑰用
CryptographicOperations.FixedTimeEquals。
- ✅ 安全銷毀敏感資料:
Array.Clear(key, 0, key.Length) 或 Span.Clear()。
- ❌ 不要自己寫 AES 給生產用:側通道攻擊、實作錯誤風險極高,務必用經過驗證的庫 (BouncyCastle、.NET 內建)。
效能參考 (AES-256-CBC, 1MB 資料)
| 實作 | 加密時間 | 解密時間 | 備註 |
.NET 內建 Aes (硬體加速 AES-NI) | ~8 ms | ~7 ms | 現代 CPU 內建指令集加速 |
.NET 內建 AesGcm | ~10 ms | ~9 ms | 含認證標籤計算 |
| 純 C# 手工實作 | ~1200 ms | ~1100 ms | 無硬體加速、教學用 |