AES概念详解

什么是 AES?

AES(Advanced Encryption Standard)是一种分组对称加密算法,加密单元固定为 128 bit(16 字节)。它支持三种密钥长度:

  • AES-128 → 10 轮

  • AES-192 → 12 轮

  • AES-256 → 14 轮

AES 是现代密码学的基石,被广泛用于 HTTPS、VPN、磁盘加密、移动支付等场景。


AES 的数据结构

AES 以一个 16 字节的数据块为单位工作,把这 16 个字节组织成一个 4×4 字节矩阵(state),列优先存储:

1
2
3
4
s0  s4  s8  s12
s1 s5 s9 s13
s2 s6 s10 s14
s3 s7 s11 s15

每轮加密都会对这个矩阵进行一系列固定变换。


AES 的四大核心操作

AES 的每一轮由四个步骤构成(最后一轮不含 MixColumns):

1. SubBytes(字节代换)

使用固定的 非线性 S-box 对每个字节独立替换。
S-box 的构造包含:

  • GF(2^8) 中求逆元

  • 进行一次仿射变换

这是 AES 安全性的主要来源。


2. ShiftRows(行移位)

对 state 按行循环左移:

  • 第 0 行:不变

  • 第 1 行:左移 1

  • 第 2 行:左移 2

  • 第 3 行:左移 3

此步骤用于跨列扩散。


3. MixColumns(列混合)

对每列执行一次固定的线性变换:

1
2
3
4
[02 03 01 01]
[01 02 03 01]
[01 01 02 03]
[03 01 01 02]

所有运算都在 GF(2^8) 中进行。MixColumns 能让单个字节的变化扩散到整列,使密文更加不可预测。


4. AddRoundKey(轮密钥异或)

把轮密钥(来自 Key Expansion)按字节 XOR 到 state 中。
XOR 是对称密码中最基本、最安全的组合方式。


密钥扩展(Key Expansion)

AES 不会每一轮都直接用原始密钥,而是把原始密钥扩展成多个 128-bit 子密钥,每一轮使用一个。

以 AES-128(16 字节密钥)为例,最终会生成 44 个 32-bit word

  • 前 4 个来自原始密钥

  • 每个后续 word 根据以下规则生成:

核心步骤包括:

  • RotWord(循环左移 1 字节)

  • SubWord(S-box 替换)

  • Rcon(加入轮常量)

AES-256 在 i % Nk == 4 时还会额外执行一次 SubWord。


AES 完整加密流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
State = 明文

初始轮:
AddRoundKey

中间轮 1..Nr-1:
SubBytes
ShiftRows
MixColumns
AddRoundKey

最后一轮:
SubBytes
ShiftRows
AddRoundKey

输出密文

解密只需执行上述步骤的逆操作(InvShiftRows、InvSubBytes、InvMixColumns、AddRoundKey)。


AES 需要配合“模式(Mode of Operation)”

AES 本身只能处理 16 字节。要加密任意长度数据必须使用模式:

  • ECB:不安全,不建议使用

  • CBC:传统模式,需随机 IV,需要填充

  • CTR:流式模式,无填充,需保证计数器不重复

  • GCM:带认证(AEAD),现代默认选项,HTTPS 就在用

实际工程中,优先使用 AES-GCM 或 AES-CTR。
CBC 和 ECB 都不推荐用于新项目。


Go语言使用AES

Go AES-GCM 加密 & 解密模板

AES-GCM 是 AEAD(Authenticated Encryption with Associated Data)模式,提供:

  • 加密

  • 认证

  • 防篡改

无需手动填充,也没有 padding oracle 风险。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package aesutil

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
)

// 加密:输出 nonce + ciphertext
func AesGcmEncrypt(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}

ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext, nil
}

// 解密:输入 nonce + ciphertext
func AesGcmDecrypt(key, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, io.ErrUnexpectedEOF
}

nonce := ciphertext[:nonceSize]
data := ciphertext[nonceSize:]

return gcm.Open(nil, nonce, data, nil)
}

使用示例:

1
2
3
4
key := []byte("0123456789abcdef") // 长度必须是16/24/32
ciphertext, _ := aesutil.AesGcmEncrypt(key, []byte("hello"))
plaintext, _ := aesutil.AesGcmDecrypt(key, ciphertext)
fmt.Println(string(plaintext)) // hello

AES-CTR

只有在特定场景下才用 CTR(并且必须认证):当需要流式分组(可任意中断/续传)、或必须与遗留系统兼容、或在某些硬件/协议里必须用 CTR 时,可用 CTR。但不要单用 CTR,必须配合强 MAC(例如 HMAC-SHA256 或 HKDF 派生的认证密钥)做 Encrypt-then-MAC,否则数据容易被篡改或遭受重放/剪切攻击。