openssl EVP_CipherFinal_ex 失败

openssl EVP_CipherFinal_ex failed

我得到了以下函数 file_encrypt_decrypt,用于使用 AES256 CBC from here 加密和解密文件。
如果我从同一个程序中同时进行加密和解密,(最后给出的主要功能)加密和解密工作正常。尽管同时调用了相同的函数并再次启动了 ctx。

如果我评论加密部分,传递上面创建的 encrypted_file,解密失败并出现错误:
错误:EVP_CipherFinal_ex 失败。 OpenSSL 错误:error:06065064:lib(6):func(101):reason(100)
[[有意义]] OpenSSL 错误:error:06065064:digital 信封 routines:EVP_DecryptFinal_ex:bad 解密

有些地方有人在谈论一些填充长度问题。但我无法正确理解。
另外,如果在同一个程序中加密但单独加密失败,那么相同的功能如何正常工作?

一些指导将不胜感激。

PS:我尝试使用 EVP_DecryptInit_ex()EVP_DecryptUpdate()EVP_DecryptFinal_ex() 和类似的加密函数进行加密和解密,而不是使用通用函数,但是无效。

完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/rand.h>

#define ERR_EVP_CIPHER_INIT -1
#define ERR_EVP_CIPHER_UPDATE -2
#define ERR_EVP_CIPHER_FINAL -3
#define ERR_EVP_CTX_NEW -4

#define AES_256_KEY_SIZE 32
#define AES_BLOCK_SIZE 16
#define BUFSIZE 1024

typedef struct _cipher_params_t{
    unsigned char *key;
    unsigned char *iv;
    unsigned int encrypt;
    const EVP_CIPHER *cipher_type;
}cipher_params_t;

