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

问题描述 投票:2回答:1

我有两个功能: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
python encryption cryptography aes
1个回答
1
投票

这里的错误是直接使用加密算法,而不指定特定协议,以及随附的协议版本号。幸运的是,有一些方法可以解决这个问题,并指定一个特定的协议。

密码学采用的技巧之一是不可能随机生成特定内容,因为生成特定值的机会每个位都会减少。所以你可以做的是在你的密文前加上例如: 128/16字节位值。在密文开头生成此值的机会是每个消息的功率为2(如果消息对于特定密钥本身不是随机的,则更少)。换句话说,机会与猜测AES-128密钥一样低;我们称这样的机会“可以忽略不计”。这个技巧当然取决于使用随机密钥进行ECB加密的输出也是随机的。

但是,对于将来,您可能希望包含一个或多个字节作为协议版本指示符。所以你可以发送,例如字节值01作为新版本,然后是16字节魔术值,后跟GCM的随机nonce,密文和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。

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

© www.soinside.com 2019 - 2024. All rights reserved.