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 不是秘密的),以便在解密期间重建和使用它。
我得到了以下函数 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 不是秘密的),以便在解密期间重建和使用它。