使用OpenSSL解密EVP密码后,8个字节的明文总是错误的



我在使用EVP函数和对称密钥解密OpenSSL库的一些数据时遇到了麻烦。我像使用openssl enc一样在命令上加密数据,然后用c++代码解密。这是可行的……大部分.

无论我使用什么数据,在我执行解密之后,明文中的第二个8字节块是不正确的(字节8到15)-但文件的其余部分是正确的。我甚至在一个130 meg的文件中这样做了——所有130 meg都是完全正确的,并且在文件中的正确位置,除了那些字节。

这发生在我们的ARM目标上,当构建在Ubuntu 12.04上(不同的库,不同的工具链)。

下面是一个简短的完整的程序,它有这个问题。下面是一些演示它的终端输出。

#include <string>
#include <fstream>
#include <stdexcept>
#include <openssl/evp.h>
void decrypt_and_untar(const std::string& infile, const std::string& outfile)
{
    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};
    std::ifstream is(infile, std::ios_base::in | std::ios_base::binary);
    std::ofstream os(outfile, std::ios_base::out | std::ios_base::binary);
    auto ctx = EVP_CIPHER_CTX_new();
    if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key, iv))
    {
        const int BufferSize = 10240;
        unsigned char enc_buff[BufferSize];
        int bytes_in_enc_buff = 0;
        unsigned char dec_buff[BufferSize + EVP_CIPHER_CTX_block_size(ctx)];
        int bytes_in_dec_buff = 0;
        while (!is.eof())
        {
            is.read(reinterpret_cast<char*>(enc_buff), BufferSize);
            bytes_in_enc_buff = is.gcount();
            if (bytes_in_enc_buff > 0)
            {
                if (EVP_DecryptUpdate(ctx, dec_buff, &bytes_in_dec_buff, enc_buff, bytes_in_enc_buff))
                {
                    os.write(reinterpret_cast<char*>(dec_buff), bytes_in_dec_buff);
                    bytes_in_enc_buff = 0;
                    bytes_in_dec_buff = 0;
                }
                else
                    throw std::runtime_error("Failed DecryptUpdate.");
            }
        }
        if (EVP_DecryptFinal_ex(ctx, dec_buff, &bytes_in_dec_buff))
            os.write(reinterpret_cast<char*>(dec_buff), bytes_in_dec_buff);
        else
            throw std::runtime_error("Failed DecryptFinal.");
    }
    else
    {
        throw std::runtime_error("Failed DecryptInit.");
    }

    EVP_CIPHER_CTX_free(ctx);
}
int main(int argc, char* argv[])
{
    if (argc == 3)
        decrypt_and_untar(argv[1], argv[2]);
    return 0;
}

下面是实际问题的演示。我创建了一个1兆的文件,全是0,加密,解密,然后用od查看它。如果我多次执行此操作,那8个错误字节的值会随着运行而变化…

~/workspace/test_crypto/Debug$ dd if=/dev/zero of=plain.original bs=1024 count=1024
1024+0 records in
1024+0 records out
1048576 bytes (1.0 MB) copied, 0.00154437 s, 679 MB/s
~/workspace/test_crypto/Debug$ od -t x1 plain.original 
0000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
4000000
~/workspace/test_crypto/Debug$ /usr/bin/openssl enc -aes-256-cbc -salt -in plain.original -out encrypted -K 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF -iv 0011223344556677
~/workspace/test_crypto/Debug$ ./test_crypto encrypted plain
~/workspace/test_crypto/Debug$ od -t x1 plain
0000000 00 00 00 00 00 00 00 00 00 40 02 0d 18 93 b8 bf
0000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
4000000
~/workspace/test_crypto/Debug$ 

iv太短,iv需要是一个完整的块长度,AES为16字节。这就是为什么字节8-15是不正确的,它们对应于缺失的iv字节。

在CBC模式下,加密时iv与第一个明文块异位,解密时iv与解密后的输出异位。最好的猜测是,加密实现在(短)iv之后拾取8字节的垃圾,因此每次运行时都是不同的,无论是加密还是解密。

最新更新