C++11 使用OpenSSL解密EVP密码后,8字节的明文总是不正确的

C++11 使用OpenSSL解密EVP密码后,8字节的明文总是不正确的,c++11,openssl,aes,evp-cipher,C++11,Openssl,Aes,Evp Cipher,使用EVP函数和对称密钥,我在使用OpenSSL库解密某些数据时遇到问题。在命令上加密数据,如使用 OpenSSL Enc,然后用C++C++代码解密。这很有效。。。大部分 无论我使用什么数据,在执行解密后,明文中第二个8字节的块是不正确的(字节8到15),但文件的其余部分是正确的。我甚至用一个130兆的文件完成了这项工作,所有130兆都是完全正确的,并且在文件中处于正确的位置,除了那些字节 这发生在OutARM target上,以及在Ubuntu 12.04(不同的库、不同的工具链)上构建时

使用EVP函数和对称密钥,我在使用OpenSSL库解密某些数据时遇到问题。在命令上加密数据,如使用<代码> OpenSSL Enc<代码>,然后用C++C++代码解密。这很有效。。。大部分

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

这发生在OutARM target上,以及在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;
}

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


在on encryption中,iv与第一个纯文本块异或,在decryption中与解密后的输出异或。最好的猜测是,加密实现正在接收刚刚超过(简称)iv的8字节垃圾,因此每次运行时,加密和解密都会有所不同。

哎哟!您甚至知道下次调用
EVP\u CIPHER\u CTX\u block\u size(CTX)
来获取块大小:),让代码自己调试:
ASSERT(EVP\u CIPHER\u CTX\u block\u size(CTX)=COUNTOF(iv))。您甚至可以对密钥执行类似的操作。@jww好吧,API文档对此非常清楚。我希望其他事情也能说清楚:)谢谢。就是这样!谢谢顺便问一下,这只会对这8个字节产生什么影响?我希望算法从此点向前“关闭”,或者可能每秒8字节块出错,等等。在加密期间,第一个块与iv异或,下一个块与第一个块的加密数据异或,iv仅用于第一个块。在解密过程中,第一个块被解密并与iv异或,前8个字节是正确的,第二个是垃圾,因此输出的第二个8个字节是垃圾。第二个块被解密,并与前面的加密数据块异或,因此它是正确的。通常,CBC模式将从损坏的块中恢复。看。
~/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$