【问题标题】:AES-CBC encrypted output in CNG is not matching with online toolsCNG 中的 AES-CBC 加密输出与在线工具不匹配
【发布时间】:2017-08-18 02:03:59
【问题描述】:

我是 CNG 的新手。我正在玩 msdn 网站上的基本程序。 我已经修改了输入纯字符串并使用其他提供 aes cbc 加密输出的网站测试了输出。 不幸的是只有上半场比赛,下半场没有比赛。 如果有人能指出错误所在,那就太好了,

msdn 的原始代码是here

这是我的代码生成的输出(如下)。请注意,除了修改输入的纯字符串外,我的代码没有任何区别。

这是来自在线网站的输出(http://aes.online-domain-tools.com/anothersite

前半场以“B0 C4 29 18”结束..之后第二半场不匹配。

这里是代码sn-p

#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>

#pragma comment(lib, "bcrypt.lib")

#ifndef STATUS_UNSUCCESSFUL
#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)
#endif // !STATUS_UNSUCCESSFUL

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#endif

void
print_inhex(char *buf, int len) {
    for (int i = 0; i < len; i++)
        printf(" %02x", buf[i]);
    printf("\n");
}

const BYTE rgbPlaintext[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};

static const BYTE rgbIV[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};

static const BYTE rgbAES128Key[] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};


void
CNG_aes_cbc()
{

    BCRYPT_ALG_HANDLE       hAesAlg = NULL;
    BCRYPT_KEY_HANDLE       hKey = NULL;
    NTSTATUS                status = STATUS_UNSUCCESSFUL;
    DWORD                   cbCipherText = 0,
        cbPlainText = 0,
        cbData = 0,
        cbKeyObject = 0,
        cbBlockLen = 0,
        cbBlob = 0;
    PBYTE                   pbCipherText = NULL,
        pbPlainText = NULL,
        pbKeyObject = NULL,
        pbIV = NULL,
        pbBlob = NULL;

    // Open an algorithm handle.
    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    // Calculate the size of the buffer to hold the KeyObject.
    if (!NT_SUCCESS(status = BCryptGetProperty(hAesAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbData, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    // Allocate the key object on the heap.
    pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
    if (NULL == pbKeyObject) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    // Calculate the block length for the IV.
    if (!NT_SUCCESS(status = BCryptGetProperty(hAesAlg, BCRYPT_BLOCK_LENGTH, (PBYTE)&cbBlockLen, sizeof(DWORD), &cbData, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    // Determine whether the cbBlockLen is not longer than the IV length.
    if (cbBlockLen > sizeof(rgbIV)) {
        wprintf(L"**** block length is longer than the provided IV length\n");
        goto Cleanup;
    }

    // Allocate a buffer for the IV. The buffer is consumed during the 
    // encrypt/decrypt process.
    pbIV = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlockLen);
    if (NULL == pbIV) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    memcpy(pbIV, rgbIV, cbBlockLen);

    if (!NT_SUCCESS(status = BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptSetProperty\n", status);
        goto Cleanup;
    }

    // Generate the key from supplied input key bytes.
    if (!NT_SUCCESS(status = BCryptGenerateSymmetricKey(hAesAlg, &hKey, pbKeyObject, cbKeyObject, (PBYTE)rgbAES128Key, sizeof(rgbAES128Key), 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptGenerateSymmetricKey\n", status);
        goto Cleanup;
    }


    // Save another copy of the key for later.
    if (!NT_SUCCESS(status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, NULL, 0, &cbBlob, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status);
        goto Cleanup;
    }


    // Allocate the buffer to hold the BLOB.
    PUCHAR pbBlob_1 = (PUCHAR)malloc(sizeof(PUCHAR) * cbBlob);
    //pbBlob = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlob);
    if (NULL == pbBlob_1) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, pbBlob_1, cbBlob, &cbBlob, 0))) {
        wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status);
        goto Cleanup;
    }

    PUCHAR blob = pbBlob_1 + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
    int len = cbBlob - sizeof(BCRYPT_KEY_DATA_BLOB_HEADER);
    printf("key:");
    print_inhex(blob, len);

    cbPlainText = sizeof(rgbPlaintext);
    pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbPlainText);
    if (NULL == pbPlainText) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    /*memcpy(pbPlainText, rgbPlaintext, sizeof(rgbPlaintext));*/
    char *test_msg = "This is my test msg";
    cbPlainText = strlen(test_msg) + 1;
    memcpy(pbPlainText, test_msg, cbPlainText);

    printf("plain text:");
    print_inhex(test_msg, strlen(test_msg));

    // Get the output buffer size.
    if (!NT_SUCCESS(status = BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, pbIV, cbBlockLen, NULL, 0, &cbCipherText, BCRYPT_BLOCK_PADDING)))  {
        wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status);
        goto Cleanup;
    }

    pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText);
    if (NULL == pbCipherText) {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    // Use the key to encrypt the plaintext buffer.
    // For block sized messages, block padding will add an extra block.
    if (!NT_SUCCESS(status = BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, pbIV, 
                         cbBlockLen, pbCipherText, cbCipherText, &cbData, BCRYPT_BLOCK_PADDING))){
        wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status);
        goto Cleanup;
    }

    printf("cipher text:");
    for (int i = 0; i < cbCipherText; i++)
        printf(" %02x", pbCipherText[i]);

    wprintf(L"\nSuccess!\n");

