2

クライアントとサーバーの間に 2 つのチャネルを確立する必要があります。1 つ目はデータ送信用の UDP チャネル、2 つ目はキーを送信するための TCP チャネル、UDP チャネルで AES-128 用の iv です。

TCP ソケットは、次のようにサーバーで作成されます。

listen_fd = socket (AF_INET, SOCK_STREAM, 0);
// sa_serv contains TCP port
error = bind(listen_fd, (struct sockaddr*) &sa_serv, sizeof (sa_serv));

UDP ソケットは、次のようにサーバーで作成されます。

sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
// local contains UDP port
error = bind(sock_fd, (struct sockaddr*) &local, sizeof(local));

サーバーは複数のクライアントに接続できる必要があり、TCP および UDP ソケットは次のように select() で使用されます。

max = (listen_fd > sock_fd) : listen_fd : sock_fd;
fd_set set;
FD_ZERO(&set);
FD_SET(listen_fd, &set); FD_SET(sock_fd, &set);

while(1)
{
      select(max + 1, &set, NULL, NULL, NULL);

      if(FD_ISSET(listen_fd, &set){
          // server accepts connection
          // server receives key and IV over TCP connection
      }
      if(FD_ISSET(sock_fd, &set){
          // server receives encrypted data from client using UDP socket
      }
}

サーバーがUDPソケットでデータを受信すると、サーバーはTCP接続を使用して受信したキーとIVを使用してデータを復号化します。復号化コードは次のとおりです。

 int decrypt(unsigned char *plain, unsigned char *key, unsigned char *iv, unsigned char *cipher, int len)
{
   int i;

    unsigned char buf[3000];
    int outlen, tmplen;
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
    EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);

    if(!EVP_DecryptUpdate(&ctx, buf, &outlen, cipher, len))
    {
            EVP_CIPHER_CTX_cleanup(&ctx);
            return 0;
    }

    if(!EVP_DecryptFinal_ex(&ctx, buf + outlen, &tmplen))
    {
            EVP_CIPHER_CTX_cleanup(&ctx);
            return 0;
    }

    outlen += tmplen;
    EVP_CIPHER_CTX_cleanup(&ctx);

    printf("\nLength decrypted :%d\n",outlen);

    printf("\nBuf: ");
    for(i=0; i<outlen; i++){
           plain[i] = outbuf[i];    
       printf(" %02x ",buf[i]);
    }
     printf("\n");

     return outlen;
 }

クライアントから受信した暗号がキーと IV とともにこの関数に渡されると、結果の平文は正しくないことが判明します (約 8 バイトが間違っています)。ここで、暗号が間違っている可能性がある、またはキーまたは iv に問題がある可能性があると主張する人がいるかもしれません。全部確認しました。

しかし、私のサーバーが 1 つのクライアントだけに接続されていると考えると、上記の解読コードが正しく解読されるという奇妙な状況が存在します。select() で TCP ソケットを使用せず、(前に) select() の外部で使用して、接続を受け入れ、1 つのクライアントからキー/iv を取得する場合 (接続を受け入れ、クライアントからキー/iv を受信するためのコードは正確にselect()) 内で使用する場合と同じで、select() では UDP ソケットを使用してデータを送受信します。受信した暗号化データは正しく解読されます。

私が理解できないのは、TCP ソケットを select() fd_set に入れることで、正しい暗号、キー、および IV を取得しているにもかかわらず、同じ復号化コードが問題を引き起こしている理由です。

誰もこれについて説明がありますか?

ありがとう。

4

1 に答える 1

1

最初の 8 バイトが正しくなく、次のバイトが正しいと仮定すると、暗号化に使用したものとは異なる IV を復号化に使用していることになります。復号化する場合、IV は最初の復号化されたブロック (平文の最初の 128 ビット) にのみ影響します。

最後のバイトが間違っていると仮定すると、メッセージの展開を正しく考慮していますか? つまり、完全な暗号文を相手に送信していますか、それとも暗号文の len(plaintext) バイトのみを渡していますか?

追加のポイント:

  • 複数の UDP パケットに同じ IV を再利用している場合: 各メッセージ (個別に暗号化された UDP パケット) ごとに異なる IV を使用する必要があります。
  • 帯域外チャネルを介して渡す代わりに、ランダムな IV を使用して、UDP 経由で送信する暗号文の前に追加することをお勧めします。
  • TCP は安全でないため、適切な帯域外チャネルではありません。

基本的に、あなたがしていることは DTLS を再発明することです。

于 2012-04-20T11:33:17.263 に答える