第二次解密时AES解密失败

AES decryption fails when decrypting a second time

我有这个可逆编码的实现:

# coding=utf-8

from Crypto.Cipher import AES
from Crypto import Random
import uuid
import unittest
import random


key = r'Sixteen byte key'  # Keep this real secret
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CFB, iv)


def encode(role, plaintext):
    '''Encode the message, prefix with the role specifier'''
    msg = iv + cipher.encrypt(plaintext)
    msg = msg.encode('hex')
    msg = role + '-' + msg
    return msg


def decode(msg):
    '''Decode message, return role and plaintext'''
    role, msg = msg.split('-', 1)
    plaintext = cipher.decrypt(msg.decode('hex'))[len(iv):]
    return role, plaintext


class TestMe(unittest.TestCase):

    def test_whole(self):
        ROLES = ['sales', 'vendor', 'designer']
        for _ in xrange(100):
            role = random.choice(ROLES)
            txt = uuid.uuid4().hex
            msg = encode(role, txt)
            drole, dtxt = decode(msg)
            self.assertEqual(role, drole)
            self.assertEqual(txt, dtxt)
            print 'ok'


if __name__ == '__main__':
    unittest.main()

但这是失败的,总是在第二轮测试中。我在做一些明显错误的事情,但我不知道是什么。

备注

您需要:

pip install pycrypto

到运行那个代码

代码失败:

» python test.py 
ok
F
======================================================================
FAIL: test_whole (__main__.TestMe)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 40, in test_whole
    self.assertEqual(txt, dtxt)
AssertionError: 'b2e7894dd6254b259ae06350f199e6a2' != '\xa7\xcd\t\xde~\x15\xce\x9d\xcfU\x8f\xb2\xfa\x08\x98\x1c9ae06350f199e6a2'

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

错误消息提供了有关正在发生的事情的重要线索。如您所见,解密消息的前 16 个字节不同,但接下来的 16 个字节相同。当密钥正确但 IV 不正确时会发生这种情况。

问题似乎是 pyCrypto 没有在 encryption/decryption 之后重置密码状态,而 IV 是其他值。

无论哪种方式,您都不应该设置一次 IV 并多次重复使用它。 IV是为了提供密文的随机化,使得观察密文的攻击者无法确定加密的明文是否重复。

将 AES 对象创建移到函数中,解决了这个问题:

key = r'Sixteen byte key'  # Keep this real secret

def encode(role, plaintext):
    '''Encode the message, prefix with the role specifier'''
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CFB, iv)
    msg = iv + cipher.encrypt(plaintext)
    msg = msg.encode('hex')
    msg = role + '-' + msg
    return msg


def decode(msg):
    '''Decode message, return role and plaintext'''
    role, msg = msg.split('-', 1)
    msg = msg.decode('hex')
    iv = msg[:AES.block_size]
    cipher = AES.new(key, AES.MODE_CFB, iv)
    plaintext = cipher.decrypt(msg[AES.block_size:])
    return role, plaintext

您应该查看 pyCrypto 的 2.7-alpha 版本,其中包括经过身份验证的模式,例如 GCM、EAX、SIV。密文身份验证很重要,因为可能会在您的系统中使用填充 oracle 攻击来解密任何密文。