EVP_EncryptUpdate丢失4个字节

时间:2019-04-10 19:48:43

标签: openssl aes c++17

我想使用OpenSSL AES-256-cbc加密以100字节为块对输入数据进行加密。但是,EVP_EncryptUpdate仅加密96个字节(参数tmp = 96)。下一次对EVP_EncryptUpdate的调用将加密其余数据(除了丢失的那四个数据)。

解密很好,除了那4个丢失的字节。

如果我只调用一次EVP_EncryptUpdate,然后一次传递所有数据,一切都很好。

如果我仅将100个字节传递给EVP_EncryptUpdate,我不明白为什么这4个字节会丢失。

std::string data = "A12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123hgfedcba";

unsigned char key[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
                        0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
unsigned char iv[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };

int  i = 0, tmp = 0, ol = 0;

int res = EVP_EncryptInit(&ctx, EVP_aes_256_ecb(), key, iv);   

std::unique_ptr<unsigned char[]> ret(new unsigned char[dataLen+EVP_CIPHER_CTX_block_size(&ctx)]);

for (i = 0; i < dataLen / 100; i++)
{  
   EVP_EncryptUpdate(&ctx,
   &ret[ol], &tmp, &data[ol], 100); 
   ol += tmp;
}


if (dataLen % 100)
{  
    EVP_EncryptUpdate(&ctx, &ret[ol], &tmp, &data[ol], dataLen % 100);
    ol += tmp;
}

EVP_EncryptFinal(&ctx, &ret[ol], &tmp);

1 个答案:

答案 0 :(得分:0)

EVP_EncryptUpdate之后必须是EVP_EncryptFinal。 CBC模式只能加密/解密完整的块。 lib不知道对EVP_EncryptUpdate的最后一次调用不会再接一个,因此EVP_EncryptFinal 必须用于表示要加密的纯文本消息的结尾/解密。

EVP_EncryptUpdate将尝试加密尽可能多的块,并将其余块留在内部缓冲区中。在EVP_EncryptFinal期间,将与PKCS#7兼容的填充添加到缓冲区中的数据中,并对最后一个块进行加密,并返回密文的最后一部分。请注意,即使内部缓冲区为空,也会始终应用填充。

总而言之,当您调用EVP_EncryptFinal或使用更新添加足够的数据以创建完整的纯文本块进行加密时,将填充并加密最后四个字节。如果您不调用EVP_EncryptFinal,则不会填充密文,并且您的解密将失败-如果密文的最后一部分可以被视为有效的填充-它将剥离末尾有一些字节,然后返回剩余的内容。同样,在使用更新程序时,您必须完成。

如果您不喜欢这种行为,则可以考虑CTR模式-至少从理论上讲是流模式,可以直接返回加密字节,而无需填充。这称为分组密码模式的“在线”属性。有时,实现仍会缓冲纯文本,直到出现完整的块为止,因此请注意。


编码说明:

  • 100并非块大小(16字节)的倍数,因此显然只剩下4个字节。

  • 对于CBC模式,CBC需要唯一且不可预测的密钥/ IV,而CTR需要唯一的随机数。

您最好研究这些算法,否则您将无法创建安全代码。为了传输安全,您将需要使用经过身份验证的密码。