web-dev-qa-db-ja.com

OpenSSLでのAESCTR256暗号化動作モード

OpenSSLは初めてですが、CファイルからAESCTRモードを初期化する方法のヒントを教えてもらえますか。これがメソッドのシグネチャであることは知っていますが、パラメータに問題があります。ドキュメントも多くなく、単純な暗号化を行う方法の明確な例もありません。誰かがこのメソッドの呼び出しを例示できれば幸いです。前もって感謝します!

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

こんにちはカフェ私はあなたの素早い回答に本当に感謝しています。それは本当に便利で、間違いなく私がウェブで見つけた最良の例です。長さが不定のファイルを開き、暗号化して、生成された暗号文で別のファイルを書き込んでから、暗号化されたファイルを開いて平文を復元しようとしています。 CPUのパフォーマンスをベンチマークしたいので、かなりのMBのファイルを使用する必要があります。ただし、Imは復号化中にまだ問題があります。どういうわけか、かなりの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;
}

各暗号化および復号化関数は異なる実行で呼び出されるため、すべてが常に同じ値で初期化されます。事前に私に提供できるヒントとよろしくお願いします!!!

18
Bartzilla

通常、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_はメッセージの長さです)。

復号化はまったく同じですが、Rand_bytes()を使用してIVを生成しない点が異なります。代わりに、反対側から指定された値を取得します。

重要:

  1. not暗号化プロセス中にinit_ctr()を複数回呼び出します。カウンターとIVは、暗号化を開始する前に初期化する必要があります1回のみ

  2. いかなる状況においても、暗号化側のRand_bytes()以外の場所でIVを取得しようとすることはありません。固定値に設定しないでください。ハッシュ関数を使用しないでください。受信者の名前は使用しないでください。ディスクから読み取らないでください。 Rand_bytes()を使用して生成し、宛先に送信します。ゼロカウンターから始めるときはいつでも、これまでに使用したことがないまったく新しいIVから始めなければなりません

  3. IVやキーを変更せずに2 ** 64バイトを送信する可能性がある場合は、カウンターのオーバーフローをテストする必要があります。

  4. エラーチェックを省略しないでください。関数が失敗して無視した場合、システムは正常に機能しているように見えますが、実際には完全に安全でない状態で動作している可能性があります。

28
caf

テストプログラムの基本的な問題は、fopen呼び出しのモード値が正しくないことのようです。暗号化でのfopen呼び出しを次のように変更する必要があると思います。

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

そして、復号化するものは次のとおりです。

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

指摘する価値のあるもう1つのことは、ckeyはおそらく32バイト(256ビット)のバッファーとして宣言する必要があるということです。 128ビットの暗号化では、キーからの16バイトのデータのみが使用されることは事実です。しかし、OpenSSL関数_AES_set_encrypt_key_(少なくとも私が使用しているバージョンでは)は、そのバッファーから32バイトを読み取ります。適切なバイト数のみを使用しますが、読み取りは行われます。つまり、バッファが16バイトのみで、メモリ内の読み取り不可能なページに隣接するページの最後で発生すると、アクセス違反が発生します。

ああ-そして私はそこにfreeへの無関係な呼び出しがあることに気づきました。バッファが割り当てられなかったため、free(buffer);呼び出しは無効です。あなたのコードは単なるテストだと思いますが...まあ、私たちはプログラマーであり、自分自身を助けることはできません。

2
Mark Wilkins