【问题标题】:openssl EVP_CipherFinal_ex failedopenssl EVP_CipherFinal_ex 失败
【发布时间】:2020-01-02 19:54:29
【问题描述】:

我得到了下面的函数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 envelope routines:EVP_DecryptFinal_ex:bad decrypt

人们在某处谈论一些填充长度问题。但我无法正确弄清楚。
如果在同一个程序中单独进行加密,那么相同的功能如何正常工作,但它会失败?

我们将不胜感激。

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;
}

【问题讨论】:

    标签: c openssl aes encryption-symmetric evp-cipher


    【解决方案1】:

    代码生成一个新密钥和一个新的 IV,each 运行。所以如果只注释掉加密部分,那么就会生成两个不同的密钥/IV对并用于加密和解密,从而导致观察到的错误信息。如果出于测试目的使用固定密钥/IV 对而不是每次新生成的对,则代码按预期工作。

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

    【讨论】:

    • 对不起那个常见的复制粘贴代码。我正在使用从静态 key_data 和盐生成的一对静态密钥和 IV 以及 EVP_BytesToKey()。但那是一个不同的问题。我使用strncpy 从字符串中复制静态数据,但使用的格式说明符为%ld。解决这个问题,解决了我的问题。但是我仍然对静态副本如何因启用或禁用解密而受到影响感到困惑。无论如何,这个答案类似于我的修复,所以接受它。
    猜你喜欢
    • 2013-02-24
    • 2015-03-13
    • 2020-12-20
    • 1970-01-01
    • 1970-01-01
    • 2020-10-14
    • 2021-01-30
    • 2017-09-18
    • 2019-04-22
    相关资源
    最近更新 更多