在用OpenSSL解密EVP密码之后,8字节的明文总是不正确的

时间:2016-07-01 13:17:46

标签: c++11 openssl aes evp-cipher

我在使用EVP函数和对称密钥解密OpenSSL库中的某些数据时遇到问题。我使用openssl enc加密命令上的数据,然后用来自C ++代码解密。这很有效......大部分都是。

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

在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兆的全零文件,加密,解密,然后用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$ 

1 个答案:

答案 0 :(得分:4)

iv为简短,iv需要为整个块长度,AES为16字节。这就是字节8-15不正确的原因,它们对应于丢失的iv字节。

在加密的CBC mode中,iv与第一个纯文本块和解密后的xor&x或者用解密的输出进行编码。最好的猜测是,加密实现正在通过(缩短)iv之后拾取8字节的垃圾,因此每次运行时都会有不同的加密和解密。