如何将AES模式从一种迁移到另一种?

时间:2019-02-14 06:16:24

标签: python encryption cryptography aes

我有2个功能:由AES实现的加密/解密

from cryptography.hazmat.backends import default_backend                                                      
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

DEFAULT_MODE = modes.ECB()

def encrypt(data, key):
    encryptor = Cipher(algorithms.AES(key), DEFAULT_MODE, backend=default_backend()).encryptor()
    return encryptor.update(data) + encryptor.finalize()    

def decrypt(data, key):
    if len(data) == 0 or len(data)%16 != 0:
        raise ValueError
    decryptor = Cipher(algorithms.AES(key), DEFAULT_MODE, backend=default_backend()).decryptor()
    return decryptor.update(data)

可以在下面执行代码:

>>> encrypt(b'a'*16, b'I_got_one_key_in_32bytes_length.')
'\xab\x07\x9d\xa0\xf0\xa0g\x9ae\xd9\x10\x9e\xea2\xb4\x17'
>>> decrypt(b'\xab\x07\x9d\xa0\xf0\xa0g\x9ae\xd9\x10\x9e\xea2\xb4\x17', b'I_got_one_key_in_32bytes_length.')
'aaaaaaaaaaaaaaaa'

GCM说,这里的任务是change the mode从欧洲央行到其他国家。

DEFAULT_MODE = modes.GCM('iv')

该代码仍可以在下面执行:

>>> encrypt(b'a'*16, b'I_got_one_key_in_32bytes_length.')
'Y5y\xbe\xeeK\xb9\x10\xcdf\x99\xa6\x1d\xf2\xa0\x1e'
>>> decrypt(b'Y5y\xbe\xeeK\xb9\x10\xcdf\x99\xa6\x1d\xf2\xa0\x1e', b'I_got_one_key_in_32bytes_length.')
'aaaaaaaaaaaaaaaa'

但是,由于这些加密/解密功能已经广泛使用了一段时间,因此出现了一个问题:如何在不影响原始加密数据的情况下迁移到新模式?


PS:我的草案思想是如下逻辑:如果可能,以GCM模式解密数据,否则以ECB模式解密;并在以后以GCM模式对其全部加密。但是AFAIK,这个想法行不通,因为我没有办法提出错误。 通过这种方法,有可能知道加密数据的AES模式吗?

def decrypt(data, key):
    try:
        # decrypt data by key in GCM
    except GCMDecryptFailedError:
        # decrypt data by key in ECB
    return decrypted_data

1 个答案:

答案 0 :(得分:1)

此处的错误是直接使用加密算法,而没有指定特定的协议及其附带的协议版本号。幸运的是,有很多方法可以解决这个问题,并指定一个特定的协议

密码学所采用的技巧之一是不可能仅仅因为生成特定值的机会每减少一位就随机生成特定内容。因此,您可以做的是在密文前面加上例如一个128/16字节的位值。对于每条消息,在密文开始时生成此值的几率是2的乘方,即128的幂(如果消息本身不是特定密钥的随机消息,则更少)。换句话说,这种机会低至猜测出AES-128密钥的可能性。我们称这样的机会“微不足道”。当然,此技巧取决于使用随机密钥的ECB加密输出也要随机。

但是,将来您可能希望包含一个或多个字节作为协议版本指示器。这样您就可以发送字节值01作为新版本,然后是16字节的 magic值,后跟GCM的随机随机数,密文和GCM身份验证标签(如果尚未包含在GCM密文)。一旦摆脱了协议的ECB版本(消息中未显示版本00),就可以摆脱魔术的困扰,将消息协议头中的16个字节重新用于协议2或更高。

如果您想产生一个很好的魔术,则可以使用任何类型的16字节字符串,以ASCII表示"Protocol 1, GCM:"(不带引号)。如果要使用更大的字符串,也可以使用哈希的最左边的128位。

所以最初,您的逻辑将是伪代码:

versionByte = message[0]
if message.length >= 17 && versionByte == 01h then
    magic = message[1- 16]
    if magic == "Protocol 1, GCM:" then
        gcmDecrypt(message, 17)
    else
        ecbDecrypt(message, 0)
// --- include other versions here ---
else
    ecbDecrypt(message, 0)

当然,这仍然是一个非常基本的协议。但是至少您以后可以更改它。您可能希望查看更完整的协议规范,例如Fernet,CMS或-当然-TLS,而不仅仅是AES-GCM。

无论您做什么:在单独的文档中写下协议,并从代码中引用它。您可以在源代码中引用您的简单协议,以方便查找。