问题描述
我在使用Python的AES256加密进行多遍加密时遇到麻烦。
到目前为止,我正在研究以下功能:
加密:
def AESEncrypt(plaintext, password, passes = 1):
try:
salt = Random.get_random_bytes(32)
iv = Random.get_random_bytes(16)
hmacsha256 = get_prf("hmac-sha256")
key = KDF.PBKDF2(password, salt, 32, 4096, hmacsha256[0])
aesManaged = AES.new(key, AES.MODE_CBC,iv)
padlength = 0
padByte = chr(0)
if len(plaintext) < 16:
padlength = 16 - len(plaintext)
padByte = chr(padlength)
else:
padlength = 16 - (len(plaintext) % 16)
padByte = chr(padlength)
for i in range(padlength):
plaintext = plaintext + b"\x00"
countByte = chr(passes)
if passes == 1:
ciphertext = countByte + padByte + iv + salt + aesManaged.encrypt(plaintext)
elif passes >= 2:
ciphertext = aesManaged.encrypt(plaintext)
for i in range(passes - 1):
ciphertext = aesManaged.encrypt(ciphertext)
ciphertext = countByte + padByte + iv + salt + ciphertext
return ciphertext
except:
print str(sys.exc_info()[1])
return None
解密:
def AESDecrypt(ciphertext, password):
try:
base_cipher = ciphertext
passes = ord(base_cipher[0])
padLength = ord(base_cipher[1])
iv = base_cipher[2:18]
salt = base_cipher[18:50]
hmacsha256 = get_prf("hmac-sha256")
key = KDF.PBKDF2(password, salt, 32, 4096, hmacsha256[0])
aesManaged = AES.new(key, AES.MODE_CBC,iv)
msg_cipher = base_cipher[50:]
if passes == 1:
plaintext_bytes = aesManaged.decrypt(msg_cipher)
elif passes >= 2:
plaintext_bytes = aesManaged.decrypt(msg_cipher)
for i in range(passes - 1):
plaintext_bytes = aesManaged.decrypt(plaintext_bytes)
if padLength > 0:
ptLength = len(plaintext_bytes)
plaintext_bytes = plaintext_bytes[:ptLength - padLength]
return plaintext_bytes
except:
print str(sys.exc_info()[1])
return None
至于写入二进制数据,它可以与单遍和多遍加密一起正常工作。
当涉及加密文本数据(如文本文件或消息)时,当我在解密时使用大于1的passes
值时,它会开始出错。
例如,如果我只使用纯文本,例如"Hello World!"
使用上述步骤或使用UTF-16,多遍加密将给我一堆损坏的文本(最多显示一半的消息),其余的消息则被正确解密。
当我也使用UTF-8编码(例如"Hello World".encode('utf-8')
进行相同的过程时,会收到一条错误消息(尽管每次运行时字节数和位置都会改变):
'utf8'编解码器无法解码位置0的字节0xf7:无效的起始字节
我做错了吗?
更新:
看Python文档为后bytes
和bytearray
的类型,我试图转换应该被认为是一个字节数组的部分:
(一旦获得正确的加密,我计划进行解密和类型检测部分)
def AESEncrypt(plaintext, password, passes = 1):
try:
plaintext = bytearray(plaintext)
salt = bytes(Random.get_random_bytes(32))
iv = bytes(Random.get_random_bytes(16))
hmacsha256 = get_prf("hmac-sha256")
key = KDF.PBKDF2(password, salt, 32, 4096, hmacsha256[0])
aesManaged = AES.new(key, AES.MODE_CBC,iv)
padlength = 0
padByte = chr(0)
if len(plaintext) < 16:
padlength = 16 - len(plaintext)
padByte = chr(padlength)
else:
padlength = 16 - (len(plaintext) % 16)
padByte = chr(padlength)
for i in range(padlength):
plaintext.append(b"\x00")
countByte = chr(passes)
if passes == 1:
ciphertext = bytes(countByte + padByte + iv + salt + aesManaged.encrypt(plaintext))
elif passes >= 2:
ciphertext = bytearray(aesManaged.encrypt(plaintext))
for i in range(passes - 1):
ciphertext = aesManaged.encrypt(ciphertext)
ciphertext.insert(0, salt)
ciphertext.insert(0, iv)
ciphertext.insert(0, padByte)
ciphertext.insert(0, countByte)
return ciphertext
except:
print "Error on line %d: %s" % (sys.exc_traceback.tb_lineno, str(sys.exc_info()[1]))
return None
现在,我只是想知道argument must be string or read-only buffer, not bytearray
应该为字节数组但要求输入字符串的部分的字节数组错误。
在当前示例中,它在elif passes >=2
之后的行中将密文转换为字节数组。
1楼
至少有两个问题:
sys.getdefaultencoding()
在您的Python 2环境中返回utf-8
而不是ascii
。 它可能会无声地破坏某些输入上的某些Python库。 这是一种创建难以调试的数据相关错误的方法。 寻找sys.setdefaultencoding
在site
,sitecustomize
,usercustomize
模块bytestring.encode('utf-8')
等效于bytestring.decode(sys.getdefaultencoding()).encode('utf-8')
。 不要在字节字符串上调用.encode(char_encoding)
,而应在Unicode字符串上使用它。
不要混合字节和Unicode字符串。 为简单起见,从仅适用于字节的API开始,即拒绝Unicode字符串,并确保您的代码不会偶然返回(适用于加密/解密方法)。
bytearray()
是字节的可变序列。
您几乎可以在任何可以使用bytes
( bytes
不可变序列)或(通常)使用任何支持对象的地方使用它。
当plaintext
是aesManaged.encrypt(plaintext)
,似乎aesManaged.encrypt(plaintext)
引发异常。
除了可以用基于.rjust
的代码替换的.append
调用之外,您的代码不会就地修改plaintext
。
直到AES
模块更新为支持bytearray
;
在这种情况下,请勿使用bytearray
或对其进行转换(尝试buffer(plaintext)
),然后再将其传递给不直接支持bytearray
AES
方法。
您的代码中没有不必要的类型转换。 删除它们。