C语言 OpenSSL 上的 AES CTR 256 加密操作模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3141860/
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
AES CTR 256 Encryption Mode of operation on OpenSSL
提问by Bartzilla
Im new to OpenSSL, Can anybody give me a hint in how to initialize AES CTR mode from a C file. I know this is the method′s signature but I am having problems with the parameters, there′s not many documentation neither a clear example how to make a simple encryption. I would appreciate if somebody could exemplify a call to this method. Thanks in advance!
我是 OpenSSL 的新手,谁能给我一个关于如何从 C 文件初始化 AES CTR 模式的提示。我知道这是该方法的签名,但是我遇到了参数问题,文档不多,也没有明确的示例如何进行简单的加密。如果有人可以举例说明对此方法的调用,我将不胜感激。提前致谢!
void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,
const unsigned long length, const AES_KEY *key,
unsigned char ivec[AES_BLOCK_SIZE],
unsigned char ecount_buf[AES_BLOCK_SIZE],
unsigned int *num);
Hi Caf I really appreciate your quick answer it has been really useful, and defenetly the best example I have found on the web. I am trying to open a file with undetermined length, encrypt it and write another file with the ciphertext generated, then open the ciphered file and recover the plaintext. I need to use a file of a considerable amount of MB cause I would like to benchmark the performance of the CPU. However Im still having a problem while decrypting. Somehow when decrypting a considerable txt files (1504KB)it wont decrypt it complete, and I get half of it in plaintext and the other half still ciphered. I think this might be related to the size of the iv or the way I am calling the counter. Here is what I have so far:
嗨 Caf 我真的很感谢你的快速回答它真的很有用,而且绝对是我在网上找到的最好的例子。我试图打开一个不确定长度的文件,对其进行加密并使用生成的密文写入另一个文件,然后打开加密文件并恢复明文。我需要使用大量 MB 的文件,因为我想对 CPU 的性能进行基准测试。但是我在解密时仍然遇到问题。不知何故,当解密相当多的 txt 文件 (1504KB) 时,它不会解密完成,我得到了一半的明文,另一半仍然是加密的。我认为这可能与 iv 的大小或我调用计数器的方式有关。这是我到目前为止所拥有的:
#include <openssl/aes.h>
#include <stdio.h>
#include <string.h>
struct ctr_state {
unsigned char ivec[16];
unsigned int num;
unsigned char ecount[16];
};
FILE *fp;
FILE *rp;
FILE *op;
size_t count;
char * buffer;
AES_KEY key;
int bytes_read, bytes_written;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
unsigned char ckey[] = "thiskeyisverybad"; // It is 128bits though..
unsigned char iv[8] = {0};//This should be generated by RAND_Bytes I will take into consideration your previous post
struct ctr_state state;
int init_ctr(struct ctr_state *state, const unsigned char iv[8]){
state->num = 0;
memset(state->ecount, 0, 16);
memset(state->ivec + 8, 0, 8);
memcpy(state->ivec, iv, 8);
}
void encrypt(){
//Opening files where text plain text is read and ciphertext stored
fp=fopen("input.txt","a+b");
op=fopen("output.txt","w");
if (fp==NULL) {fputs ("File error",stderr); exit (1);}
if (op==NULL) {fputs ("File error",stderr); exit (1);}
//Initializing the encryption KEY
AES_set_encrypt_key(ckey, 128, &key);
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while (1) {
init_ctr(&state, iv); //Counter call
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, fp);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
bytes_written = fwrite(outdata, 1, bytes_read, op);
if (bytes_read < AES_BLOCK_SIZE)
break;
}
fclose (fp);
fclose (op);
free (buffer);
}
void decrypt(){
//Opening files where text cipher text is read and the plaintext recovered
rp=fopen("recovered.txt","w");
op=fopen("output.txt","a+b");
if (rp==NULL) {fputs ("File error",stderr); exit (1);}
if (op==NULL) {fputs ("File error",stderr); exit (1);}
//Initializing the encryption KEY
AES_set_encrypt_key(ckey, 128, &key);
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while (1) {
init_ctr(&state, iv);//Counter call
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, op);
AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);
bytes_written = fwrite(outdata, 1, bytes_read, rp);
if (bytes_read < AES_BLOCK_SIZE)
break;
}
fclose (rp);
fclose (op);
free (buffer);
}
int main(int argc, char *argv[]){
encrypt();
//decrypt();
system("PAUSE");
return 0;
}
Each encrypt and decrypt function are called in different runs so everything is initialized always with the same values. Thanks again for the hints you can provide me in advance & Regards!!!
每个加密和解密函数都在不同的运行中调用,因此所有内容始终使用相同的值进行初始化。再次感谢您可以提前为我提供的提示和问候!!!
回答by caf
Usually, you will be intending to call AES_ctr128_encrypt()repeatedly to send several messages with the same key and IV, and an incrementing counter. This means you need to keep track of the 'ivec', 'num' and 'ecount' values between calls - so create a structto hold these, and an initialisation function:
通常,您打算AES_ctr128_encrypt()重复调用以发送具有相同密钥和 IV 以及递增计数器的多条消息。这意味着您需要跟踪调用之间的 'ivec'、'num' 和 'ecount' 值 - 因此创建一个struct来保存这些值和一个初始化函数:
struct ctr_state {
unsigned char ivec[16]; /* ivec[0..7] is the IV, ivec[8..15] is the big-endian counter */
unsigned int num;
unsigned char ecount[16];
};
int init_ctr(struct ctr_state *state, const unsigned char iv[8])
{
/* aes_ctr128_encrypt requires 'num' and 'ecount' set to zero on the
* first call. */
state->num = 0;
memset(state->ecount, 0, 16);
/* Initialise counter in 'ivec' to 0 */
memset(state->ivec + 8, 0, 8);
/* Copy IV into 'ivec' */
memcpy(state->ivec, iv, 8);
}
Now, when you start communicating with the destination, you'll need to generate an IV to use and initialise the counter:
现在,当您开始与目的地通信时,您需要生成一个 IV 来使用和初始化计数器:
unsigned char iv[8];
struct ctr_state state;
if (!RAND_bytes(iv, 8))
/* Handle the error */;
init_ctr(&state, iv);
You will then need to send the 8 byte IV to the destination. You'll also need to initialise an AES_KEYfrom your raw key bytes:
然后您需要将 8 字节 IV 发送到目的地。您还需要AES_KEY从原始密钥字节初始化一个:
AES_KEY aes_key;
if (!AES_set_encrypt_key(key, 128, &aes_key))
/* Handle the error */;
You can now start encrypting data and sending it to the destination, with repeated calls to AES_ctr128_encrypt()like this:
您现在可以开始加密数据并将其发送到目的地,重复调用AES_ctr128_encrypt()如下:
if (!AES_ctr128_encrypt(msg_in, msg_out, msg_len, &aes_key, state->ivec, state->ecount, &state->num))
/* Handle the error */;
(msg_inis a pointer to a buffer containing the plaintext message, msg_outis a pointer to a buffer where the encrypted message should go, and msg_lenis the message length).
(msg_in是指向包含明文消息msg_out的缓冲区的指针,是指向加密消息应该去的缓冲区的指针,msg_len是消息长度)。
Decryption is exactly the same, except that you do not generate the IV with RAND_bytes()- instead, you take the value given to you by the other side.
解密是完全一样的,除了你没有用RAND_bytes()-生成IV,而是取另一方给你的值。
Important:
重要的:
Do notcall
init_ctr()more than once during the encryption process. The counter and IV must be initialised once onlyprior to the start of encryption.Under no circumstances be tempted to get the IV anywhere other than from
RAND_bytes()on the encryption side. Don't set it to a fixed value; don't use a hash function; don't use the recipient's name; don't read it from disk. Generate it withRAND_bytes()and send it to the destination. Whenever you start with a zero counter, you muststart with a completely fresh IV that you have never used before.If it is at all possible that you will be sending 2**64 bytes without changing the IV and/or key, you will need to test for the counter overflowing.
Do not omit error-checking. If a function fails and you ignore it, it's quite possible (even likely) that your system will appear to be functioning normally, but will actually be operating completely insecurely.
难道不叫
init_ctr()在加密过程中不止一次。计数器和 IV 必须仅在加密开始之前初始化一次。在任何情况下都不要试图从
RAND_bytes()加密端以外的任何地方获取 IV 。不要将其设置为固定值;不要使用哈希函数;不要使用收件人的姓名;不要从磁盘读取它。生成它RAND_bytes()并将其发送到目的地。每当您从零计数器开始时,您必须从以前从未使用过的全新 IV 开始。如果您有可能在不更改 IV 和/或密钥的情况下发送 2**64 字节,则需要测试计数器是否溢出。
不要省略错误检查。如果某个功能失败而您忽略它,则您的系统很可能(甚至很可能)看起来运行正常,但实际上运行起来完全不安全。
回答by Mark Wilkins
It looks like the basic problem with your test program is that the mode values of the fopencalls is not correct. I think you need to change your fopen calls in encrypt to this:
看起来您的测试程序的基本问题是fopen调用的模式值不正确。我认为您需要将加密的 fopen 调用更改为:
fp=fopen("input.txt","rb");
op=fopen("output.txt","wb");
And the ones in decrypt to:
和解密的那些:
rp=fopen("recovered.txt","wb");
op=fopen("output.txt","rb");
One other thing worth pointing out is that ckeyshould probably be declared as a 32 byte (256 bit) buffer. It is true that the 128-bit encryption only uses 16 bytes of the data from the key. But the OpenSSL function AES_set_encrypt_key(at least in the version I am using) reads 32 bytes from that buffer. It only uses the appropriate number of bytes, but the read does occur. That means that if the buffer is only 16-bytes and happens end at the end of a page that is adjacent to a non-readable page in memory, it would result in an access violation.
值得指出的另一件事是,它ckey可能应该声明为 32 字节(256 位)缓冲区。确实,128 位加密仅使用密钥中的 16 字节数据。但是 OpenSSL 函数AES_set_encrypt_key(至少在我使用的版本中)从该缓冲区读取 32 个字节。它仅使用适当数量的字节,但确实会发生读取。这意味着如果缓冲区只有 16 字节并且发生在与内存中不可读页面相邻的页面末尾,则会导致访问冲突。
Oh - and I just noticed that there is an extraneous call to freein there. The free(buffer);call is not valid since buffer was never allocated. I realize your code is just a simple test, but ... well, we are programmers and can't help ourselves.
哦 - 我刚刚注意到那里有一个无关紧要的电话free。该free(buffer);调用无效,因为从未分配过缓冲区。我意识到你的代码只是一个简单的测试,但是......好吧,我们是程序员,无法自拔。

