AES CTR 256 Шифрование Режим работы на OpenSSL

17

Im new to OpenSSL, может ли кто-нибудь дать мне подсказку о том, как инициализировать режим CTR AES из файла C. Я знаю, что это подпись метода, но у меня возникают проблемы с параметрами, документации не так много, нет ясного примера, как сделать простое шифрование. Я был бы признателен, если бы кто-нибудь мог продемонстрировать призыв к этому методу. Спасибо заранее!

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);

Привет, я очень ценю ваш быстрый ответ, который был действительно полезен, и defenetly лучший пример, который я нашел в Интернете. Я пытаюсь открыть файл с неопределенной длиной, зашифровать его и записать другой файл с зашифрованным текстом, затем открыть зашифрованный файл и восстановить открытый текст. Мне нужно использовать файл значительного количества MB, потому что я хотел бы сравнить производительность процессора. Однако у меня все еще есть проблема при расшифровке. Как-то при расшифровке значительных файлов txt (1504 КБ) он не будет расшифровывать его полностью, и я получу его в открытом тексте, а другая половина все еще зашифрована. Я думаю, что это может быть связано с размером 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;
}

Каждая функция шифрования и дешифрования вызывается в разных прогонах, поэтому все инициализируется всегда с одинаковыми значениями. Еще раз спасибо за подсказки, которые вы можете предоставить мне заранее и amp; С уважением!!!     

задан Bartzilla 29.06.2010 в 16:50
источник
  • Ваша проблема в том, что вы повторно инициализируете счетчик после каждого блока. Это неверно - переместите вызов init_ctr () за пределы while () циклов как при шифровании, так и в расшифровке. indata и outdata также не должны иметь длину AES_BLOCK_SIZE - они могут быть значительно большими. –  caf 06.08.2010 в 02:24
  • Вы не должны использовать AES_encrypt и друзей. Это программная реализация, поэтому вам не понравится аппаратная поддержка, например AES-NI. Вы должны использовать функции EVP_ *. См. Симметричное шифрование и дешифрование EVP на вики OpenSSL. На самом деле, вероятно, вы должны использовать аутентифицированное шифрование, поскольку оно обеспечивает как конфиденциальность, так и аутентичность. См. EVP Authenticated Encryption and Decryption в вики OpenSSL. –  jww 28.05.2016 в 15:15
  • Если вы используете функции EVP_ *, то представляющие интерес шифры - EVP_aes_128_ctr, EVP_aes_192_ctr и EVP_aes_256_ctr. –  jww 28.05.2016 в 15:27

2 ответа

27

Обычно вы намереваетесь повторно называть 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);
}

Теперь, когда вы начинаете общаться с пунктом назначения, вам нужно создать IV для использования и инициализации счетчика:

unsigned char iv[8];
struct ctr_state state;

if (!RAND_bytes(iv, 8))
    /* Handle the error */;

init_ctr(&state, iv);

Затем вам нужно отправить 8-байтный IV в пункт назначения. Вам также потребуется инициализировать AES_KEY из ваших необработанных ключевых байтов:

AES_KEY aes_key;

if (!AES_set_encrypt_key(key, 128, &aes_key))
    /* Handle the error */;

Теперь вы можете начать шифрование данных и отправить их в пункт назначения с повторными вызовами 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 - указатель на буфер, содержащий сообщение с открытым текстом, msg_out - указатель на буфер, в котором должно быть зашифрованное сообщение, а msg_len - длина сообщения).

Расшифровка точно такая же, за исключением того, что вы не генерируете IV с RAND_bytes() - вместо этого вы берете значение, предоставленное вам другой стороной.

Важно:

  1. Вызывает не вызов init_ctr() более одного раза в процессе шифрования. Счетчик и IV должны быть инициализированы только один раз до начала шифрования.

  2. Ни при каких обстоятельствах не возникает соблазн получить IV где-нибудь, кроме RAND_bytes() на стороне шифрования. Не устанавливайте его на фиксированное значение; не используйте хеш-функцию; не используйте имя получателя; не читайте его с диска. Сгенерируйте его с RAND_bytes() и отправьте его в пункт назначения. Всякий раз, когда вы начинаете с нулевого счетчика, вы должны начинать с совершенно нового IV, который вы никогда раньше не использовали.

  3. Если вообще возможно, что вы будете отправлять 2 ** 64 байта без изменения ключа IV и / или, вам нужно будет проверить, что счетчик переполнен.

  4. Не пропускайте проверку ошибок. Если функция выходит из строя, и вы ее игнорируете, вполне возможно (даже вероятно), что ваша система будет работать нормально, но фактически будет работать совершенно небезопасно.

ответ дан caf 30.06.2010 в 05:09
  • Позвольте мне добавить деталь, которая ускользнула от меня, когда я использовал это: аргумент num - это количество байтов в блоке, а не счетчик. Если вы шифруете пакеты (например), всегда устанавливайте state-> num в ноль и помещайте счетчик в верхние байты iv. –  Mike Elkins 09.11.2010 в 19:51
  • @Mike Elkins: Действительно - вы можете рассматривать как num, так и ecount как непрозрачное внутреннее состояние реализации CTR OpenSSL. В большинстве случаев нет необходимости прямо изменять их. –  caf 09.11.2010 в 22:35
2

Похоже, основная проблема с вашей тестовой программой заключается в том, что значения режима вызовов fopen неверны. Я думаю, вам нужно изменить ваши вызовы fopen в encrypt для этого:

fp=fopen("input.txt","rb");
op=fopen("output.txt","wb");

И те, которые дешифруются до:

rp=fopen("recovered.txt","wb");
op=fopen("output.txt","rb");

Еще одна вещь, заслуживающая внимания, состоит в том, что ckey , вероятно, должен быть объявлен как 32-байтовый (256-битный) буфер. Это правда, что 128-битное шифрование использует только 16 байт данных из ключа. Но функция OpenSSL AES_set_encrypt_key (по крайней мере, в используемой версии) считывает 32 байта из этого буфера. Он использует только соответствующее количество байтов, но чтение происходит. Это означает, что если буфер составляет всего 16 байт и заканчивается в конце страницы, которая смежна с нечитаемой страницей в памяти, это приведет к нарушению доступа.

О, и я только заметил, что там есть посторонний вызов free . Вызов free(buffer); недействителен, поскольку буфер никогда не выделялся. Я понимаю, что ваш код - просто простой тест, но ... ну, мы программисты и не можем помочь себе.

    
ответ дан Mark Wilkins 05.08.2010 в 18:06