2

我需要在客户端和服务器之间建立两个通道,第一个是用于数据传输的 UDP 通道,第二个是用于发送密钥的 TCP 通道,iv 用于 UDP 通道中的 AES-128。

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

服务器需要能够连接多个客户端,在 select() 中使用的 TCP 和 UDP 套接字如下:

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 可能有问题;我验证了所有这些。

但是奇怪的情况是,当我认为我的服务器只连接到一个客户端时,上面的解密代码可以正确解密;当我不在 select() 中使用我的 TCP 套接字并在外部(之前)使用它时 select() 接受连接并仅从一个客户端获取密钥/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 回答