目录
1. AES简介
高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法。对称加密算法也就是加密和解密用相同的密钥,具体的加密流程如下图:
名词 | 含义 |
---|---|
明文P | 没有经过加密的数据 |
秘钥K | 用来加密明文的密码,在对称加密算法中,加密与解密的密钥是相同的。密钥为接收方与发送方协商产生,但不可以直接在网络上传输,否则会导致密钥泄漏,通常是通过非对称加密算法加密密钥,然后再通过网络传输给对方,或者直接面对面商量密钥。密钥是绝对不可以泄漏的,否则会被攻击者还原密文,窃取机密数据。 |
AES加密函数 | 设AES加密函数为E,则 C = E(K, P),其中P为明文,K为密钥,C为密文。也就是说,把明文P和密钥K作为加密函数的参数输入,则加密函数E会输出密文C。 |
密文C | 经加密函数处理后的数据 |
AES解密函数 | 设AES解密函数为D,则 P = D(K, C),其中C为密文,K为密钥,P为明文。也就是说,把密文C和密钥K作为解密函数的参数输入,则解密函数会输出明文P。 |
AES只是个基本算法,实现AES有几种模式,主要有ECB、CBC、CFB等几种模式。CBC模式中还有一个偏移量参数IV。
AES加密有AES-128、AES-192和AES-256三种,分别对应三种密钥长度128位(16字节)、192位(24字节)和256位(32字节)。密钥越长,安全性越高,加密和解密时间也会更长。一般默认是128位,其安全性完全够用。
2. 加密/解密时,字节数不够时的处理
2.1 加密时
因为密钥是16字节,所以明文加密时,字符串不足16字节的倍数,则要补充个数,例如:少4个,要补chr(4)chr(4)chr(4)chr(4),少2个,要补chr(2)chr(2)。
chr(参数)中的参数是缺少的字节,要补全。这里为什么要补充chr(缺少位的ASCII码作为参数),是因为这样能更好的读取字符串最后字符时,知道有几个填充字符,从而能采用字符串切片操作而逆向清除填充字符。
实现方法:
明文字符串 + chr(16-len(明文字符串)%16) * (16 - len(明文字符串)%16)
# 1.自定义函数实现
def pad(data):
text = data + chr(16 - len(data) % 16) * (16 - len(data) % 16)
return text
# 2.用lambda匿名函数
pad = lambda s: s + chr(16 - len(s) % 16) * (16 - len(s) % 16)
2.2 解密时
加密时字符串不足16字节倍数时,填充的字符是chr(缺少的字节数作为参数的),所以逆向清除填充的字符串,利用最后字符的ASCII码进行切片得出所需字符串。
实现方法:
# 1.自定义函数实现
def unpad(s):
last_num = s[-1]
text = s[:-last_num]
return text
# 2.用lambda匿名函数
unpad = lambda s: s[:-s[-1]]
3. 加/解密用到的pyhton库
用到Crypto 和 base64,Crypto包安装方法:
pip install cryptodome
安装后,在使用的时候可能还会提示包找不到,可以去包所在的路径看下包文件名是不是Crypto,如果是crypto,则需要改下名字即可
base64为内置库,无需安装。
Base64是网络上最常见的用于传输8Bit字节码的编码方式,能实现二进制与字符之间编码的互转。所以说Base64就是把字符串以二进制编码/解码。其中,b64encode为编码,b64decode为解码
4. 加/解密的python实现
4.1 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))
执行结果如下:
待加密的字符是:haha1234567890
秘钥为:1qaz@WSXabcdefgh
加密后的密文为:2Waxo/Owz/UdizFLDVcyiQ==
4.2 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))
执行结果为:
待加密的字符是:haha1234567890
秘钥为:1qaz@WSXabcdefgh
偏移量为:1a2b3c4d5e6f7g8h
加密后的密文为:O2P4OqlNzEQHUpSfXE8KPw==
4.3 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))
# 解密函数
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))
执行结果:
待加密的字符是:haha1234567890
秘钥为:1qaz@WSXabcdefgh
加密后的密文为:2Waxo/Owz/UdizFLDVcyiQ==
待解密的字符是:2Waxo/Owz/UdizFLDVcyiQ==
秘钥为:1qaz@WSXabcdefgh
解密后的字符为:haha1234567890
4.4 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))
# 解密函数
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))
执行结果:
待加密的字符是:haha1234567890
秘钥为:1qaz@WSXabcdefgh
偏移量为:1a2b3c4d5e6f7g8h
加密后的密文为:O2P4OqlNzEQHUpSfXE8KPw==
待解密的字符是:O2P4OqlNzEQHUpSfXE8KPw==
秘钥为:1qaz@WSXabcdefgh
偏移量为:1a2b3c4d5e6f7g8h
解密后的字符为:haha1234567890