完整性检查导致AES解密在Python 3中失败

时间:2017-08-31 22:27:39

标签: python cryptography aes pycrypto


我目前正致力于使用pycryptodome及其AES密码的简单加密和解密工具。但是当将明文填充到块的大小时会出现问题 通常人们通过添加零来实现。但是当明文的最后一个字节也是零时会发生什么?我在网上找到了其他一些padunpad函数,但没有任何对我有用。

def _pad(self, s):
    rest = (AES.block_size - len(s)) % AES.block_size
    if not s:
        rest = AES.block_size
    return s + (rest * bytes([rest]))
def _unpad(self, s):
    return s[:-ord(s[len(s) - 1:])]

这是我看过很多次的另一个功能,但它不起作用。

加密

def encrypt(self, plaintext, check_integrity=False):
    if check_integrity is True:
        plaintext += struct.pack('L', zlib.crc32(plaintext))

    plaintext = self._pad(plaintext)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(self.key, AES.MODE_CBC, iv)
    ciphertext = iv + cipher.encrypt(plaintext)
    return ciphertext

我使用zlib.crc32()创建校验和,以便在解密后检查完整性。我只是将它附加在明文的末尾。也许它与此有关,我不确定。

解密

def decrypt(self, ciphertext, check_integrity=False):
    iv = ciphertext[:AES.block_size]
    cipher = AES.new(self.key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext[AES.block_size:])
    plaintext = self._unpad(plaintext)

    if check_integrity is True:
        crc, plaintext = (plaintext[-4:], plaintext[:-4])
        if not crc == struct.pack('L', zlib.crc32(plaintext)):
            return False
    return plaintext

ciphertext解密并取消填充后,最后4个字节应等于zlib.crc36(plaintext)。但这种情况并非如此。我的猜测是pad和/或unpad函数正在弄乱一些字节,导致完整性检查失败。

感谢您阅读!

2 个答案:

答案 0 :(得分:1)

我不打算纠正你的代码,而是告诉你,你的做法是错误的。这是一个糟糕的加密API的典型示例,它会给开发人员带来问题。加密API应该为您做填充,而不是要求您自己实现它。

正确的填充方法是使用像pkcs #7这样的标准。

执行完整性检查的正确方法是使用为您(例如GCM)执行操作的操作模式,或使用加密完整性检查(如HMAC)。完整性检查应该在密文上,并且应该在解密之前完成。否则,你打开自己填充oracle攻击。我愿意打赌你的代码容易遭受这种攻击。

答案 1 :(得分:0)

我终于找到了解决问题的方法:

如果不需要填充(rest = 0),_unpad()方法将获取明文然后将其取消。这就是为什么我补充说:

if rest == 0:
    rest = AES.block_size

_pad()方法 这样一切正常!