不要验证码的广告网站,wordpress添加备案号,哪种nas可以做网站服务器,怎么给网站做关键词搜索1. 前言 AES是一种对称加密#xff0c;所谓对称加密就是加密与解密使用的秘钥是一个。
之前写过一片关于python AES加密解密的文章#xff0c;但是这里面细节实在很多#xff0c;这次我从 参数类型、加密模式、编码模式、补全模式、等等方面 系统的说明如何使用AES加密解密…1. 前言 AES是一种对称加密所谓对称加密就是加密与解密使用的秘钥是一个。
之前写过一片关于python AES加密解密的文章但是这里面细节实在很多这次我从 参数类型、加密模式、编码模式、补全模式、等等方面 系统的说明如何使用AES加密解密。
看文章不能急功近利为了解决一个问题临时查到一个代码套用进去或许可以迅速解决问题但是遇到新的问题还需要再次查询这种我认为还是比较浪费时间的。我相信看完认真看完这篇文章的你会大有收获。
2. 环境安装
pip uninstall crypto
pip uninstall pycryptodome
pip install pycryptodome前面两个卸载命令是为了防止一些安装环境问题具体请看章
3.加密模式 AES 加密最常用的模式就是 ECB模式 和 CBC 模式当然还有很多其它模式他们都属于AES加密。ECB模式和CBC 模式俩者区别就是 ECB 不需要 iv偏移量而CBC需要。
4.AES加密使用参数
以下参数都是在python中使用的。
参数作用及数据类型秘钥加密的时候用秘钥解密的时候需要同样的秘钥才能解出来; 数据类型为bytes明文需要加密的参数; 数据类型为bytes模式aes 加密常用的有 ECB 和 CBC 模式我只用了这两个模式还有其他模式;数据类型为aes类内部的枚举量iv 偏移量这个参数在 ECB 模式下不需要在 CBC 模式下需要数据类型为byte 下面简单的一个例子ECB模式加密解密 :
from Crypto.Cipher import AESpassword b1234567812345678 #秘钥b就是表示为bytes类型
text babcdefghijklmnhi #需要加密的内容bytes类型
aes AES.new(password,AES.MODE_ECB) #创建一个aes对象
# AES.MODE_ECB 表示模式是ECB模式
en_text aes.encrypt(text) #加密明文
print(密文,en_text) #加密明文bytes类型
den_text aes.decrypt(en_text) # 解密密文
print(明文,den_text)
输出:
密文 bWU\xe0\x0e\xa3\x87\x12\x95\\]O\xd7\xe3\xd4 )
明文 babcdefghijklmnhi
以上是针对ECB模式的加密解密从这个例子中可以看出参数中有几个限制。
秘钥必须为16字节或者16字节的倍数的字节型数据。明文必须为16字节或者16字节的倍数的字节型数据如果不够16字节需要进行补全关于补全规则后面会在补全模式中具体介绍。
通过CBC模式例子
from Crypto.Cipher import AES
password b1234567812345678 #秘钥b就是表示为bytes类型
iv b1234567812345678 # iv偏移量bytes类型
text babcdefghijklmnhi #需要加密的内容bytes类型
aes AES.new(password,AES.MODE_CBC,iv) #创建一个aes对象
# AES.MODE_CBC 表示模式是CBC模式
en_text aes.encrypt(text)
print(密文,en_text) #加密明文bytes类型
aes AES.new(password,AES.MODE_CBC,iv) #CBC模式下解密需要重新创建一个aes对象
den_text aes.decrypt(en_text)
print(明文,den_text)输出:
密文 b\x93\x8bN!\xe7~\xb0M\xba\x91\xab74;0
明文 babcdefghijklmnhi
通过上面CBC模式的例子可以简单看出CBC模式与ECB模式的区别AES.new() 解密和加密重新生成了aes对象加密和解密不能调用同一个aes对象否则会报错TypeError: decrypt() cannot be called after encrypt()。
总结
1. 在Python中进行AES加密解密时所传入的密文、明文、秘钥、iv偏移量、都需要是bytes字节型数据。python 在构建aes对象时也只能接受bytes类型数据。
2.当秘钥iv偏移量待加密的明文字节长度不够16字节或者16字节倍数的时候需要进行补全。
3. CBC模式需要重新生成AES对象为了防止这类错误我写代码无论是什么模式都重新生成AES对象。 5.编码模式
前面说了python中的 AES 加密解密只能接受字节型(bytes)数据。而我们常见的 待加密的明文可能是中文或者待解密的密文经过base64编码的这种都需要先进行编码或者解码然后才能用AES进行加密或解密。反正无论是什么情况在python使用AES进行加密或者解密时都需要先转换成bytes型数据。
我们以ECB模式针对中文明文进行加密解密举例
from Crypto.Cipher import AESpassword b1234567812345678 #秘钥b就是表示为bytes类型
text 好好学习天天向上.encode(gbk) #gbk编码是1个中文字符对应2个字节8个中文正好16字节
aes AES.new(password,AES.MODE_ECB) #创建一个aes对象
# AES.MODE_ECB 表示模式是ECB模式
print(len(text))
en_text aes.encrypt(text) #加密明文
print(密文,en_text) #加密明文bytes类型
den_text aes.decrypt(en_text) # 解密密文
print(明文,den_text.decode(gbk)) # 解密后同样需要进行解码
输出:
16
密文 b\xdd8k\x86\xed\xec\x17\x1f\xf7\xb2\x84~\x02\xc6C
明文 好好学习天天向上
对于中文明文我们可以使用encode()函数进行编码将字符串转换成bytes类型数据而这里我选择gbk编码是为了正好能满足16字节utf8编码是一个中文字符对应3个字节。这里为了举例所以才选择使用gbk编码。
在解密后同样是需要decode()函数进行解码的将字节型数据转换回中文字符字符串类型。
现在我们来看另外一种情况密文是经过base64编码的(这种也是非常常见的很多网站也是这样使用的)我们用 http://tool.chacuo.net/cryptaes/ 这个网站举例子 模式ECB
密码: 1234567812345678
字符集gbk编码
输出: base64
我们来写一个python 进行aes解密 from Crypto.Cipher import AES
import base64password b1234567812345678
aes AES.new(password,AES.MODE_ECB)
en_text bPd04a4bt7Bcf97KEfgLGQw
en_text base64.decodebytes(en_text) #将进行base64解码返回值依然是bytes
den_text aes.decrypt(en_text)
print(明文,den_text.decode(gbk))
输出:
明文 好好学习天天向上
这里的 bPd04a4bt7Bcf97KEfgLGQw 是一个bytes数据 如果你传递的是一个字符串你可以直接使用 encode()函数 将其转换为 bytes类型数据。
from Crypto.Cipher import AES
import base64password b1234567812345678
aes AES.new(password,AES.MODE_ECB)
en_text Pd04a4bt7Bcf97KEfgLGQw.encode() #将字符串转换成bytes数据
en_text base64.decodebytes(en_text) #将进行base64解码参数为bytes数据,返回值依然是bytes
den_text aes.decrypt(en_text)
print(明文,den_text.decode(gbk)) 因为无论是 utf8 和 gbk 编码针对英文字符编码都是一个字符对应一个字节所以这里**encode()**函数主要作用就是转换成bytes数据然后使用base64进行解码。
hexstrbase64编码解码例子
import base64
import binascii
data hello.encode()
data base64.b64encode(data)
print(base64编码:,data)
data base64.b64decode(data)
print(base64解码:,data)
data binascii.b2a_hex(data)
print(hexstr编码:,data)
data binascii.a2b_hex(data)
print(hexstr解码:,data) 输出:
base64编码: baGVsbG8
base64解码: bhello
hexstr编码: b68656c6c6f
hexstr解码: bhello
这里要说明一下有一些AES加密所用的秘钥或者IV向量是通过 base64编码或者 hexstr编码后的。针对这种首先要进行的就是进行解码都转换回 bytes数据再次强调python实现 AES加密解密传递的参数都是 bytes(字节型) 数据。
另外我记得之前的 pycryptodome库传递IV向量时和明文时可以直接使用字符串类型数据不过现在新的版本都必须为 字节型数据了可能是为了统一好记。 6.填充模式 前面我使用秘钥还有明文包括IV向量都是固定16字节也就是数据块对齐了。而填充模式就是为了解决数据块不对齐的问题使用什么字符进行填充就对应着不同的填充模式 AES补全模式常见有以下几种
模式意义ZeroPadding用b’\x00’进行填充这里的0可不是字符串0而是字节型数据的b’\x00’PKCS7Padding当需要N个数据才能对齐时填充字节型数据为N、并且填充N个PKCS5Padding与PKCS7Padding相同在AES加密解密填充方面我没感到什么区别no padding当为16字节数据时候可以不进行填充而不够16字节数据时同ZeroPadding一样
这里有一个细节问题我发现很多文章说的也是不对的。
ZeroPadding填充模式的意义很多文章解释是当为16字节倍数时就不填充然后当不够16字节倍数时再用字节数据0填充这个解释是不对的这解释应该是no padding的而ZeroPadding是不管数据是否对其都进行填充直到填充到下一次对齐为止也就是说即使你够了16字节数据它会继续填充16字节的0然后一共数据就是32字节。
这里可能会有一个疑问为什么是16字节 其实这个是 数据块的大小网站上也有对应设置网站上对应的叫128位也就是16字节对齐当然也有192位(24字节)256位(32字节)。
本文在这个解释之后后面就说数据块对齐问题了而不会再说16字节倍数了。
除了no padding 填充模式剩下的填充模式都会填充到下一次数据块对齐为止而不会出现不填充的问题。
PKCS7Padding和 PKCS5Padding需要填充字节对应表
明文长度值mod 16添加的填充字节数每个填充字节的值0160x101150x0F2140x0E3130X0D4120x0C5110x0B6100x0A790x09880x08970x081060x071150x061240x051330x041420x031510x01 这里可以看到当明文长度值已经对齐时mod 16 0还是需要进行填充并且填充16个字节值为0x10。ZeroPadding填充逻辑也是类似的只不过填充的字节值都为0x00在python表示成 b\x00。
填充完毕后就可以使用 AES进行加密解密了当然解密后也需要剔除填充的数据无奈Python这些步骤需要自己实现如果有这样的库还请评论指出。 7.Python的完整实现:
from Crypto.Cipher import AES
import base64
import binascii# 数据类
class MData():def __init__(self, data b,characterSetutf-8):# data肯定为bytesself.data dataself.characterSet characterSetdef saveData(self,FileName):with open(FileName,wb) as f:f.write(self.data)def fromString(self,data):self.data data.encode(self.characterSet)return self.datadef fromBase64(self,data):self.data base64.b64decode(data.encode(self.characterSet))return self.datadef fromHexStr(self,data):self.data binascii.a2b_hex(data)return self.datadef toString(self):return self.data.decode(self.characterSet)def toBase64(self):return base64.b64encode(self.data).decode()def toHexStr(self):return binascii.b2a_hex(self.data).decode()def toBytes(self):return self.datadef __str__(self):try:return self.toString()except Exception:return self.toBase64()### 封装类
class AEScryptor():def __init__(self,key,mode,iv ,paddingMode NoPadding,characterSet utf-8):构建一个AES对象key: 秘钥字节型数据mode: 使用模式只提供两种AES.MODE_CBC, AES.MODE_ECBiv iv偏移量字节型数据paddingMode: 填充模式默认为NoPadding, 可选NoPaddingZeroPaddingPKCS5PaddingPKCS7PaddingcharacterSet: 字符集编码self.key keyself.mode modeself.iv ivself.characterSet characterSetself.paddingMode paddingModeself.data def __ZeroPadding(self,data):data b\x00while len(data) % 16 ! 0:data b\x00return datadef __StripZeroPadding(self,data):data data[:-1]while len(data) % 16 ! 0:data data.rstrip(b\x00)if data[-1] ! b\x00:breakreturn datadef __PKCS5_7Padding(self,data):needSize 16-len(data) % 16if needSize 0:needSize 16return data needSize.to_bytes(1,little)*needSizedef __StripPKCS5_7Padding(self,data):paddingSize data[-1]return data.rstrip(paddingSize.to_bytes(1,little))def __paddingData(self,data):if self.paddingMode NoPadding:if len(data) % 16 0:return dataelse:return self.__ZeroPadding(data)elif self.paddingMode ZeroPadding:return self.__ZeroPadding(data)elif self.paddingMode PKCS5Padding or self.paddingMode PKCS7Padding:return self.__PKCS5_7Padding(data)else:print(不支持Padding)def __stripPaddingData(self,data):if self.paddingMode NoPadding:return self.__StripZeroPadding(data)elif self.paddingMode ZeroPadding:return self.__StripZeroPadding(data)elif self.paddingMode PKCS5Padding or self.paddingMode PKCS7Padding:return self.__StripPKCS5_7Padding(data)else:print(不支持Padding)def setCharacterSet(self,characterSet):设置字符集编码characterSet: 字符集编码self.characterSet characterSetdef setPaddingMode(self,mode):设置填充模式mode: 可选NoPaddingZeroPaddingPKCS5PaddingPKCS7Paddingself.paddingMode modedef decryptFromBase64(self,entext):从base64编码字符串编码进行AES解密entext: 数据类型strmData MData(characterSetself.characterSet)self.data mData.fromBase64(entext)return self.__decrypt()def decryptFromHexStr(self,entext):从hexstr编码字符串编码进行AES解密entext: 数据类型strmData MData(characterSetself.characterSet)self.data mData.fromHexStr(entext)return self.__decrypt()def decryptFromString(self,entext):从字符串进行AES解密entext: 数据类型strmData MData(characterSetself.characterSet)self.data mData.fromString(entext)return self.__decrypt()def decryptFromBytes(self,entext):从二进制进行AES解密entext: 数据类型bytesself.data entextreturn self.__decrypt()def encryptFromString(self,data):对字符串进行AES加密data: 待加密字符串数据类型为strself.data data.encode(self.characterSet)return self.__encrypt()def __encrypt(self):if self.mode AES.MODE_CBC:aes AES.new(self.key,self.mode,self.iv) elif self.mode AES.MODE_ECB:aes AES.new(self.key,self.mode) else:print(不支持这种模式) return data self.__paddingData(self.data)enData aes.encrypt(data)return MData(enData)def __decrypt(self):if self.mode AES.MODE_CBC:aes AES.new(self.key,self.mode,self.iv) elif self.mode AES.MODE_ECB:aes AES.new(self.key,self.mode) else:print(不支持这种模式) return data aes.decrypt(self.data)mData MData(self.__stripPaddingData(data),characterSetself.characterSet)return mDataif __name__ __main__:key b1234567812345678iv b0000000000000000aes AEScryptor(key,AES.MODE_CBC,iv,paddingMode ZeroPadding,characterSetutf-8)data 好好学习rData aes.encryptFromString(data)print(密文,rData.toBase64())rData aes.decryptFromBase64(rData.toBase64())print(明文,rData)
我简单的对其进行了封装加密和解密返回的数据类型可以使用toBase64(),toHexStr() 进行编码。另外我没有对key和iv进行补全可以使用MData类自己实现更多详细使用可以通过源码中注释了解。 文章出自:
原文链接https://blog.csdn.net/chouzhou9701/article/details/122019967
csdn上发现的一位大佬写怕找不到了就完全复制他是文章