使用 AES 256 (MS CryptoAPI) 时 CryptDecrypt 出现错误数据错误

发布于 2024-12-11 03:29:12 字数 4394 浏览 0 评论 0 原文

我正在尝试解密 - 使用 C++ 中的微软 CryptoAPI - 使用 PHP 中的 mcrypt_encrypt 加密的短消息。 php 行是: mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $key, $msg, MCRYPT_MODE_CBC);

其中 $key 和 $msg 是字符串。

在 C++ 中,我有密钥,我的解密函数如下所示:

bool decrypt( const unsigned char* input_buffer,
                 const size_t& input_size,
                 const unsigned char* key,
                 const size_t& key_size,
                 unsigned char* output_buffer,
                 DWORD* out_size)
{
   Log(L"START init_crypto");

   bool       ret        = false;
   HCRYPTKEY  hKey       = NULL;
   HCRYPTPROV hCryptProv = NULL;
   DWORD      dwDataLen  = 0;

   // Attempt to acquire a handle to the crypto provider for AES
   if(0 == CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) ){//PROV_RSA_AES
    Log(L"CryptAcquireContext failed with code %ld", GetLastError());
        goto end;
}

   // key creation based on
   // http://mirror.leaseweb.com/NetBSD/NetBSD-release-5-0/src/dist/wpa/src/crypto/crypto_cryptoapi.c
   struct {
     BLOBHEADER hdr;
     DWORD len;
     BYTE key[32]; 
   } key_blob;

   key_blob.hdr.bType    = PLAINTEXTKEYBLOB;
   key_blob.hdr.bVersion = CUR_BLOB_VERSION;
   key_blob.hdr.reserved = 0;
   key_blob.hdr.aiKeyAlg = CALG_AES_256;
   key_blob.len          = 32;//key_size;

   memset(key_blob.key, '\0', sizeof(key_blob.key));

   assert(key_size <= sizeof(key_blob.key));
   CopyMemory(key_blob.key, key, min(key_size, sizeof(key_blob.key)));

   if (!CryptImportKey( hCryptProv,
                    (BYTE *)&key_blob,
                    sizeof(key_blob),
                    0,
                    CRYPT_EXPORTABLE,
                    &hKey)){
      Log(L"Error in CryptImportKey 0x%08x \n", GetLastError());
      goto free_context;
   }
   else{
       //---------------------------
       // Set Mode
       DWORD dwMode = CRYPT_MODE_CBC;
       if(!CryptSetKeyParam( hKey, KP_MODE, (BYTE*)&dwMode, 0 )){
           // Handle error
           Log(L"Cannot set the cbc mode for decryption 0x%08x \n", GetLastError());
           goto free_key;
       }

       //----------------------------
       // Set IV
       DWORD dwBlockLen = 0;
       DWORD dwDataLen  = sizeof(dwBlockLen);
       if (!CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE *)&dwBlockLen, &dwDataLen, 0)){
           // Handle error
           Log(_USTR("Cannot get the block length 0x%08x \n"), GetLastError());
           goto free_key;
       }
       dwBlockLen /= 8;
       BYTE *pbTemp = NULL;
       if (!(pbTemp = (BYTE *)LocalAlloc(LMEM_FIXED, dwBlockLen))){
           // Handle error
           Log(L"Cannot allcoate the IV block 0x%08x \n", GetLastError());
          goto free_key;
       }
       memset(pbTemp, '\0', dwBlockLen);
       if (!CryptSetKeyParam(hKey, KP_IV, pbTemp, 0)){
           // Handle error
           Log(L"Cannot set the IV block 0x%08x \n", GetLastError());
           LocalFree(pbTemp);
           goto free_key;
        }

        LocalFree(pbTemp);
  }

  CopyMemory(output_buffer, input_buffer, min(*out_size, input_size));
  *out_size = input_size;

  if (!CryptDecrypt(hKey, NULL, TRUE, 0, output_buffer, out_size)){
    Log(L"CryptDecrypt failed with code %ld", GetLastError());
    goto free_key;
  }
  else{
    Log(L"Decryption...");
    ret = true;
  }
free_key:
   if (hKey)
      CryptDestroyKey( hKey );

free_context:
   if (hCryptProv)
      CryptReleaseContext(hCryptProv, 0);
end:
   return ret;
}

