AES ctr256在OpenSSL上的操作加密模式

时间:2022-12-29 13:13:37

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模式。我知道这是´s签名的方法与参数,但是我有问题´s没有许多文档都没有一个清晰的例子如何制作一个简单的加密。如果有人能举例说明这个方法,我将不胜感激。提前谢谢!

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)时,它无法完成解密工作,而我得到的一半是明文,另一半仍是密码。我认为这可能与静脉注射的大小或者我打电话给柜台的方式有关。这是我到目前为止所得到的:

#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!!!

每个加密和解密函数在不同的运行中被调用,所以所有的东西总是用相同的值初始化。再次感谢您能提前给我的提示和问候!!

2 个解决方案

#1


27  

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 struct to hold these, and an initialisation function:

通常,您需要反复调用AES_ctr128_encrypt(),以发送具有相同密钥和IV以及递增计数器的多个消息。这意味着您需要跟踪调用之间的“ivec”、“num”和“ecount”值——因此创建一个包含这些值的结构体和初始化函数:

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_KEY from 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_in is a pointer to a buffer containing the plaintext message, msg_out is a pointer to a buffer where the encrypted message should go, and msg_len is 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:

重要的是:

  1. Do not call init_ctr() more than once during the encryption process. The counter and IV must be initialised once only prior to the start of encryption.

    在加密过程中,不要多次调用init_ctr()。只有在开始加密之前,计数器和IV才必须被初始化一次。

  2. 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 with RAND_bytes() and send it to the destination. Whenever you start with a zero counter, you must start with a completely fresh IV that you have never used before.

    在任何情况下,除了从加密端的RAND_bytes()获取IV是没有吸引力的。不要将它设置为一个固定值;不要使用哈希函数;不要使用收件人的姓名;不要从磁盘读取。使用RAND_bytes()生成它并将其发送到目的地。无论何时你从零计数器开始,你必须从一个你从未用过的全新的IV开始。

  3. 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.

    如果您可能在不更改IV和/或键的情况下发送2**64字节,则需要对计数器溢出进行测试。

  4. 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.

    不要忽略错误检查。如果一个函数失败了,而您忽略了它,那么很有可能(甚至可能)您的系统看上去是正常的,但实际上是完全不安全的。

#2


2  

It looks like the basic problem with your test program is that the mode values of the fopen calls 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 ckey should 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 free in 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.

哦,我刚刚注意到那里有一个无关紧要的免费电话。*(缓冲);调用无效,因为从未分配缓冲区。我知道你的代码只是一个简单的测试,但是……嗯,我们是程序员,没办法。

#1


27  

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 struct to hold these, and an initialisation function:

通常,您需要反复调用AES_ctr128_encrypt(),以发送具有相同密钥和IV以及递增计数器的多个消息。这意味着您需要跟踪调用之间的“ivec”、“num”和“ecount”值——因此创建一个包含这些值的结构体和初始化函数:

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_KEY from 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_in is a pointer to a buffer containing the plaintext message, msg_out is a pointer to a buffer where the encrypted message should go, and msg_len is 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:

重要的是:

  1. Do not call init_ctr() more than once during the encryption process. The counter and IV must be initialised once only prior to the start of encryption.

    在加密过程中,不要多次调用init_ctr()。只有在开始加密之前,计数器和IV才必须被初始化一次。

  2. 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 with RAND_bytes() and send it to the destination. Whenever you start with a zero counter, you must start with a completely fresh IV that you have never used before.

    在任何情况下,除了从加密端的RAND_bytes()获取IV是没有吸引力的。不要将它设置为一个固定值;不要使用哈希函数;不要使用收件人的姓名;不要从磁盘读取。使用RAND_bytes()生成它并将其发送到目的地。无论何时你从零计数器开始,你必须从一个你从未用过的全新的IV开始。

  3. 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.

    如果您可能在不更改IV和/或键的情况下发送2**64字节,则需要对计数器溢出进行测试。

  4. 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.

    不要忽略错误检查。如果一个函数失败了,而您忽略了它,那么很有可能(甚至可能)您的系统看上去是正常的,但实际上是完全不安全的。

#2


2  

It looks like the basic problem with your test program is that the mode values of the fopen calls 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 ckey should 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 free in 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.

哦,我刚刚注意到那里有一个无关紧要的免费电话。*(缓冲);调用无效,因为从未分配缓冲区。我知道你的代码只是一个简单的测试,但是……嗯,我们是程序员,没办法。