概述
对称加密算法是使用同一把密钥加密明文和解密密文的算法,其保障数据的机密性。常见的对称加密算法有AES、DES和SM4等。
应用场景
假定Alice要向Bob传送表白信,具体过程如下:
- Alice事先需要和Bob共享一把密钥
- Alice使用对称密钥对该信加密,并将密文发送给Bob
- Bob收到密文信后,使用相同的密钥对信进行解密
- Bob得到明文表白信后十分开心
分组密码模式
对称密码算法一次只能处理固定长度的数据,消息需要进行分组和填充,分组密码模式规范参考NIST SP 800-38A,其定义了五种模式:ECB模式,CBC模式,CFB模式,OFB模式,CTR模式。
ECB模式
ECB模式将明文分组加密之后直接变成密文分组,由于存在安全隐患,目前已废弃。
CBC模式
CBC模式首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密,由于第一个明文分组前
面没有密文分组,需要事先准备,其被称为初始化向量IV。CBC模式仅解密支持并行计算。
CFB模式
CFB模式将明文分组与密码算法的输出进行XOR运算来生成密文分组,无需填充,仅解密支持并行计算。
OFB模式
OFB模式将密码算法的输出反馈到密码算法的输入中,无需填充,不支持并行计算。
CTR模式
CTR模式首先对逐次累加的计数器进行加密来生成密钥流,然后与明文分组进行XOR运算得到密文分组。CTR模式仅需要加密算法而不需要解密算法,另外不需要对明文进行填充,支持并行计算。
PKCS填充方案
上面一些分组密码模式要求明文长度是分组密码长度的整数倍,如ECB模式和CBC模式。因此如果明文长度不满足是分组密码长度的整数倍要求,需要对其进行填充,常见的填充方案是PKCS#7。例如AES-CBC算法的分组长度为16字节,如果明文消息的长度为28字节,则需要在其末尾填充4个字节的0x04
,使其为32字节,即可满足明文长度是分组密码长度的整数倍要求。另外如果明文消息刚好是16字节的倍数,也需要再填充16字节的0x10
,这样可以判断解密之后的明文是否经过填充。
AES
AES算法是一个对称分组加密算法,规范为NIST FIPS 197。AES算法分组块大小为128 bit,需要4个32位的数据,表示为Nb=4。AES算法密钥长度为128、192和256 bit,同样表示为Nk=4,6,8。AES算法密钥长度不同,对应的加密转换轮数不一样,128位密钥长度对应Nr=10轮,192位密钥长度对应Nr=12轮,256位密钥长度对应Nr=14轮。
算法 | 密钥长度Nk | 分组块大小Nb | 轮数Nr |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
AES-128算法执行加解密的过程如下所示:
AES算法的加密过程伪代码如下所示,其中in
为输入明文,out
为输入密文,w
为轮密钥:
对于AES-128加密算法总共需要执行10轮转换操作,每一轮都是由四个操作组成(解密只是逆向操作):
- 字节替换(SubBytes)
- 行移位(ShiftRows)
- 列混合(MixColumns)
- 轮密钥加法(AddRoundKey)
AES算法将消息划分成若干16字节的消息分组,每个分组块映射到4x4的字节矩阵上,该矩阵称为状态(state)。假设分组块数据为:
32 43 f6 a8 88 5a 30 8d 31 31 98 a2 e0 37 07 34
映射到4x4的字节矩阵上如下:
[ 32 88 31 e 0 43 5 a 31 37 f 6 30 98 07 a 8 8 d a 2 34 ] \left[ \begin{matrix} 32 & 88 & 31 & e0 \\ 43 & 5a & 31 & 37 \\ f6 & 30 & 98 & 07 \\ a8 & 8d & a2 & 34 \end{matrix} \right] 3243f6a8885a308d313198a2e0370734
同理,假设密钥数据为:
2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c
映射到4x4的字节矩阵上如下:
[
2
b
28
a
b
09
7
e
a
e
f
7
c
f
15
d
2
15
4
f
16
a
6
88
3
c
]
\left[ \begin{matrix} 2b & 28 & ab & 09 \\ 7e & ae & f7 & cf \\ 15 & d2 & 15 & 4f \\ 16 & a6 & 88 & 3c \end{matrix} \right]
2b7e151628aed2a6abf7158809cf4f3c
第一轮的输入状态矩阵是输入明文矩阵与初始密钥进行异或得到,结果如下:
[
19
a
0
9
a
e
9
3
d
f
4
c
6
f
8
e
3
e
2
8
d
48
b
e
2
b
2
a
08
]
\left[ \begin{matrix} 19 & a0 & 9a & e9 \\ 3d & f4 & c6 & f8 \\ e3 & e2 & 8d & 48 \\ be & 2b & 2a & 08 \end{matrix} \right]
193de3bea0f4e22b9ac68d2ae9f84808
密钥扩展
AES算法将输入密钥扩展成为轮密钥,轮密钥个数为轮数加1。例如AES-128总共需要10轮操作,轮密钥个数为11。轮密钥是通过递归计算得到,伪代码如下:
其中RotWord
是执行行移位操作,SubWord
是执行S盒替换操作,Rcon
是轮密钥常量。例如密钥扩展第一轮的轮密钥计算流程如下图所示:
字节替换
每轮的字节替换是将输入的4x4字节矩阵通过S盒进行查找替换,替换后可以得到一个新的4x4字节矩阵,替换过程如下图所示:
其中S盒以16进制表示,如下:
以状态矩阵中的数据0x5a
为例,取S盒中第5行第a列的数据进行替换,即替换后的数据为0xbe
。
行移位
行移位是将矩阵的第2行向右移动3字节,第3行向右移动2字节,第4行向右移动1字节,而第1行保持不变。
列混合
列混合是将一个固定矩阵与状态矩阵进行相乘,即变换后矩阵中的每个元素等于固定矩阵中的相应行和状态矩阵相应列对应元素的乘积之和,其中加法和乘法都是在扩展域GF(28)内完成的。固定矩阵定义如下:
[
02
03
01
01
01
02
03
01
01
01
02
03
03
01
01
02
]
\left[ \begin{matrix} 02 & 03 & 01 & 01 \\ 01 & 02 & 03 & 01 \\ 01 & 01 & 02 & 03 \\ 03 & 01 & 01 & 02 \end{matrix} \right]
02010103030201010103020101010302
轮密钥加法
轮密钥加法操作是将状态矩阵与该轮的密钥进行异或运算,而轮密钥是通过密钥生成算法得到。轮密钥加法操作如下图所示:
AES算法示例
NIST FIPS 197规范的附录B给出一个AES-ECB-128示例,展示了加密一个块时状态矩阵和轮密钥变化的过程,其中明文、密钥和密文的数据如下表:
# 明文
32 43 f6 a8 88 5a 30 8d 31 31 98 a2 e0 37 07 34
# 密钥
2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c
# 密文
39 02 dc 19 25 dc 11 6a 84 09 85 0b 1d fb 97 32
示例密钥为128比特,数据块也为128比特,总共需要执行10轮操作。
可以看出第一轮计算完成后的状态矩阵等于上一章节执行完成轮密钥加法后的数据矩阵。
应用示例
使用openssl
对上面的示例消息数据进行加解密测试,使用AES-ECB-128模式,不进行数据填充。
$ echo -n 3243f6a8885a308d313198a2e0370734 | xxd -r -ps > msg.txt
# 加密
$ openssl enc -e -aes-128-ecb -in msg.txt -out ciphertext.txt -K 2b7e151628aed2a6abf7158809cf4f3c -nopad
# 解密
$ openssl enc -d -aes-128-ecb -in ciphertext.txt -out plaintext.txt -K 2b7e151628aed2a6abf7158809cf4f3c -nopad
$ diff plaintext.txt msg.txt
.txt -out ciphertext.txt -K 2b7e151628aed2a6abf7158809cf4f3c -nopad
# 解密
$ openssl enc -d -aes-128-ecb -in ciphertext.txt -out plaintext.txt -K 2b7e151628aed2a6abf7158809cf4f3c -nopad
$ diff plaintext.txt msg.txt