我一直在 CryptDecrypt() 处收到“坏数据”错误...我可能遗漏了一些明显的东西 - 如果是这样,请提供帮助。

编辑 - 为了找出问题的原因,我用以下代码替换了 CryptDecrypt (CopyMemory 内容)之前的两行: ......

 strcpy((char*)output_buffer, "stuff");
 DWORD plain_txt_len = strlen((char*)output_buffer);
 if (!CryptEncrypt(hKey, NULL, TRUE, 0, output_buffer, &plain_txt_len, *out_size)){
      Log(L"CryptEncrypt failed with code 0x%08x", GetLastError());
      goto free_key;
 }  

​ 并且 CryptDecrypt 正在工作 - 这让我相信问题在于从 php 到 C++ 的密钥/和/或消息传输...如果您同意,您能给我一个提示,告诉我如何确保我在 PHP 中使用的字符串与 C++ 中的相同(在字节级别?)

EDIT2 -- 在我在 php 中更改二进制流中的字符串(使用 pack)之后,以及在我从此处实现 AES 与 Rijndael 的解决方法(?)之后: http://kix.in/2008/07/22/aes-256-using-php-mcrypt/ 我终于让 CryptDecrypt 解密了我的 PHP 消息...问题是它即使输出包含解密的文本,仍然会失败。关于为什么会发生这种情况有什么想法吗?

I'm trying to decrypt - using the microsoft's CryptoAPI in C++ - a short message encrypted using mcrypt_encrypt in PHP. The php line is:
mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $key, $msg, MCRYPT_MODE_CBC);

where $key and $msg are strings.

In C++ I have the key, and my decryption function looks like this:

bool decrypt( const unsigned char* input_buffer,
                 const size_t& input_size,
                 const unsigned char* key,
                 const size_t& key_size,
                 unsigned char* output_buffer,
                 DWORD* out_size)
{
   Log(L"START init_crypto");

   bool       ret        = false;
   HCRYPTKEY  hKey       = NULL;
   HCRYPTPROV hCryptProv = NULL;
   DWORD      dwDataLen  = 0;

   // Attempt to acquire a handle to the crypto provider for AES
   if(0 == CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) ){//PROV_RSA_AES
    Log(L"CryptAcquireContext failed with code %ld", GetLastError());
        goto end;
}

   // key creation based on
   // http://mirror.leaseweb.com/NetBSD/NetBSD-release-5-0/src/dist/wpa/src/crypto/crypto_cryptoapi.c
   struct {
     BLOBHEADER hdr;
     DWORD len;
     BYTE key[32]; 
   } key_blob;

   key_blob.hdr.bType    = PLAINTEXTKEYBLOB;
   key_blob.hdr.bVersion = CUR_BLOB_VERSION;
   key_blob.hdr.reserved = 0;
   key_blob.hdr.aiKeyAlg = CALG_AES_256;
   key_blob.len          = 32;//key_size;

   memset(key_blob.key, '\0', sizeof(key_blob.key));

   assert(key_size <= sizeof(key_blob.key));
   CopyMemory(key_blob.key, key, min(key_size, sizeof(key_blob.key)));

   if (!CryptImportKey( hCryptProv,
                    (BYTE *)&key_blob,
                    sizeof(key_blob),
                    0,
                    CRYPT_EXPORTABLE,
                    &hKey)){
      Log(L"Error in CryptImportKey 0x%08x \n", GetLastError());
      goto free_context;
   }
   else{
       //---------------------------
       // Set Mode
       DWORD dwMode = CRYPT_MODE_CBC;
       if(!CryptSetKeyParam( hKey, KP_MODE, (BYTE*)&dwMode, 0 )){
           // Handle error
           Log(L"Cannot set the cbc mode for decryption 0x%08x \n", GetLastError());
           goto free_key;
       }

       //----------------------------
       // Set IV
       DWORD dwBlockLen = 0;
       DWORD dwDataLen  = sizeof(dwBlockLen);
       if (!CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE *)&dwBlockLen, &dwDataLen, 0)){
           // Handle error
           Log(_USTR("Cannot get the block length 0x%08x \n"), GetLastError());
           goto free_key;
       }
       dwBlockLen /= 8;
       BYTE *pbTemp = NULL;
       if (!(pbTemp = (BYTE *)LocalAlloc(LMEM_FIXED, dwBlockLen))){
           // Handle error
           Log(L"Cannot allcoate the IV block 0x%08x \n", GetLastError());
          goto free_key;
       }
       memset(pbTemp, '\0', dwBlockLen);
       if (!CryptSetKeyParam(hKey, KP_IV, pbTemp, 0)){
           // Handle error
           Log(L"Cannot set the IV block 0x%08x \n", GetLastError());
           LocalFree(pbTemp);
           goto free_key;
        }

        LocalFree(pbTemp);
  }

  CopyMemory(output_buffer, input_buffer, min(*out_size, input_size));
  *out_size = input_size;

  if (!CryptDecrypt(hKey, NULL, TRUE, 0, output_buffer, out_size)){
    Log(L"CryptDecrypt failed with code %ld", GetLastError());
    goto free_key;
  }
  else{
    Log(L"Decryption...");
    ret = true;
  }
