我将详细说明@gertvdijk 解释为什么密码的行为方式与原始问题中的方式相同(我的编辑被拒绝),但也指出设置计数器以返回静态值是一个主要缺陷并显示如何正确设置它。
为新操作重置计数器
这样做的原因与您在问题中描述的一样,因为您的纯文本(4 字节/32 位)是 CTR 密码输出用于加密的密钥流块大小(16 字节/128 位)的四倍.
因为您一遍又一遍地使用相同的固定值而不是实际的计数器,所以密码会不断输出相同的 16 字节密钥流块。您可以通过重复加密 16 个空字节来观察这一点:
>>> crypto.encrypt('\x00'*16)
'?\\-\xdc\x16`\x05p\x0f\xa7\xca\x82\xdbE\x7f/'
>>> crypto.encrypt('\x00'*16)
'?\\-\xdc\x16`\x05p\x0f\xa7\xca\x82\xdbE\x7f/'
在执行解密之前,您也不会重置密码的状态,因此 4 字节的密文将根据来自第一个输出流块的接下来的 4 字节 XOR 密钥进行解密。这也可以通过加密和解密空字节来观察:
>>> crypto.encrypt('\x00' * 4)
'?\\-\xdc'
>>> crypto.decrypt('\x00' * 4)
'\x16`\x05p'
如果这以您想要的方式工作,那么这两个操作的结果应该是相同的。相反,您可以在第一个结果中看到 16 字节块的前四个字节,在第二个结果中看到后四个字节。
通过对 4 字节值执行四次操作(总共 16 字节)用完 16 字节的 XOR 键块后,将生成一个新的 XOR 键块。每个 XOR 密钥块的前四个字节(以及所有其他字节)都是相同的,所以这次调用解密时,它会返回明文。
这真的很糟糕!您不应该以这种方式使用 AES-CTR - 它等同于使用 16 字节重复密钥的简单 XOR 加密,很容易被破解。
解决方案
在对新数据流执行操作(或对其进行其他操作)之前,您必须重置密码的状态,因为原始实例将不再处于正确的初始状态。您的问题将通过crypto
为解密实例化一个新对象以及重置计数器和密钥流位置来解决。
您还需要使用适当的计数器功能,将随机数与每次生成新的密钥流块时增加的计数器值结合起来。PyCrypto 有一个 Counter 类可以为你做这件事。
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto import Random
# Set up the counter with a nonce.
# 64 bit nonce + 64 bit counter = 128 bit output
nonce = Random.get_random_bytes(8)
countf = Counter.new(64, nonce)
key = Random.get_random_bytes(32) # 256 bits key
# Instantiate a crypto object first for encryption
encrypto = AES.new(key, AES.MODE_CTR, counter=countf)
encrypted = encrypto.encrypt("asdk")
# Reset counter and instantiate a new crypto object for decryption
countf = Counter.new(64, nonce)
decrypto = AES.new(key, AES.MODE_CTR, counter=countf)
print decrypto.decrypt(encrypted) # prints "asdk"