我正在尝试解密先前使用c ++和openssl中的相同代码加密的文件。不幸的是,我遇到了段错误,无法找出确切的问题。
我能够加密图像文件,但是在尝试对其解密时会出现段错误。
似乎问题出在此功能上:
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 = 0, out_len = 0;
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);
}
我怀疑密钥长度不一样,问题出在这些方面:
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == AES_256_KEY_SIZE);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == AES_BLOCK_SIZE);
输入的密码在main()中使用(请参见下文):
if (EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), (unsigned char*)salt, (unsigned char*)pwd, sizeof(pwd), 1, params->key, params->iv) == 0) {
perror("Error: EVP_BytesToKey\n");
}
为了完整起见,这是一个加密文件的有效示例(不包括上面的函数(void file_encrypt_decrypt)):
#define _XOPEN_SOURCE
#include <crypt.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <cstdint>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/sha.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);
}
std::string sha256(const std::string str)
{
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, str.c_str(), str.size());
SHA256_Final(hash, &sha256);
std::stringstream ss;
for(int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
}
return ss.str();
}
char *take_pwd(char *salt)
{
int MAX_SIZE = 80;
char password[MAX_SIZE];
char *encrypted_password;
int i = 0;
char c;
printf("Enter password: ");
while ((c = getchar()) != '\n' && i < MAX_SIZE) {
password[i++] = c;
}
std::string salt_str = sha256(salt);
encrypted_password = crypt(password, const_cast<char *>(salt_str.c_str()));
encrypted_password[MAX_SIZE] = '\0';
return encrypted_password;
}
int main(int argc, char *argv[])
{
FILE *f_input, *f_enc, *f_dec;
/* Make sure user provides the input file */
if (argc != 3) {
printf("Usage: %s /path/to/file + 0 (decrypt) or 1 (encrypt)\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];
char salt[] = "$1$........";
char *pwd = take_pwd(salt);
if (EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), (unsigned char*)salt, (unsigned char*)pwd, sizeof(pwd), 1, params->key, params->iv) == 0) {
perror("Error: EVP_BytesToKey\n");
}
params->key = key;
params->iv = iv;
char *file_name = argv[1];
if (*argv[2] == '1') {
printf("\nEncrypting File\n");
/* 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 */
std::string name_of_file = file_name;
std::string encrypted = "_encrypted_";
std::size_t position_of_dot = name_of_file.find_last_of(".");
if (position_of_dot != std::string::npos) {
name_of_file.insert(position_of_dot, encrypted);
}
f_enc = fopen(name_of_file.c_str(), "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);
} else if (*argv[2] == '0') {
printf("\nDecrypting File\n");
/* Decrypt the file */
/* Indicate that we want to decrypt */
params->encrypt = 0;
/* Open the encrypted file for reading in binary ("rb" mode) */
printf("name_encrypted: %s\n", file_name);
f_input = fopen(file_name, "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;
}
我这样编译并执行了用于加密的代码(arg 1用于加密):
g++ -Wall -std=c++11 encrypt.cpp -o encrypt -lssl -lcrypto -lcrypt
./encrypt "~/path/to/image/file.jpg" 1
并用于解密(0):
./encrypt "~/path/to/image/file_encrypted_.jpg" 0
segfault似乎出现在void file_encrypt_decrypt函数的第一行:
EVP_CIPHER_block_size () from target:/usr/local/lib/libcrypto.so.1.1
答案 0 :(得分:1)
在解密过程中,这是您在GDB下的程序:
(gdb) r NEWS 0
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Enter password: test
Decrypting File
name_encrypted: NEWS
Program received signal SIGSEGV, Segmentation fault.
0x000000000040ee20 in EVP_CIPHER_block_size ()
(gdb) bt full
#0 0x000000000040ee20 in EVP_CIPHER_block_size ()
No symbol table info available.
#1 0x000000000040447d in file_encrypt_decrypt (params=0x67be70, ifp=0x685e50,
ofp=0x686080) at test.c:81
cipher_block_size = 0x0
in_buf = "\022\000;9Z\310\333T\354\251bH\361\253:\203Q\001\312\063ZL\225\003p2f\000\000\000\000\000\340\322\377\377\377\177", '\000' <repeats 18 times>, "\240Ec", '\000' <repeats 13 times>, "\347\026\376\367\377\177\000\000\001", '\000' <repeats 15 times>, "\360\226\243T9\035\215\066\bQ\243\367\377\177\000\000\320\321\377\377\377\177\000\000\236\203\376\367\377\177\000\000\021\327\377\377\377\177\000\000\000\000\000\000\000\000\000\000@ע\367\377\177\000\000GrZ\000\000\000\000\000\021\327\377\377\377\177\000\000\377\377\377\377\000\000\000\000\025\000\000\000\000\000\000\000p\001\001\000\000\000\000\000\320,j\000\000\000\000\000`"...
out_buf = <error reading variable out_buf (value requires 6840449 bytes, which is more than max-value-size)>
num_bytes_read = 0x0
out_len = 0xf6af024b
ctx = 0x686080
#2 0x0000000000404e1f in main (argc=0x3, argv=0x7fffffffd3c8) at test.c:257
f_input = 0x685e50
f_enc = 0x7fffffffd3e8
f_dec = 0x686080
然后,检出第1帧。只是OpenSSL库代码因错误的参数而崩溃:
(gdb) frame 1
#1 0x000000000040447d in file_encrypt_decrypt (params=0x67be70, ifp=0x685e50,
ofp=0x686080) at test.c:81
81 int cipher_block_size = EVP_CIPHER_block_size(params->cipher_type);
(gdb) p params
$1 = (cipher_params_t *) 0x67be70
(gdb) p params->cipher_type
$2 = (const EVP_CIPHER *) 0x0
您的密码对象为NULL。您需要创建一个密码对象并将其分配给params
。或者您需要向EVP_CIPHER_block_size
提供一个真实的对象。
这是您在加密期间正在做的事情:
190 if (*argv[2] == '1') {
(gdb)
192 printf("\nEncrypting File\n");
(gdb)
Encrypting File
195 params->encrypt = 1;
(gdb)
198 params->cipher_type = EVP_aes_256_cbc();
我猜想解密丢失了对params->cipher_type = EVP_aes_256_cbc();
的呼叫。
您应该将其从if/else
块中取出,以便加密和解密都可以执行它。
/* Set the cipher type you want for encryption-decryption */
params->cipher_type = EVP_aes_256_cbc();