void cleanup(cipher_params_t *params, FILE *ifp, FILE *ofp, int rc){
    free(params);
    fclose(ifp);
    fclose(ofp);
    exit(rc);
}
void file_encrypt_decrypt(cipher_params_t *params, FILE *ifp, FILE *ofp){
    // Allow enough space in output buffer for additional block 
    int cipher_block_size = EVP_CIPHER_block_size(params->cipher_type);
    unsigned char in_buf[BUFSIZE], out_buf[BUFSIZE + cipher_block_size];

    int num_bytes_read, out_len;
    EVP_CIPHER_CTX *ctx;

    ctx = EVP_CIPHER_CTX_new();
    if(ctx == NULL){
        fprintf(stderr, "ERROR: EVP_CIPHER_CTX_new failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        cleanup(params, ifp, ofp, ERR_EVP_CTX_NEW);
    }

    // Don't set key or IV right away; we want to check lengths 
    if(!EVP_CipherInit_ex(ctx, params->cipher_type, NULL, NULL, NULL, params->encrypt)){
        fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        cleanup(params, ifp, ofp, ERR_EVP_CIPHER_INIT);
    }

    OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == AES_256_KEY_SIZE);
    OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == AES_BLOCK_SIZE);

    // Now we can set key and IV 
    if(!EVP_CipherInit_ex(ctx, NULL, NULL, params->key, params->iv, params->encrypt)){
        fprintf(stderr, "ERROR: EVP_CipherInit_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        EVP_CIPHER_CTX_cleanup(ctx);
        cleanup(params, ifp, ofp, ERR_EVP_CIPHER_INIT);
    }

    while(1){
        // Read in data in blocks until EOF. Update the ciphering with each read.
        num_bytes_read = fread(in_buf, sizeof(unsigned char), BUFSIZE, ifp);
        if (ferror(ifp)){
            fprintf(stderr, "ERROR: fread error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            cleanup(params, ifp, ofp, errno);
        }
        if(!EVP_CipherUpdate(ctx, out_buf, &out_len, in_buf, num_bytes_read)){
            fprintf(stderr, "ERROR: EVP_CipherUpdate failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
            EVP_CIPHER_CTX_cleanup(ctx);
            cleanup(params, ifp, ofp, ERR_EVP_CIPHER_UPDATE);
        }
        fwrite(out_buf, sizeof(unsigned char), out_len, ofp);
        if (ferror(ofp)) {
            fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
            EVP_CIPHER_CTX_cleanup(ctx);
            cleanup(params, ifp, ofp, errno);
        }
        if (num_bytes_read < BUFSIZE) {
            // Reached End of file 
            break;
        }
    }

    // Now cipher the final block and write it out to file 
    if(!EVP_CipherFinal_ex(ctx, out_buf, &out_len)){
        fprintf(stderr, "ERROR: EVP_CipherFinal_ex failed. OpenSSL error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        EVP_CIPHER_CTX_cleanup(ctx);
        cleanup(params, ifp, ofp, ERR_EVP_CIPHER_FINAL);
    }
    fwrite(out_buf, sizeof(unsigned char), out_len, ofp);
    if (ferror(ofp)) {
        fprintf(stderr, "ERROR: fwrite error: %s\n", strerror(errno));
        EVP_CIPHER_CTX_cleanup(ctx);
        cleanup(params, ifp, ofp, errno);
    }
    EVP_CIPHER_CTX_cleanup(ctx);
}


int main(int argc, char *argv[]) {
    FILE *f_input, *f_enc, *f_dec;

    // Make sure user provides the input file 
    if (argc != 2) {
        printf("Usage: %s /path/to/file\n", argv[0]);
        return -1;
    }

    cipher_params_t *params = (cipher_params_t *)malloc(sizeof(cipher_params_t));
    if (!params) {
        // Unable to allocate memory on heap
        fprintf(stderr, "ERROR: malloc error: %s\n", strerror(errno));
        return errno;
    }

    // Key to use for encrpytion and decryption 
    unsigned char key[AES_256_KEY_SIZE];

    // Initialization Vector 
    unsigned char iv[AES_BLOCK_SIZE];

    // Generate cryptographically strong pseudo-random bytes for key and IV 
    if (!RAND_bytes(key, sizeof(key)) || !RAND_bytes(iv, sizeof(iv))) {
        // OpenSSL reports a failure, act accordingly 
        fprintf(stderr, "ERROR: RAND_bytes error: %s\n", strerror(errno));
        return errno;
    }
    params->key = key;
    params->iv = iv;

    // Indicate that we want to encrypt 
    params->encrypt = 1;

    // Set the cipher type you want for encryption-decryption 
    params->cipher_type = EVP_aes_256_cbc();

    // Open the input file for reading in binary ("rb" mode) 
    f_input = fopen(argv[1], "rb");
    if (!f_input) {
        // Unable to open file for reading 
        fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
        return errno;
    }

    // Open and truncate file to zero length or create ciphertext file for writing 
    f_enc = fopen("encrypted_file", "wb");
    if (!f_enc) {
        // Unable to open file for writing 
        fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
        return errno;
    }

    // Encrypt the given file 
    file_encrypt_decrypt(params, f_input, f_enc);

    // Encryption done, close the file descriptors 
    fclose(f_input);
    fclose(f_enc);

    // Decrypt the file 
    // Indicate that we want to decrypt 
    params->encrypt = 0;

    // Open the encrypted file for reading in binary ("rb" mode) 
    f_input = fopen("encrypted_file", "rb");
    if (!f_input) {
        // Unable to open file for reading 
        fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
        return errno;
    }

    // Open and truncate file to zero length or create decrypted file for writing 
    f_dec = fopen("decrypted_file", "wb");
    if (!f_dec) {
        // Unable to open file for writing 
        fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno));
        return errno;
    }

    // Decrypt the given file 
    file_encrypt_decrypt(params, f_input, f_dec);

    // Close the open file descriptors 
    fclose(f_input);
    fclose(f_dec);

    // Free the memory allocated to our structure 
    free(params);

    return 0;
}

该代码生成一个新密钥和一个新 IV 每个 运行。因此,如果仅注释掉加密部分,则会生成两个 different 密钥/IV 对并用于加密和解密,从而导致观察到的错误消息。如果出于测试目的使用固定密钥/IV 对而不是每次新生成的对,代码将按预期工作。

一般来说,用于加密的密钥/IV对也必须用于解密。关于IV,实际上在加密过程中通常会生成一个random IV。使用后,只需将其添加到密文前面(因为 IV 不是秘密的),以便在解密期间重建和使用它。