我有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说,这里的任务是从欧洲央行到其他国家。

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

最佳答案

这里的错误是直接使用加密算法,而没有指定特定的协议及其附带的协议版本号。幸运的是,有很多方法可以摆脱这种情况,并指定特定的协议。

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

但是,将来您可能希望包含一个或多个字节作为协议版本指示符。这样您就可以发送字节值01作为新版本,然后是16字节魔术值,然后是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。

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

关于python - 如何将AES模式从一种迁移到另一种?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54684260/

10-13 09:43