C语言 用C中的openssl库进行简单的AES加密解密
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19889740/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Simple AES encryption decryption with openssl library in C
提问by Susitha Ravinda Senarath
I want to encrypt a struct containing few String and then decrypt it. I tried following code. The original code is found from the web and it was working perfectly. I change the input of it to a struct. following is the code.
我想加密一个包含几个字符串的结构,然后解密它。我尝试了以下代码。原始代码是从网上找到的,它运行良好。我将它的输入更改为结构。以下是代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
typedef struct ticket { /* test field */
int ticketId;
char username[20];
char date[20];
} USR_TICKET;
// a simple hex-print routine. could be modified to print 16 bytes-per-line
static void hex_print(const void* pv, size_t len)
{
const unsigned char * p = (const unsigned char*)pv;
if (NULL == pv)
printf("NULL");
else
{
size_t i = 0;
for (; i<len;++i)
printf("%02X ", *p++);
}
printf("\n");
}
// main entrypoint
int main(int argc, char **argv)
{
int keylength;
printf("Give a key length [only 128 or 192 or 256!]:\n");
scanf("%d", &keylength);
/* generate a key with a given length */
unsigned char aes_key[keylength/8];
memset(aes_key, 0, keylength/8);
if (!RAND_bytes(aes_key, keylength/8))
exit(-1);
/* input struct creation */
size_t inputslength = sizeof(USR_TICKET);
USR_TICKET ticket;
ticket.ticketId = 1;
time_t now = time(NULL);
strftime(ticket.date, 20, "%Y-%m-%d", localtime(&now));
strcpy(ticket.username, "ravinda");
printf("Username - %s\n", ticket.username);
printf("Ticket Id - %d\n", ticket.ticketId);
printf("Date - %s\n", ticket.date);
/* init vector */
unsigned char iv_enc[AES_BLOCK_SIZE], iv_dec[AES_BLOCK_SIZE];
RAND_bytes(iv_enc, AES_BLOCK_SIZE);
memcpy(iv_dec, iv_enc, AES_BLOCK_SIZE);
// buffers for encryption and decryption
const size_t encslength = ((inputslength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
unsigned char enc_out[encslength];
unsigned char dec_out[inputslength];
memset(enc_out, 0, sizeof(enc_out));
memset(dec_out, 0, sizeof(dec_out));
// so i can do with this aes-cbc-128 aes-cbc-192 aes-cbc-256
AES_KEY enc_key, dec_key;
AES_set_encrypt_key(aes_key, keylength, &enc_key);
AES_cbc_encrypt((unsigned char *)&ticket, enc_out, inputslength, &enc_key, iv_enc, AES_ENCRYPT);
AES_set_decrypt_key(aes_key, keylength, &dec_key);
AES_cbc_encrypt(enc_out, dec_out, encslength, &dec_key, iv_dec, AES_DECRYPT);
printf("original:\t");
hex_print((unsigned char *)&ticket, inputslength);
printf("encrypt:\t");
hex_print(enc_out, sizeof(enc_out));
printf("decrypt:\t");
hex_print(dec_out, sizeof(dec_out));
USR_TICKET * dyc = (USR_TICKET *)dec_out;
printf("Username - %s\n", dyc->username);
printf("Ticket Id - %d\n", dyc->ticketId);
printf("Date - %s\n", dyc->date);
return 0;
}
The problem is only first two members of the struct is decrypting correctly. After that data get currupted. What am I doing wrong here?
问题是只有结构的前两个成员正确解密。在该数据被破坏之后。我在这里做错了什么?
回答by Iridium
I would almostgo so far as to say this is a problem with OpenSSL. It seems that when the lengthparameter passed to AES_cbc_encryptis > AES_BLOCK_SIZEbut not an integral multiple thereof (i.e. length mod AES_BLOCK_SIZE != 0), then the last block is encrypted using the initialIV and not the previous block of ciphertext as should be the case for CBC mode.
我几乎要说这是 OpenSSL 的问题。似乎当length传递给的参数AES_cbc_encrypt是 >AES_BLOCK_SIZE但不是其整数倍(即length mod AES_BLOCK_SIZE != 0)时,最后一个块使用初始IV加密,而不是像 CBC 模式那样使用前一个密文块加密。
You can fix this in one of two ways:
您可以通过以下两种方式之一解决此问题:
Copy your struct to a buffer whose size is an integral multiple of AES_BLOCK_SIZE, or encrypt in two parts - the full blocks, followed by a single partial block. The latter has the advantage of avoiding additional memory usage and can be done as follows:
将您的结构复制到一个缓冲区,其大小是 的整数倍AES_BLOCK_SIZE,或加密两部分 - 完整块,后跟单个部分块。后者具有避免额外内存使用的优点,可以按如下方式完成:
size_t fullBlocks = inputslength - (inputslength % AES_BLOCK_SIZE);
size_t remainingBlock = inputslength - fullBlocks;
AES_cbc_encrypt((unsigned char *)&ticket, enc_out, fullBlocks, &enc_key, iv_enc, AES_ENCRYPT);
AES_cbc_encrypt((unsigned char *)&ticket + fullBlocks, enc_out + fullBlocks, remainingBlock, &enc_key, iv_enc, AES_ENCRYPT);
You should then be able to decrypt as you currently are without issue. It's worth noting however that you should declare dec_outas the same size as enc_out, because you're currently overrunning the dec_outbuffer when decrypting.
然后,您应该能够像目前一样解密,没有问题。但是值得注意的是,您应该声明dec_out为与 相同的大小enc_out,因为您当前dec_out在解密时超出了缓冲区。
Edit:
编辑:
I raised this as a bug in OpenSSL: https://rt.openssl.org/Ticket/Display.html?id=3182&user=guest&pass=guestand whilst there is some argument over whether this is actually a bug, or just (undocumented) undefined behavior, the general consensus is that the EVProutines should be used instead, rather than these low-level functions.
我将此作为 OpenSSL 中的一个错误提出:https: //rt.openssl.org/Ticket/Display.html?id =3182 &user =guest &pass =guest 虽然有一些争论这是否真的是一个错误,或者只是(未记录) 未定义的行为,普遍的共识是EVP应该使用例程,而不是这些低级函数。
回答by joe
The problem might be in the struct you are using, basically because of the struct padding and member sizes. Try to serialize your input and it must work. in a simple way, try to allocate one char buffer and copy your struct contents to the buffer one by one and try to encrypt that buffer and while decrypting also follow the same method. Post here what you are observing after that. We must be able to comment better on that.
问题可能出在您正在使用的结构中,主要是因为结构填充和成员大小。尝试序列化您的输入,它必须工作。以一种简单的方式,尝试分配一个字符缓冲区并将您的结构内容一一复制到缓冲区,然后尝试加密该缓冲区,同时解密也遵循相同的方法。在这里发布您之后观察到的内容。我们必须能够对此作出更好的评论。
回答by Chiara Hsieh
The original code has done the padding for you. The problem is that you passed the wrong plaintext length to AES function. You should pass encslength(padded length) to AES_cbc_encrypt. Just change this line
原始代码已为您完成填充。问题是您将错误的明文长度传递给 AES 函数。您应该将encslength(填充长度)传递给AES_cbc_encrypt. 只需改变这一行
AES_cbc_encrypt((unsigned char *)&ticket, enc_out, inputslength, &enc_key, iv_enc, AES_ENCRYPT);
to
到
AES_cbc_encrypt((unsigned char *)&ticket, enc_out, encslength, &enc_key, iv_enc, AES_ENCRYPT);
This should solve your problem.
这应该可以解决您的问题。
回答by Eric Tsui
At least I also think it is not a good practise to use structand sizeof(a_struct_type)here.
至少我也认为使用struct和sizeof(a_struct_type)这里不是一个好习惯。
The actual binary sequence of the struct USR_TICKETand the result of sizeofwill vary according to different padding and byte alignment implementation.
的实际二进制序列struct USR_TICKET和结果 sizeof将根据不同的填充和字节对齐实现而有所不同。
Here is the test result.
这是测试结果。
1) Firstly, I download the latest openssl1.0.2c and build it, and test the code here with this library on OS X 10.10.3, the code works fine.
1) 首先,我下载最新的openssl1.0.2c 并构建它,并在OS X 10.10.3 上用这个库测试这里的代码,代码工作正常。
2) And I tried to run the test for 5 times. Each time, even the originalplain text will changed due to padding of struct(on my OS, the last 4 bytes will be changed every time).
2) 我尝试运行测试 5 次。每次,即使是original纯文本也会由于填充而改变struct(在我的操作系统上,每次都会更改最后 4 个字节)。
3) So,the same input to the structproduced different plain text, which finally produced different cipher text. The user might be confused by the different cipher text output, when they try to encrypt the same input info(here raving, 1, 2015-6-25) with the same key and iv.
3) 所以,同样的输入struct产生了不同的plain text,最终产生了不同的密文。当用户尝试raving, 1, 2015-6-25使用相同的密钥和 iv加密相同的输入信息(此处)时,他们可能会对不同的密文输出感到困惑。
4) Furthermore, just change the structinternal definition, which will produce the same concern in description (3).
4)此外,只要改变struct内部定义,这将产生与描述(3)相同的关注。
typedef struct ticket { /* test field */
int ticketId;
char username[19]; // here try to change from 20 to 19
char date[20]; // here try to change from 20 to other size
} USR_TICKET;
PS. Some outputs result from above description(2),
附注。上面的描述(2)产生了一些输出,
Output1:
输出 1:
Give a key length [only 128 or 192 or 256!]:
128
Username - ravinda
Ticket Id - 1
Date - 2015-06-25
original: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 BB 3A 50
encrypt: BA 32 86 CC 71 55 2F 73 ED A1 C9 DE 00 32 1A 20 D9 A5 16 52 8A CD F0 F7 38 04 76 38 5A 47 35 3B A3 07 97 41 C4 C2 05 53 74 93 91 26 7E DE 40 47
decrypt: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 BB 3A 50
Username - ravinda
Ticket Id - 1
Date - 2015-06-25
Output2:
输出 2:
Give a key length [only 128 or 192 or 256!]:
128
Username - ravinda
Ticket Id - 1
Date - 2015-06-25
original: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 0B 10 5A
encrypt: BE 60 0F FC 17 A3 42 4A 95 7C 39 DB BF 2C BA 59 42 DC 0C AD B2 20 76 6A 04 E3 DE 11 3E D0 AF 88 A5 B9 D2 25 D4 AE F0 B7 82 9F 13 39 80 39 61 9D
decrypt: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 0B 10 5A
Username - ravinda
Ticket Id - 1
Date - 2015-06-25

