文章目录
基础知识
需要先了解什么是有限域
, 伽罗瓦有限域
, GF(28), 否则就是看天书
AES
AES的本质: 替换, 移位, 异或, 每一步都是可逆的
ISO/IEC 18033-3, 不是免费文档.
http://www.doc88.com/p-6405051328829.html
https://tools.ietf.org/html/rfc3962
AES每次加密128 bits数据plaintext, 也就是16字节.
AES128的key为128 bits, 执行10轮加密, 记为Nr = 10
AES192的key为192 bits, 执行12轮加密 记为Nr = 12
AES256的key为256 bits, 执行14轮加密 记为Nr = 12
加密过程如下:
-
初始始State表, S = P
-
S = AddRoundKey(S, W0), 这里的W0也是一个4X4的矩阵
-
for i=1 to (Nr-1) : { //重复Nr-1次
S = SubBytes(S)
S = ShiftRows(S)
S = MixColumns(S)
S = AddRoundKey(S, Wi)
} -
S = SubBytes(S)
S = ShiftRows(S)
C = AddRoundKey(S, WNr)
C即为最后的密文
解释
Wi为4X4的矩阵, 为 [w(4i),w(4i+1),w(4i+2),w(4i+4)]
小写的w是单列数据, 由密钥key扩展生成, 规则见下面的章节
代码演示
https://github.com/wzjwhut/aes-encryption
以下是每一个步骤的说明
初始化State表
假设这plaintext 16个字节的数据分别为
p0, p1, p2, … p15
首先, 创建一个4x4的表s, 这个表称为State表
State表 | 列0 | 列1 | 列2 | 列3 |
---|---|---|---|---|
行0 | s0,0 | s0,1 | s0,2 | s0,3 |
行1 | s1,0 | s1,1 | s1,2 | s1,3 |
行2 | s2, 0 | s2,1 | s2,2 | s2,3 |
行3 | s3, 0 | s3,1 | s3,2 | s3,3 |
使用plaintext来初始化s
按照从上往下, 从左往右的顺序填充State表
State表 | 列0 | 列1 | 列2 | 列3 |
---|---|---|---|---|
行0 | p0 | p4 | p8 | p12 |
行1 | p1 | p5 | p9 | p13 |
行2 | p2 | p6 | p10 | p14 |
行3 | p3 | p7 | p11 | p15 |
替换SubBytes()
有一张称为S-box的表, 将S表中的值替换成S-box中的的值, 规则如下
假设si,j的16进制显示为mn, 其中m∈[0, f], n∈[0,f], 那么
si,j被替换成S-boxm,n
S-box https://en.wikipedia.org/wiki/Rijndael_S-box , 这个表是通过有限域的仿射变换生成的
举例, 设 si,j = 83 = 0x53, 那么使用S-box5,3 = 0xed 替换, 也就是0x53被0xed替换.
S-Box的生成规则:
首先找出GF(28)中的互为乘法逆元的元素对, 然后对这些元素执行Rijndael变形.
0 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0a | 0b | 0c | 0d | 0e | 0f | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 63 | 7c | 77 | 7b | f2 | 6b | 6f | c5 | 30 | 01 | 67 | 2b | fe | d7 | ab | 76 |
1 | ca | 82 | c9 | 7d | fa | 59 | 47 | f0 | ad | d4 | a2 | af | 9c | a4 | 72 | c0 |
2 | b7 | fd | 93 | 26 | 36 | 3f | f7 | cc | 34 | a5 | e5 | f1 | 71 | d8 | 31 | 15 |
3 | 04 | c7 | 23 | c3 | 18 | 96 | 05 | 9a | 07 | 12 | 80 | e2 | eb | 27 | b2 | 75 |
4 | 09 | 83 | 2c | 1a | 1b | 6e | 5a | a0 | 52 | 3b | d6 | b3 | 29 | e3 | 2f | 84 |
5 | 53 | d1 | 00 | ed | 20 | fc | b1 | 5b | 6a | cb | be | 39 | 4a | 4c | 58 | cf |
6 | d0 | ef | aa | fb | 43 | 4d | 33 | 85 | 45 | f9 | 02 | 7f | 50 | 3c | 9f | a8 |
7 | 51 | a3 | 40 | 8f | 92 | 9d | 38 | f5 | bc | b6 | da | 21 | 10 | ff | f3 | d2 |
8 | cd | 0c | 13 | ec | 5f | 97 | 44 | 17 | c4 | a7 | 7e | 3d | 64 | 5d | 19 | 73 |
9 | 60 | 81 | 4f | dc | 22 | 2a | 90 | 88 | 46 | ee | b8 | 14 | de | 5e | 0b | db |
a | e0 | 32 | 3a | 0a | 49 | 06 | 24 | 5c | c2 | d3 | ac | 62 | 91 | 95 | e4 | 79 |
b | e7 | c8 | 37 | 6d | 8d | d5 | 4e | a9 | 6c | 56 | f4 | ea | 65 | 7a | ae | 08 |
c | ba | 78 | 25 | 2e | 1c | a6 | b4 | c6 | e8 | dd | 74 | 1f | 4b | bd | 8b | 8a |
d | 70 | 3e | b5 | 66 | 48 | 03 | f6 | 0e | 61 | 35 | 57 | b9 | 86 | c1 | 1d | 9e |
e | e1 | f8 | 98 | 11 | 69 | d9 | 8e | 94 | 9b | 1e | 87 | e9 | ce | 55 | 28 | df |
f | 8c | a1 | 89 | 0d | bf | e6 | 42 | 68 | 41 | 99 | 2d | 0f | b0 | 54 | bb | 16 |
逆替换SubBytes-1()
SubBytes-1() 是SubBytes()还原操作
上例中, 也就是0x53被0xed替换. 还原操作就是将0xed再还原成0x53, 也是使用查表法
https://en.wikipedia.org/wiki/Rijndael_S-box 上有生成好的还原表.
行移位ShiftRows()
规则
Sr, c = Sr, (c+r) mod 4 , (0<r<4, 0≤c<4)
也就是, 第1行不变换, 剩下的3行需要变化.
比如
S1, 0 = S1, 1,
S1, 1 = S1, 2,
S1, 2 = S1, 3,
S1, 3 = S1, 0,
也就是, 第2行的数据循环向左移了1个位置. 依次类推:
第3行的数据循环向左移了2个位置
第4行的数据循环向左移了3个位置
逆行移位变换ShiftRows-1()
这是移位变换的还原操作, 也就是
第2行的数据循环向右移1个位置
第3行的数据循环向右移2个位置
第4行的数据循环向右移3个位置
列混合变换MixColumns()
公式为
S’0,c = ( 2 • S0,c ) ⊕ ( 3 • S1,c) ⊕ S2,c ⊕ S3,c
S’1,c = S0,c ⊕ ( 2 • S1,c ) ⊕ ( 3 • S2,c) ⊕ S3,c
S’2,c = S0,c ⊕ S1,c⊕ ( 2 • S2,c ) ⊕ ( 3 • S3,c)
S’2,c = ( 3 • S0,c) ⊕ S1,c ⊕ S2,c⊕ ( 2 • S3,c )
其中
⊕
表示异或
操作, 也可以认为是有限域GF( 28 )中的加法操作
•
表示有限域GF( 28 )的乘法操作. 不是整数上的乘法操作. 代码实现可参考
https://en.wikipedia.org/wiki/Finite_field_arithmetic#Rijndael.27s_finite_field
https://blog.csdn.net/wzj_whut/article/details/86521447
这个操作也可以写成矩阵形式
逆列混合变换MixColumns-1()
公式为
S’0,c = ( 0e • S0,c ) ⊕ ( 0b • S1,c) ⊕ (0d • S2,c ) ⊕ ( 09 • S3,c )
S’0,c = ( 09 • S0,c ) ⊕ ( 0e • S1,c) ⊕ (0b • S2,c ) ⊕ ( 0d • S3,c )
S’0,c = ( 0d • S0,c ) ⊕ ( 09 • S1,c) ⊕ (0e • S2,c ) ⊕ ( 0b • S3,c )
S’0,c = ( 0b • S0,c ) ⊕ ( 0d • S1,c) ⊕ (09 • S2,c ) ⊕ ( 0e • S3,c )
矩阵形式(中间的矩阵是16进制表示的)
密钥扩展Key schedule
对于AES128来说, 初始key为16个字节, 第4字节记为1列, 共4列, 记Nk=4, Nr=10 (10轮操作)
(对于AES196来说, 初始key为24个字节, 共6列, 记Nk=6, Nr=12, 其它同理)
密钥扩展的意思是: 将初始密钥扩展到4*(Nr+1) -1
列
伪代码如下, (小写的wi 表示第i列数据, 不是矩阵):
for j = Nk to ( 4*(Nr+1) -1 )
if( j mod Nk == 0 ) then
wj = wj-Nk ⊕ SubBytes( ShiftColumn(wj-1)) ⊕ R j/Nkc
else
wj = wj-Nk ⊕ wj-1
解释:
ShiftColumn表示将这列数据循环上移1个位置.
SubBytes( ShiftColumn(wj-1)) 表示循环上移1个位置之后, 再使用S-Box执行替换操作
R j/Nkc的计算规则为GF(28)的元素同余操作
xj/Nk -1 mod ( x8+x4+x3+x+1)
可以按照以下规则进行代码编写(也可以查表)
https://en.wikipedia.org/wiki/Rijndael_key_schedule
AddRoundKey()
Wi = [w(4i), w(4i+1), w(4i+2), w(4i+3)], 0≤i≤Nr
对State表的每列, 执行以下操作
Key,PlainText未对齐16字节的处理
最简单的方式, 就是补零. 如果是加密字符串, 那么就很好处理, 遇到0, 就认为是结束符.
对于二进制的数据, 需要依赖更上层的封装协议来指明padding占用的字节长度.
AES模式
https://ws680.nist.gov/publication/get_pdf.cfm?pub_id=51031
ECB: 最简单的方式, (原文,密钥) => 密文
CBC: 先将原文与IV异或, 再加密, (原文⊕IV ,密钥) => 密文
CFB: 过程比较复杂, 自己看文档吧. 大概就是, 上个数据块的输出, 会作为下个数据块的输入.
CTR: 复杂, 自己看文档吧.