这链接到 EVP_DecryptFinal_ex Error on OpenSSL
我试图找出为什么 AES 解密不起作用,最后我找到了问题所在,现在正在寻找帮助我解决它的人:)
这是我测试过的代码(来自这里的各种帖子):
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
int AES_BLOCK_SIZE = 128;
int main(int argc, char **argv)
{
EVP_CIPHER_CTX en;
EVP_CIPHER_CTX de;
EVP_CIPHER_CTX_init(&en);
EVP_CIPHER_CTX_init(&de);
const EVP_CIPHER *cipher_type;
unsigned char *passkey, *passiv, *plaintxt;
char *plain;
char *plaintext;
unsigned char *ciphertext;
int olen, len;
int i =0;
unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f, 0 };
unsigned char key[]= { 0x2b, 0x7e, 0x15, 0x16,
0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88,
0x09, 0xcf, 0x4f, 0x3c , 0 };
unsigned char *input = "hi this is patrick immling\n'Doctor'.\n'Doctor' who ?\nPrecisely! 123910!§$$§% !%%$&$(/=))?=(#ü++Ü**<,.here we go sometimes it i s difficult but 187! 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y";
printf("AES ALGORITHM FOR 128 bit CBC MODE\n");
cipher_type = EVP_aes_128_cbc();
AES_BLOCK_SIZE = 128;
passkey = key;
passiv = iv;
plain = input;
printf("iv=");
for(i = 0; i < sizeof iv; i++){
printf("%02x", iv[i]);
//printf("key[%d]= %02x\n", i, key[i]);
}
printf("\n");
printf("key=");
for(i = 0; i < sizeof key; i++){
printf("%02x", key[i]);
//printf("key[%d]= %02x\n", i, key[i]);
}
printf("\n");
printf("Initializing AES ALGORITHM FOR CBC MODE..\n");
EVP_EncryptInit_ex(&en, cipher_type, NULL, passkey, passiv);
EVP_DecryptInit_ex(&de, cipher_type, NULL, passkey, passiv);
olen = len = strlen(input)+1;
printf("len value before aes_encrypt \"%d\"\n", len);
// max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes
int c_len = len + AES_BLOCK_SIZE - 1;
int f_len = 0;
ciphertext = (unsigned char *)malloc(c_len);
/* allows reusing of 'e' for multiple encryption cycles */
if(!EVP_EncryptInit_ex(&en, NULL, NULL, NULL, NULL)){
printf("ERROR in EVP_EncryptInit_ex \n");
return NULL;
}
if(!EVP_EncryptUpdate(&en, ciphertext, &c_len, plain, len)){
printf("ERROR in EVP_EncryptUpdate \n");
return NULL;
}
printf("len value after update \"%d\"\n", len);
// printf("size of ciphertext after update \"%d\"\n", sizeof(ciphertext));
printf("strlen value of ciphertext after update \"%d\"\n", strlen(ciphertext));
if(!EVP_EncryptFinal_ex(&en, ciphertext+c_len, &f_len)){
printf("ERROR in EVP_EncryptFinal_ex \n");
return NULL;
}
printf("len value after final \"%d\"\n", len);
printf("strlen value of ciphertext after final \"%d\"\n", strlen(ciphertext));
EVP_CIPHER_CTX_cleanup(&en);
len = c_len + f_len;
printf("len value after aes_encrypt \"%d\"\n", len);
//HERE IS THE PROBLEM: IF I USE len= strlen(ciphertext) I GET ERROR
//len = strlen(ciphertext);
printf("strlen value of ciphertext after aes_encrypt \"%d\"\n", len);
int p_len = len;
f_len = 0;
plaintext = (unsigned char *)malloc(p_len);
// memset(plaintext,0,sizeof(plaintext));
if(!EVP_DecryptInit_ex(&de, NULL, NULL, NULL, NULL)){
printf("ERROR in EVP_DecryptInit_ex \n");
return NULL;
}
EVP_CIPHER_CTX_set_padding(&de, 0);
if(!EVP_DecryptUpdate(&de, plaintext, &p_len, ciphertext, len)){
printf("ERROR in EVP_DecryptUpdate\n");
return NULL;
}
printf("len value after decrypt update \"%d\"\n", len);
if(!EVP_DecryptFinal_ex(&de, plaintext+p_len, &f_len)){
printf("ERROR in EVP_DecryptFinal_ex\n");
ERR_print_errors_fp(stderr);
return NULL;
}
EVP_CIPHER_CTX_cleanup(&de);
len = p_len + f_len;
printf("Decrypted value = %s\n", plaintext);
printf("len value after aes_decrypt \"%d\"\n", len);
if (strncmp(plaintext, input, olen))
printf("FAIL: enc/dec failed for \"%s\"\n", input);
else
printf("OK: enc/dec ok for \"%s\"\n", plaintext); // \"%s\"\n
printf("\n");
free(ciphertext);
free(plaintext);
return 0;
}
我不明白的地方:
我应该将什么作为 openSSL EVP 解密例程的“len”参数? 这个神奇的 len = c_len+ f_len 是什么?
如果我只得到带有 key 和 iv 的密码,我应该如何得到它?这应该总是可能的吧?我知道 strlen 是一个错误的参数,尤其是对于二进制,因为 EVP Decrypt 的密文输入是二进制的:所以我应该如何获取它?
我已经可以看到,如果我使用 len= strlen(ciphertext) 给我一个错误的答案,并且 sizeof 参数也不是那个,因为它返回 4。
stderr 清楚地显示了 EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,据我所知,正如你们都指出的那样,AES 数据应该是 16 字节的 block 。 那么我应该改变什么来喂长度呢?
最佳答案
首先,不要从 main() 返回 NULL。其次,我修复了它并对其进行了注释,希望您能明白长度变量的含义。我认为您缺少的关键点是您为 OpenSSL 函数提供了一个缓冲区,它可以在其中写入数据。像许多使用缓冲区的函数一样,你给它一个缓冲区大小,它返回给你它实际写入缓冲区的字节数。为什么?因为您必须知道您的缓冲区何时已满,或者如果您正在增量填充缓冲区,您必须知道将下一个数据 block 写入何处。
此外,我认为您应该阅读一些教程,了解如何使用二进制数据以及它与 C 风格字符串的区别。 OpenSSL EVP 函数使用二进制数据,这就是为什么您需要告诉每个函数您的数据有多少字节。
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
int main(int argc, char **argv)
{
EVP_CIPHER_CTX en;
EVP_CIPHER_CTX de;
EVP_CIPHER_CTX_init(&en);
EVP_CIPHER_CTX_init(&de);
const EVP_CIPHER *cipher_type;
unsigned char *passkey, *passiv, *plaintxt;
unsigned char *plaintext = NULL;
unsigned char *ciphertext = NULL;
int input_len = 0;
unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f };
unsigned char key[] = { 0x2b, 0x7e, 0x15, 0x16,
0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88,
0x09, 0xcf, 0x4f, 0x3c };
const char *string_to_encrypt = "hi this is patrick immling\n'Doctor'.\n'Doctor' who ?\nPrecisely! 123910!§$$§% !%%$&$(/=))?=(#ü++Ü**<,.here we go sometimes it i s difficult but 187! 1$5 78@2 14 .TӒ��틪�ձ1z.$�?�U���<y";
cipher_type = EVP_aes_128_cbc();
EVP_EncryptInit_ex(&en, cipher_type, NULL, key, iv);
EVP_DecryptInit_ex(&de, cipher_type, NULL, key, iv);
// The data we want to encrypt is a string. So we can just do a simple
// strlen to calculate its length. But the encrypted buffer is going to
// be padded with PKCS padding. In the worst case, our string is a
// multiple of the AES block size (16 bytes). In that case, the PKCS
// padding will be an additional 16 bytes after our data. So we could
// precisely calculate the buffer with this:
// int input_len = strlen(string_to_encrypt);
// malloc( input_len + 16 - (input_len % 16) );
// But why get fancy? Just add an extra AES block and have at most 16
// unused bytes at the end, and usually less than that.
static const int MAX_PADDING_LEN = 16;
// We add 1 because we're encrypting a string, which has a NULL terminator
// and want that NULL terminator to be present when we decrypt.
input_len = strlen(string_to_encrypt) + 1;
ciphertext = (unsigned char *) malloc(input_len + MAX_PADDING_LEN);
// This function works on binary data, not strings. So we cast our
// string to an unsigned char * and tell it that the length is the string
// length + 1 byte for the null terminator.
int bytes_written = 0;
int ciphertext_len = 0;
if(!EVP_EncryptUpdate(&en,
ciphertext, &bytes_written,
(unsigned char *) string_to_encrypt, input_len) ) {
printf("ERROR in EVP_EncryptUpdate \n");
return 1;
}
ciphertext_len += bytes_written;
// Right now the ciphertext buffer contains only the encrypted version
// of the input data up to the last full AES block. E.g., if your input
// size is 206, then ciphertext_len will be 192 because you have 14 bytes
// left to encrypt and those bytes can't fill a full AES block. But the
// encryptor has stored those bytes and is waiting either for more bytes
// or the call to EVP_EncryptFinal where it will add padding to make the
// encrypted data the same size as the AES block (i.e., 2 bytes of padding
// in the above example).
printf("Input len: %d, ciphertext_len: %d\n", input_len, ciphertext_len);
// EVP_EncryptFinal_ex writes the padding. The whole point of the
// bytes_written variable from EVP_EncryptUpdate is to tell us how much
// of the buffer is full so we know where we can write the padding.
// Note that we know our buffer is large enough so we're not bothering to
// keep track of the buffer size. We just keep track of how much data is
// in it.
if(!EVP_EncryptFinal_ex(&en,
ciphertext + bytes_written,
&bytes_written)){
printf("ERROR in EVP_EncryptFinal_ex \n");
return 1;
}
ciphertext_len += bytes_written;
EVP_CIPHER_CTX_cleanup(&en);
printf("Input len: %d, ciphertext_len: %d\n", input_len, ciphertext_len);
// We'll pretend we don't know the input length here. We do know that
// the ciphertext length is at most 16 bytes + the input length. So
// since the ciphertext is always greater than the input length, we can
// declare plaintext buffer size = ciphertext buffer size and know that
// there's no way we'll overflow our plaintext buffer. It will have at
// most 16 bytes of wasted space on the end, but that's ok.
plaintext = (unsigned char *) malloc(ciphertext_len);
// No! You're encrypting arbitrary data, so you should use padding. You
// don't use padding only if you know in advance that you're encrypting
// data whose length is a multiple of the block size. Like when running
// the AES known-answer tests.
// EVP_CIPHER_CTX_set_padding(&de, 0); /* no! */
int plaintext_len = 0;
if(!EVP_DecryptUpdate(&de,
plaintext, &bytes_written,
ciphertext, ciphertext_len)){
printf("ERROR in EVP_DecryptUpdate\n");
return 1;
}
plaintext_len += bytes_written;
// This function verifies the padding and then discards it. It will
// return an error if the padding isn't what it expects, which means that
// the data was malformed or you are decrypting it with the wrong key.
if(!EVP_DecryptFinal_ex(&de,
plaintext + bytes_written, &bytes_written)){
printf("ERROR in EVP_DecryptFinal_ex\n");
return 1;
}
plaintext_len += bytes_written;
EVP_CIPHER_CTX_cleanup(&de);
// We encrypted a string, so we know that we decrypted a string. So we
// can just print it. Note that we know our binary data is a string so
// we just cast it to a char *. We could just have easily declared it
// originally as a char * (I think I changed that from your original
// program, actually) and then cast it in the call to EVP_DecryptUpdate.
printf("input_len: %d, ciphertext_len: %d, plaintext_len: %d\n",
input_len, ciphertext_len, plaintext_len);
printf("Decrypted value = %s\n", plaintext);
if( strcmp(string_to_encrypt, (char *) plaintext) == 0 ) {
printf("Decrypted data matches input data.\n");
}
else {
printf("Decrypted data does not match input data.\n");
}
free(ciphertext);
free(plaintext);
return 0;
}
关于c - AES EVP_Decrypt的 "length"参数是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5727646/