我目前正致力于使用pycryptodome
及其AES密码的简单加密和解密工具。但是当将明文填充到块的大小时会出现问题
通常人们通过添加零来实现。但是当明文的最后一个字节也是零时会发生什么?我在网上找到了其他一些pad
和unpad
函数,但没有任何对我有用。
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
函数正在弄乱一些字节,导致完整性检查失败。
感谢您阅读!
答案 0 :(得分:1)
我不打算纠正你的代码,而是告诉你,你的做法是错误的。这是一个糟糕的加密API的典型示例,它会给开发人员带来问题。加密API应该为您做填充,而不是要求您自己实现它。
正确的填充方法是使用像pkcs #7这样的标准。
执行完整性检查的正确方法是使用为您(例如GCM)执行操作的操作模式,或使用加密完整性检查(如HMAC)。完整性检查应该在密文上,并且应该在解密之前完成。否则,你打开自己填充oracle攻击。我愿意打赌你的代码容易遭受这种攻击。
答案 1 :(得分:0)
我终于找到了解决问题的方法:
如果不需要填充(rest = 0
),_unpad()
方法将获取明文然后将其取消。这就是为什么我补充说:
if rest == 0:
rest = AES.block_size
到_pad()
方法
这样一切正常!