free_key:
   if (hKey)
      CryptDestroyKey( hKey );

free_context:
   if (hCryptProv)
      CryptReleaseContext(hCryptProv, 0);
end:
   return ret;
}

I consistently get the "bad data" error at CryptDecrypt()... I may be missing something obvious - if so, please help.

EDIT - To isolate the cause of the problem I replaced the two lines before CryptDecrypt (the CopyMemory stuff) with the following code:
....

 strcpy((char*)output_buffer, "stuff");
 DWORD plain_txt_len = strlen((char*)output_buffer);
 if (!CryptEncrypt(hKey, NULL, TRUE, 0, output_buffer, &plain_txt_len, *out_size)){
      Log(L"CryptEncrypt failed with code 0x%08x", GetLastError());
      goto free_key;
 }  

...
and the CryptDecrypt is working -- which makes me believe that the problem is the key/and or message transmission from php to C++ ... If you agree can you give me a hint on how to make sure that the strings I use in PHP are the same with the ones in C++ (at byte level?)

EDIT2 --
After I changed my strings in binary streams (using pack) in php, and after I implemented the workaround(?) for AES vs Rijndael from here : http://kix.in/2008/07/22/aes-256-using-php-mcrypt/ I finaly have CryptDecrypt decrypting my PHP message... the problem is that it also still fails - even if the output contains the decrypted text. Any ideas about why could this happen?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜点 2024-12-18 03:29:12

获取上下文时尝试传递 NULL 而不是 CRYPT_VERIFYCONTEXT

Try passing NULL instead of CRYPT_VERIFYCONTEXT when acquiring the context.

酒解孤独 2024-12-18 03:29:12

使用 AES 等块加密算法,您需要向要加密的数据添加填充,最多可达块长度的倍数。使用已经检索块大小的代码示例,您可以计算加密所需的填充:

dwPadding = dwBlockLen - dwDataLen % dwBlockLen

然后将“dwPadding”字符数(NULL 工作正常)附加到正在加密的数据中,您将不再在解密时收到“坏数据”错误。

您还可以在实际加密之前使用 NULL 缓冲区额外调用“CryptEncrypt”,直接找出所需加密缓冲区的大小:

dwBuffSize = dwDataLen
CryptEncrypt(hKey, NULL, TRUE, 0, NULL, &dwBuffSize, 0)
dwPadding = dwBuffsize - dwDataLen

这两种方法是等效的,并产生相同的所需结果。 “CryptDecrypt”函数已经知道填充,并将一次性返回解密缓冲区的实际大小,因此您将确切地知道解密数据的结束位置。

With block encryption algorithms such as AES you need to add padding to the data being encrypted up to a multiple of the block length. Using your code example that already retrieves the block size you can calculate the padding required for encryption:

dwPadding = dwBlockLen - dwDataLen % dwBlockLen

Then append "dwPadding" number of characters (NULL works fine) to the data being encrypted and you will no longer get "Bad Data" errors in decryption.

You can also find out the size of the required encryption buffer directly by making an additional call to "CryptEncrypt" with a NULL buffer before the actual encryption:

dwBuffSize = dwDataLen
CryptEncrypt(hKey, NULL, TRUE, 0, NULL, &dwBuffSize, 0)
dwPadding = dwBuffsize - dwDataLen

Both methods are equivalent and produce the same desired result. The "CryptDecrypt" function already knows about padding and will return the actual size of the decryption buffer in one go so you will know exactly where your decrypted data ends.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文