PyCrypto - 初始化向量是如何工作的?

14 投票
2 回答
23330 浏览
提问于 2025-04-17 14:52

我正在尝试理解PyCrypto是如何工作的,以便在一个项目中使用,但我对初始化向量(IV)的重要性还不是很明白。我发现即使在解码字符串时使用了错误的IV,我仍然能得到消息,除了前16个字节(块大小)之外的内容。我是用错了,还是没理解什么呢?

这里有一段示例代码来演示:

import Crypto
import Crypto.Random
from Crypto.Cipher import AES

def pad_data(data):
    if len(data) % 16 == 0:
        return data
    databytes = bytearray(data)
    padding_required = 15 - (len(databytes) % 16)
    databytes.extend(b'\x80')
    databytes.extend(b'\x00' * padding_required)
    return bytes(databytes)

def unpad_data(data):
    if not data:
        return data

    data = data.rstrip(b'\x00')
    if data[-1] == 128: # b'\x80'[0]:
        return data[:-1]
    else:
        return data


def generate_aes_key():
    rnd = Crypto.Random.OSRNG.posix.new().read(AES.block_size)
    return rnd

def encrypt(key, iv, data):
    aes = AES.new(key, AES.MODE_CBC, iv)
    data = pad_data(data)
    return aes.encrypt(data)

def decrypt(key, iv, data):
    aes = AES.new(key, AES.MODE_CBC, iv)
    data = aes.decrypt(data)
    return unpad_data(data)

def test_crypto ():
    key = generate_aes_key()
    iv = generate_aes_key() # get some random value for IV
    msg = b"This is some super secret message.  Please don't tell anyone about it or I'll have to shoot you."
    code = encrypt(key, iv, msg)

    iv = generate_aes_key() # change the IV to something random

    decoded = decrypt(key, iv, code)

    print(decoded)

if __name__ == '__main__':
    test_crypto()

我使用的是Python 3.3。

输出结果在每次执行时会有所不同,但我得到的结果大概是这样的:b"1^,Kp}Vl\x85\x8426M\xd2b\x1aer secret message. Please don't tell anyone about it or I'll have to shoot you."

2 个回答

3

PyCrypto的开发者从NIST获取了AES CBC模式的规范:

AES Mode_CBC -> 参考了NIST 800-38a(加密模式操作的建议)

在这份文档的第8页中:

5.3 初始化向量

CBC、CFB和OFB模式的加密过程除了明文外,还需要一个叫做初始化向量(IV)的数据块,通常用IV表示。IV在加密消息的初始步骤和相应的解密步骤中都会用到。

IV不需要保密;但是,对于CBC和CFB模式来说,每次加密过程中的IV必须是不可预测的。而对于OFB模式,每次加密过程都必须使用唯一的IV。关于IV的生成可以在附录C中找到。


需要记住的一点是,每次发送消息时都需要使用随机的IV,这样可以给消息增加一个“盐”,使得每条消息都是独一无二的;即使这个“盐”是公开的,只要AES加密密钥是未知的,也不会影响加密的安全性。如果你不使用随机的IV,比如每条消息都用相同的16个字节,那么如果你重复发送相同的内容,消息在传输过程中就会看起来一样,这样就可能会受到频率攻击或重放攻击。

这是一个随机IV和静态IV结果的测试:

def test_crypto ():
    print("Same IVs same key:")
    key = generate_aes_key()
    iv = b"1234567890123456"
    msg = b"This is some super secret message.  Please don't tell anyone about it or I'll have to shoot you."
    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

    print("Different IVs same key:")
    iv = generate_aes_key()
    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

    iv = generate_aes_key()
    code = encrypt(key, iv, msg)
    print(code.encode('hex'))
    decoded = decrypt(key, iv, code)
    print(decoded)

希望这对你有帮助!

21

你看到的这种行为是特定于CBC模式的。在CBC模式下,解密的过程可以这样理解(来自维基百科):

CBC解密

你可以看到,初始化向量(IV)只影响前16个字节的明文。如果在传输过程中IV被损坏,CBC仍然可以正确解密所有块,但第一个块会出错。在CBC模式中,IV的作用是让你即使使用相同的密钥加密相同的消息,每次得到的密文也会完全不同(尽管消息的长度可能会泄露一些信息)。

其他模式就没有这么宽容了。如果IV出错,整个消息在解密时都会变得混乱。以CTR模式为例,其中的nonce几乎和IV有相同的意思:

CTR模式

撰写回答