OpenSSL的AES-256每次加密生成不同的密文

3

以下是代码:

#include <QCoreApplication>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <iostream>

void en_de_crypt(int should_encrypt, FILE *ifp, FILE *ofp, unsigned char *ckey, unsigned char *ivec) {

    const unsigned BUFSIZE=4096;
    unsigned char *read_buf = (unsigned char*)malloc(BUFSIZE);
    unsigned char *cipher_buf;
    unsigned blocksize;
    int out_len;
    EVP_CIPHER_CTX ctx;

    for(int i=0; i<8;i++)
    {
        printf("%c\n",ckey[i]);
    }

     printf("\n\n");

    for(int i=0; i<8;i++)
    {
        printf("%c\n",ivec[i]);
    }

    EVP_CipherInit(&ctx, EVP_aes_256_cbc(), ckey, ivec, should_encrypt);
    blocksize = EVP_CIPHER_CTX_block_size(&ctx);
    cipher_buf = (unsigned char *)malloc(BUFSIZE + blocksize);

    while (1) {

        // Read in data in blocks until EOF. Update the ciphering with each read.

        int numRead = fread(read_buf, sizeof(unsigned char), BUFSIZE, ifp);
        EVP_CipherUpdate(&ctx, cipher_buf, &out_len, read_buf, numRead);
        fwrite(cipher_buf, sizeof(unsigned char), out_len, ofp);
        if (numRead < BUFSIZE) { // EOF
            break;
        }
    }

    // Now cipher the final block and write it out.

    EVP_CipherFinal(&ctx, cipher_buf, &out_len);
    fwrite(cipher_buf, sizeof(unsigned char), out_len, ofp);

    // Free memory

    free(cipher_buf);
    free(read_buf);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    std::string pathToFiles = "C:/Temp/testEnc/dqdokoleda.7z";
    unsigned char ckey[8];
    unsigned char ivec[8];

    ckey[0] = 'p';
    ckey[1] = 'a';
    ckey[2] = 's';
    ckey[3] = 's';
    ckey[4] = 'w';
    ckey[5] = 'o';
    ckey[6] = 'r';
    ckey[7] = 'd';

    ivec[0] = 'a';
    ivec[1] = 'c';
    ivec[2] = 'g';
    ivec[3] = 't';
    ivec[4] = 'i';
    ivec[5] = 'j';
    ivec[6] = 'o';
    ivec[7] = 'r';



    FILE *fIN, *fOUT;

    // First encrypt the file

    fIN = fopen((pathToFiles).c_str(), "rb"); //File to be encrypted; plain text
    fOUT = fopen((pathToFiles+".enc").c_str(), "wb"); //File to be written; cipher text

    en_de_crypt(1, fIN, fOUT, ckey, ivec);

    fclose(fIN);
    fclose(fOUT);

    //Decrypt file now

    fIN = fopen((pathToFiles+".enc").c_str(), "rb"); //File to be read; cipher text
    fOUT = fopen((pathToFiles+".decrypted").c_str(), "wb"); //File to be written; cipher text

    en_de_crypt(0, fIN, fOUT, ckey, ivec);

    fclose(fIN);
    fclose(fOUT);

    std::cout << "END" << std::endl;

    return a.exec();
}

问题在于当我注释解密部分时,每次.enc文件的内容都不同。然后,如果我尝试分别解密并注释加密代码的部分,文件将无法正确解密(7zip说它无法打开它)。如果我不注释任何代码部分,则加密和解密将成功完成。
我正在使用QT5.5.1,在文件加密和解密之间,我会进行清理、运行qmake、重新构建,并使用MSVC 12.0编译器以调试模式运行(这并不重要,但如果程序对您有效,请尝试在一个文件的加密和解密之间进行清理和构建,以查看它是否像应该那样工作)。

2
编写加密软件而不知道自己在做什么 - 不是一个好的迹象。 - usr1234567
您正在使用64位密钥和IV。应该分别为256位和128位吗?(注:http://security.stackexchange.com/questions/90848/encrypting-using-aes-256-can-i-use-256-bits-iv) - king_nak
1个回答

8
AES支持128位的块大小和128、192和256位的密钥大小。在许多模式中,特别是在CBC模式中,IV必须与块大小相同。问题在于你只使用了一个64位的IV和一个64位的密钥。你需要提供一个16字节的IV(适用于所有AES变体)和一个32字节的密钥(适用于AES-256)。
我猜EVP_CipherInit会尝试读取完整的IV和密钥,因此会读取未初始化的内存,每次程序执行时可能有任意数据。
请记住,IV必须为每个加密随机生成,以提供语义安全性。如果IV是静态的,并且您使用相同的密钥,则攻击者可以通过观察密文推断出您仅加密了相同的消息。随机化密文是一种安全属性。如果您想检查加密是否起作用,则需要尝试解密并将结果与原始明文进行比较。您可以将IV存储在密文文件格式的开头。
看起来您想使用密码作为加密密钥。不要将密码与密钥混淆。密码的熵非常小,而密钥通常看起来像噪声。如果您想使用密码,则需要从密码派生密钥。这通常使用带有随机盐和许多迭代(至少60,000次,可以是几百万次)的PBKDF2(在AES-256密钥上使用EVP_sha256PKCS5_PBKDF2_HMAC)。您可以将盐存储在与IV并排的密文文件格式开头。

你是百分之百正确的!对于像我这样不知道什么是PBKDF2的人来说:PBKDF2将伪随机函数(如加密哈希、密码或HMAC)应用于输入密码或密码短语以及盐值,并重复该过程多次以生成派生密钥,然后可以将其用作后续操作中的加密密钥。添加的计算工作使密码破解变得更加困难,这被称为密钥拉伸。 在控制台中很容易形成循环,但是当使用GUI时——是否有方法快速破解未使用PBKD2和SHA的密码? - nicksona
1
我假设用户应该输入密码,然后才能派生加密/解密密钥。在这种情况下,预计算是不可能的(或需要不安全的设置)。在这种情况下,您可以调整迭代次数,以使用户的密码安全,但用户还没有因密钥生成所需的时间而感到烦恼。密码管理器会执行此类操作,您可以设置特定的时间,例如一秒钟,软件将计算您的硬件上可能的迭代次数。我不知道OSSL中是否有内置方式。 - Artjom B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接