Cleanup:  
    if (hAesAlg)
        BCryptCloseAlgorithmProvider(hAesAlg, 0);

    if (hKey)
        BCryptDestroyKey(hKey);

    if (pbCipherText)
        HeapFree(GetProcessHeap(), 0, pbCipherText);

    if (pbKeyObject)
        HeapFree(GetProcessHeap(), 0, pbKeyObject);

    if (pbIV)
        HeapFree(GetProcessHeap(), 0, pbIV);
}

【问题讨论】:

  • IV 不是 256 位的,它是十六进制的 w=这里每个字符代表 4 位,32 字符 * 4 位 = 128 位。也不要在屏幕截图中提供输入、键和 IV。
  • 但是我在我的程序和在线网站中也使用了相同的 IV.. 他们都没有抱怨.. 这是来自 msdn 的相同代码,所以我假设 IV 可能不是问题。
  • 数据的长度不是块大小的精确倍数(AES 为 16 字节),因此添加了填充但是。那就是实现要么拒绝数据,要么使用默认方法填充,例如 0x00(密码学)、PKCS#7(通常使用的填充)或任何 junbk 跟随内存中提供的数据。最好在 AES 实例化时指定填充。
  • 感谢您的回复...根据msdn.microsoft.com/en-us/library/windows/desktop/…...对于对称密钥,唯一允许的选项是 0 或 BCRYPT_BLOCK_PADDING... 它没有谈论“填充 AES 实例化” ..您能否提供更多详细信息如何使用 CNG 执行此操作。

标签: c++ encryption aes padding cng


【解决方案1】:

您与 cbPlainText 的值不一致。

旁白:

  • 您还有一些非常可怕的复制/重新分配代码,其中您将字符串写入缓冲区,但不能保证与字符串一样大。
  • 您还定义了 NT_SUCCESS,使其无论是否失败都会返回。 0 是成功,!0 是失败。

您的十六进制打印到 tst_msg 的 strlen。但是你设置 cbPlainText = strlen(tst_msg) + 1。如果您将其设置为 strlen(tst_msg),那么您将得到 @zaph 的答案 (46CC2228E81B2A05E8E8EBF2B0C42918EC496128D7C45BD0B19BB2D6452A3936)。

您不匹配该网站,因为您使用了带有 PKCS#7 填充的 CNG,并且该网站使用了零填充。您可以通过获取输出密文并将其作为明文,然后点击解密来识别网站中使用的填充。然后它说你的输入是54686973206973206d792074657374206d736700000000000000000000000000。或者,如果您在网站的输入中添加00 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C,您将获得原始答案。或者添加0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D,你会得到@zaph 的答案。

所以,要做的事情:

  • 不要重新评估要打印的内容的长度,创建一个变量 (cbPlainText) 并坚持下去。
  • AES 是一种分组密码算法。所有块密码都需要完整的块,必须填充有缺陷的最后一个块(并且在可移动填充方案下,完整的最终块需要另一个块)。在继续之前了解这意味着什么。 https://en.wikipedia.org/wiki/Padding_(cryptography)#Symmetric_cryptography
  • 当加密出现问题时,请查看解密后的输出。
    • 通常带有“无填充”的解密输出会显示出来。
  • 学习 C 以及内存的工作原理。或者改用 C#,学习曲线不那么陡峭。

【讨论】:

    【解决方案2】:

    数据的长度不是块大小的精确倍数(AES 为 16 字节),因此添加了填充但是。这就是实现要么拒绝数据,要么使用默认方法填充,例如 0x00(密码学)、PKCS#7(通常使用的填充),要么在内存中提供的数据之后出现任何垃圾。

    不要使用BCryptEncrypt,使用AES Class

    SymmetricAlgorithm.Padding Property 注意:默认为 PaddingMode.PKCS7。

    最好在 AES 实例化时指定填充。

    PaddingMode Enumeration:PKCS7
    PKCS #7 填充字符串由一个字节序列组成,每个字节等于添加的填充字节的总数。

    手动添加 PKCS#7 填充到:

    cryptommathic AES:

    生产:46CC2228E81B2A05E8E8EBF2B0C42918EC496128D7C45BD0B19BB2D6452A3936

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-10
      • 2018-09-20
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      • 2018-11-18
      • 2013-08-11
      相关资源
      最近更新 更多