这与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 Decrypt例程的“len”参数? 什么是这个魔法len = c_len + f_len?
如果我只使用密钥和iv给出密码,我应该怎么做?这应该总是可能的吗?我知道strlen是一个不好的参数,特别是对于二进制文件,因为EVP Decrypt的密文输入是二进制的:所以我该怎么做呢?
我已经可以看到,如果我使用len = strlen(密文)给我一个错误的答案,而sizeof参数也不是那个,因为它返回4。
stderr清楚地显示了EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH以及我所理解的所有内容都指出AES数据应该是16个字节的块。 那么我应该改变什么来喂长度呢?
答案 0 :(得分:21)
首先,不要从main()返回NULL。其次,我修复了它并对其进行了注释,希望你能看到长度变量的含义。我认为你缺少的关键点是你给OpenSSL函数一个可以写入数据的缓冲区。像许多带缓冲区的函数一样,你给它一个缓冲区大小,它会返回它实际写入缓冲区的字节数。为什么?因为您必须知道缓冲区何时已满,或者如果您正在逐步填充缓冲区,则必须知道在哪里写入下一个数据块。
另外,我认为您应该阅读一些有关如何使用二进制数据以及它与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;
}