一、算法概述
1、AES密码与分组密码Rijndael基本一致,AES只要求分组大小为128位,所以可以称128位的Rijndael才成为AES。
明文x(128位)经过k(128/192/256位)的AES得到128位的密文y
密钥长度 | 轮数= |
128位 | 10 |
192位 | 12 |
256位 | 14 |
AES不具有feistel网络,注意feistel网络每轮迭代只加密64/2位,AES在每轮迭代中就加密了所有的128位,因此AES的轮数比DES小。
2、AES由群构成,每层操纵的数据路径(算法状态)对应所有的128位
密钥加法层:128位轮密钥(来自主密钥),与状态进行异或操作;
字节代换层(S-盒)根据查找表进行非线性转换,在加密中引入混淆操作;(上篇的DES中有介绍)
扩散层:由两个子层构成,每层都执行线性操作:
ShiftRows(行位移变化)层在位级进行行数据转换;
MixColumn(列行列变换)一个混淆操作,混淆了长度四个字节的分组;
二、数学知识
1、首先介绍一下域:
域F是具有以下特性的元素的集合:
- F中所有元素形成一个加法交换群,单位元为0
- F中除0外所有元素构成一个乘法交换群,单位元为1
- 混合使用加法和乘法两种操作时,分配定理始终成立,即a(b+c)=(ab)+(ac)
域所包含的元素个数称为域的阶或基
当m是一个素数幂时,及(p是素数,n为正整数),阶为m的域才存在,p为这个有限域的特征。
2、接着介绍素域:
有限域最直观的例子及阶为素数的域,即n=1,域GF(p)的元素可以用整数0,1,...,p-来表示。域中的群操作就是模整数加法和模整数乘法,模p。
假设p是一个素数,整数环Zp表示为GF(p),也称为是拥有素数个元素的素数域或伽罗瓦域。GF(p)中所有非零元素都存在逆元,GF(p)内所有运算模p。
GF(2)是一个非常重要的素域,也是存在的最小有限域。
3、介绍拓展域:
拓展域的加法:例A(x)=
;B(x)=
C(x)=A(x)+B(x)=
乘法:不可约多项式(可以看作素数的作用)例
可约即(
)(
)
GF(
)的逆操作:给定GF(
)与其对应的不可约简化多项式,任何一个非0元素A
GF(
)的逆元为:
三、AES内部结构
1、单轮AES结构
1、字节代换:AES的字节代换其实就是一个简单的查表操作。AES定义了一个S盒和一个逆S盒。这里不列了
2、扩散层
(1)shift子层
行移位是一个简单的左循环移位操作。例子:
(2)MixColumn子层
列混合变换是通过矩阵相乘来实现的,经行移位后的状态矩阵与固定的矩阵相乘,得到混淆后的状态矩阵
3、密钥加法层
与本轮子密钥进行按位Xor
四、密钥编排
五、python实现
AES只是个基本算法,实现AES有几种模式,主要有ECB、CBC、CFB等几种模式。CBC模式中还有一个偏移量参数IV
ECB加密
from Crypto.Cipher import AES
import base64
# 补位
pad = lambda s: s + chr(16 - len(s) % 16) * (16 - len(s) % 16)
# 除去补16字节的多余字符
unpad = lambda s: s[:-s[-1]]
# 加密函数
def aes_ECB_Encrypt(data, key): # ECB模式的加密函数,data为明文,key为16字节密钥
key = key.encode('utf-8')
data = pad(data) # 补位
data = data.encode('utf-8')
aes = AES.new(key=key, mode=AES.MODE_ECB) # 创建加密对象
# encrypt AES加密 B64encode为base64转二进制编码
result = base64.b64encode(aes.encrypt(data))
return str(result, 'utf-8') # 以字符串的形式返回
key = '1qaz@WSXabcdefgh' # 秘钥
data = "haha1234567890" # 明文字符串
encrypt_data = aes_ECB_Encrypt(data, key)
print("待加密的字符是:{}\n秘钥为:{}\n加密后的密文为:{}".format(data, key, encrypt_data))
解密
from Crypto.Cipher import AES
import base64
key = '1qaz@WSXabcdefgh' # 秘钥
data = "haha1234567890" # 明文字符串
encrypt_data =
# 解密函数
def aes_ECB_Decrypt(data, key): # ECB模式的解密函数,data为密文,key为16字节密钥
key = key.encode('utf-8')
aes = AES.new(key=key, mode=AES.MODE_ECB) # 创建解密对象
# decrypt AES解密 B64decode为base64 转码
result = aes.decrypt(base64.b64decode(data))
result = unpad(result) # 除去补16字节的多余字符
return str(result, 'utf-8') # 以字符串的形式返回
decrypt_data = aes_ECB_Decrypt(encrypt_data, key)
print("\n待解密的字符是:{}\n秘钥为:{}\n解密后的字符为:{}".format(encrypt_data, key, decrypt_data))
CBC模式加密
from Crypto.Cipher import AES
import base64
# 补位
pad = lambda s: s + chr(16 - len(s) % 16) * (16 - len(s) % 16)
# 除去补16字节的多余字符
unpad = lambda s: s[:-s[-1]]
# 加密函数
def aes_CBC_Encrypt(data, key, iv): # CBC模式的加密函数,data为明文,key为16字节密钥,iv为16字节的偏移量
key = key.encode('utf-8')
iv = iv.encode('utf-8') # CBC 模式下的偏移量
data = pad(data) # 补位
data = data.encode('utf-8')
aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) # 创建加密对象
# encrypt AES加密 B64encode为base64转二进制编码
result = base64.b64encode(aes.encrypt(data))
return str(result, 'utf-8') # 以字符串的形式返回
key = '1qaz@WSXabcdefgh' # 秘钥
data = "haha1234567890" # 明文字符串
iv = "1a2b3c4d5e6f7g8h" # 偏移量
encrypt_data = aes_CBC_Encrypt(data, key, iv)
print("待加密的字符是:{}\n秘钥为:{}\n偏移量为:{}\n加密后的密文为:{}".format(data, key, iv, encrypt_data))
解密
from Crypto.Cipher import AES
import base64
key = '1qaz@WSXabcdefgh' # 秘钥
data = "haha1234567890" # 明文字符串
iv = "1a2b3c4d5e6f7g8h" # 偏移量
encrypt_data =
# 解密函数
def aes_CBC_Decrypt(data, key, iv): # CBC模式的解密函数,data为密文,key为16字节密钥
key = key.encode('utf-8')
iv = iv.encode('utf-8')
aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) # 创建解密对象
# decrypt AES解密 B64decode为base64 转码
result = aes.decrypt(base64.b64decode(data))
result = unpad(result) # 除去补16字节的多余字符
return str(result, 'utf-8') # 以字符串的形式返回
decrypt_data = aes_CBC_Decrypt(encrypt_data, key, iv)
print("\n待解密的字符是:{}\n秘钥为:{}\n偏移量为:{}\n解密后的字符为:{}".format(encrypt_data, key, iv, decrypt_data))
五、例题
NewStarCTF 2023 公开赛道 babyaes
from Crypto.Cipher import AES
import os
from flag import flag
from Crypto.Util.number import *
def pad(data):
return data + b"".join([b'\x00' for _ in range(0, 16 - len(data))])
def main():
flag_ = pad(flag)
key = os.urandom(16) * 2
iv = os.urandom(16)
print(bytes_to_long(key) ^ bytes_to_long(iv) ^ 1)
aes = AES.new(key, AES.MODE_CBC, iv)
enc_flag = aes.encrypt(flag_)
print(enc_flag)
if __name__ == "__main__":
main()
# 3657491768215750635844958060963805125333761387746954618540958489914964573229
# b'>]\xc1\xe5\x82/\x02\x7ft\xf1B\x8d\n\xc1\x95i'
采用CBC模式,需要key和iv因为key有16*2字节,iv只有16字节,前部爆露,可以得到key和iv然后直接解密
from Crypto.Cipher import AES
from Crypto.Util.number import *
a = 3657491768215750635844958060963805125333761387746954618540958489914964573229
m = b'>]\xc1\xe5\x82/\x02\x7ft\xf1B\x8d\n\xc1\x95i'
key2 = long_to_bytes(a)[:16]
iv = bytes_to_long(long_to_bytes(a)[16:])^1^bytes_to_long(key2)
aes = AES.new(key2*2, AES.MODE_CBC, long_to_bytes(iv))
c = aes.decrypt(m)
print(c)