【问题标题】:How to export AES key derived using CryptoAPI如何导出使用 CryptoAPI 派生的 AES 密钥
【发布时间】:2015-06-17 15:05:03
【问题描述】:

我想使用 Windows CryptoAPI 函数进行 AES 加密。

如您所知,API 有a lot of functions 来创建、散列和更改输入的密钥;它派生出密钥,然后你就能得到它的句柄。

我的问题是我想知道派生密钥是什么?

#include <Windows.h>
#include <stdio.h>

int main()
{
    HCRYPTPROV hProv = 0;
    HCRYPTKEY hKey = 0;
    HCRYPTHASH hHash = 0;
    DWORD dwCount = 5;
    BYTE  rgData[512] = {0x01, 0x02, 0x03, 0x04, 0x05};
    LPWSTR wszPassword = L"pass";
    DWORD cbPassword = (wcslen(wszPassword)+1)*sizeof(WCHAR);

    if(!CryptAcquireContext(
        &hProv, 
        NULL,  
        MS_ENH_RSA_AES_PROV, 
        PROV_RSA_AES, 
        CRYPT_VERIFYCONTEXT))
    {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto Cleanup;
    }

    if(!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) 
    { 
        printf("Error %x during CryptCreateHash!\n", GetLastError());
        goto Cleanup;
    } 

    if(!CryptHashData(hHash, (PBYTE)wszPassword, cbPassword, 0)) 
    { 
        printf("Error %x during CryptHashData!\n", GetLastError());
        goto Cleanup;
    } 

    if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, CRYPT_EXPORTABLE, &hKey)) 
    { 
        printf("Error %x during CryptDeriveKey!\n", GetLastError());
        goto Cleanup;
    }

    for(DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ",rgData[i]);
    }
    printf("\n");

    if (!CryptEncrypt(
        hKey,
        0,
        TRUE,
        0,
        rgData,
        &dwCount,
        sizeof(rgData))) 
    {
        printf("Error %x during CryptEncrypt!\n", GetLastError());
        goto Cleanup;
    }

    for(DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ",rgData[i]);
    }
    printf("\n");

Cleanup:
    if(hKey) 
    {
        CryptDestroyKey(hKey);
    }
    if(hHash) 
    {
        CryptDestroyHash(hHash);
    }
    if(hProv) 
    {
        CryptReleaseContext(hProv, 0);
    }
    return 0;
}

【问题讨论】:

标签: c encryption cryptography aes cryptoapi


【解决方案1】:

编辑:如果密钥派生是在软件中,那么the answer of softwariness 可能会更好。所以这是一个更通用的答案,应该是首选。如果软件方法失败,您可以使用此重放方法。如果令牌不允许明文导出,则可能是这种情况。

一般来说,这些方法的创建方式是,即使不是不可能,也很难检索得到的秘密。但是,导出密钥的方法在in the Remarks section of the API documentation of CryptDeriveKey 中进行了描述。所以你可以重播你有基础数据的创建。

API 没有描述如果使用 SHA-2 会发生什么,但我认为它只是使用 SHA-256 结果的最左边的位作为密钥。

推导后当然可以通过加密/解密或者一些数据进行测试。

n 为所需的派生密钥长度,以字节为单位。派生密钥是CryptDeriveKey 完成哈希计算后哈希值的前 n 个字节。如果散列不是 SHA-2 系列的成员,并且所需的密钥用于 3DES 或 AES,则密钥派生如下:

  1. 通过重复常量0x36 64 次形成一个 64 字节的缓冲区。令 k 为输入参数hBaseData 表示的哈希值的长度。将缓冲区的前 k 个字节设置为缓冲区前 k 个字节与输入参数 @987654327 表示的哈希值的 XOR 运算的结果@。
  2. 通过重复常量0x5C 64 次形成一个 64 字节的缓冲区。将缓冲区的前 k 个字节设置为缓冲区前 k 个字节的 XOR 操作的结果,其哈希值为由输入参数hBaseData表示。
  3. 使用与hBaseData 参数表示的哈希值相同的哈希算法对步骤 1 的结果进行哈希处理。
  4. 使用与用于计算 hBaseData 参数表示的哈希值相同的哈希算法对步骤 2 的结果进行哈希处理。
  5. 将第 3 步的结果与第 4 步的结果连接起来。
  6. 使用第 5 步结果的前 n 个字节作为派生密钥。

【讨论】:

  • MickeySoft 似乎没有提供“备注”部分的永久链接或锚点,因此请在页面消失时更新。
  • 我对 OP 问题的解释是他们想导出使用CryptDeriveKey 派生的密钥,他们可以使用CryptExportKey 来完成,因为他们通过传递CryptDeriveKey 使派生密钥可导出CRYPT_EXPORTABLE 标志在他们的示例中,我对问题进行了一些编辑以澄清这一点。让我知道您是否对此有不同的解释,因为您在这里采取了不同的方向,这似乎是正确的,但不是我认为 OP 正在寻找的。​​span>
  • @softwariness OP 询问“我的问题是我想知道派生密钥是什么?”但我的问题是我没有阅读太多过去:“要导出的密钥 BLOB 是无用的,直到预期的接收者使用其上的 CryptImportKey 函数将密钥或密钥对导入到接收者的 CSP 中。” Mickeysoft 文档中提到过。很高兴为您的答案投票,我会在此处留下我的答案以防令牌不允许二进制材料的明文导出
  • 如果令牌不允许明文导出 - 这是一个有效点。我本来想提一下,但在我完成编码的时候忘记了。我已经链接到您的答案,并附有关于该场景的注释。
  • 非常感谢您指出这一点!这对于将 CryptoAPI 移植到 Crypto++ 或 OpenSSL 等至关重要。我很困惑为什么使用相同的哈希/加密方案会在库之间产生不同的结果。
【解决方案2】:

由于您通过将CRYPT_EXPORTABLE 传递给CryptDeriveKey function 使您的派生密钥可导出,因此可以使用CryptExportKey function 导出派生密钥材料。

例外情况是,如果您使用的第三方 CSP 不允许密钥导出,即使带有 CRYPT_EXPORTABLE 标志,在这种情况下,请参阅 Maarten Bodewes' answer 自己重播密钥派生步骤在 CSP 之外。

如果您关注CryptExportKey MSDN link,您会看到一个示例函数,展示了如何使用该函数以普通格式导出密钥材料。如果需要,也可以使用另一个密钥(即由另一个密钥加密)导出密钥包装,方法是将密钥句柄传递给hExpKey(第二个参数)到CryptExportKey。将 NULL 传递给此参数会以普通格式导出。

我已更新您的示例程序(如下)以使用上面 MSDN 链接中的示例代码导出密钥,并使用 CryptBinaryToString 函数将密钥材料打印为 base64 字符串。我已导出为PLAINTEXTKEYBLOB blob 类型,它使用数据结构BLOBHEADER|key length|key material,因此还需要做一些工作才能从该blob 中提取我们感兴趣的原始密钥材料:

#include <Windows.h>
#include <stdio.h>

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

BOOL GetExportedKey(
    HCRYPTKEY hKey,
    DWORD dwBlobType,
    LPBYTE *ppbKeyBlob,
    LPDWORD pdwBlobLen)
{
    DWORD dwBlobLength;
    *ppbKeyBlob = NULL;
    *pdwBlobLen = 0;

    // Export the public key. Here the public key is exported to a 
    // PUBLICKEYBLOB. This BLOB can be written to a file and
    // sent to another user.

    if (CryptExportKey(
        hKey,
        NULL,
        dwBlobType,
        0,
        NULL,
        &dwBlobLength))
    {
        printf("Size of the BLOB for the public key determined. \n");
    }
    else
    {
        printf("Error computing BLOB length.\n");
        return FALSE;
    }

    // Allocate memory for the pbKeyBlob.
    if (*ppbKeyBlob = (LPBYTE)malloc(dwBlobLength))
    {
        printf("Memory has been allocated for the BLOB. \n");
    }
    else
    {
        printf("Out of memory. \n");
        return FALSE;
    }

    // Do the actual exporting into the key BLOB.
    if (CryptExportKey(
        hKey,
        NULL,
        dwBlobType,
        0,
        *ppbKeyBlob,
        &dwBlobLength))
    {
        printf("Contents have been written to the BLOB. \n");
        *pdwBlobLen = dwBlobLength;
    }
    else
    {
        printf("Error exporting key.\n");
        free(*ppbKeyBlob);
        *ppbKeyBlob = NULL;

        return FALSE;
    }

    return TRUE;
}

int main()
{
    HCRYPTPROV hProv = 0;
    HCRYPTKEY hKey = 0;
    HCRYPTHASH hHash = 0;
    DWORD dwCount = 5;
    LPBYTE keyBlob = NULL;
    DWORD keyBlobLength;
    LPSTR keyBlobBase64 = NULL;
    DWORD base64Length = 0;
    BYTE  rgData[512] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
    LPWSTR wszPassword = L"pass";
    DWORD cbPassword = (wcslen(wszPassword) + 1)*sizeof(WCHAR);

    if (!CryptAcquireContext(
        &hProv,
        NULL,
        MS_ENH_RSA_AES_PROV,
        PROV_RSA_AES,
        CRYPT_VERIFYCONTEXT))
    {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto Cleanup;
    }

    if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
    {
        printf("Error %x during CryptCreateHash!\n", GetLastError());
        goto Cleanup;
    }

    if (!CryptHashData(hHash, (PBYTE)wszPassword, cbPassword, 0))
    {
        printf("Error %x during CryptHashData!\n", GetLastError());
        goto Cleanup;
    }

    if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, CRYPT_EXPORTABLE, &hKey))
    {
        printf("Error %x during CryptDeriveKey!\n", GetLastError());
        goto Cleanup;
    }

    if (!GetExportedKey(hKey, PLAINTEXTKEYBLOB, &keyBlob, &keyBlobLength))
    {
        printf("Error %x during GetExportedKey!\n", GetLastError());
        goto Cleanup;
    }

    while (1)
    {
        // PLAINTEXTKEYBLOB: BLOBHEADER|DWORD key length|Key material|
        DWORD keyMaterialLength;
        LPBYTE keyMaterial;

        keyMaterialLength = *(DWORD*)(keyBlob + sizeof(BLOBHEADER));
        keyMaterial = (keyBlob + sizeof(BLOBHEADER) + sizeof(DWORD));

        if (!CryptBinaryToStringA(keyMaterial, keyMaterialLength, CRYPT_STRING_BASE64, keyBlobBase64, &base64Length))
        {
            printf("Error %x during GetExportedKey!\n", GetLastError());
            goto Cleanup;
        }

        if (keyBlobBase64)
        {
            printf("%d-bit key blob: %s\n", keyMaterialLength * 8, keyBlobBase64);
            break;
        }
        else
        {
            keyBlobBase64 = malloc(base64Length);
        }
    }

    for (DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ", rgData[i]);
    }
    printf("\n");

    if (!CryptEncrypt(
        hKey,
        0,
        TRUE,
        0,
        rgData,
        &dwCount,
        sizeof(rgData)))
    {
        printf("Error %x during CryptEncrypt!\n", GetLastError());
        goto Cleanup;
    }

    for (DWORD i = 0; i < dwCount; i++)
    {
        printf("%d ", rgData[i]);
    }
    printf("\n");

Cleanup:
    free(keyBlob);
    free(keyBlobBase64);
    if (hKey)
    {
        CryptDestroyKey(hKey);
    }
    if (hHash)
    {
        CryptDestroyHash(hHash);
    }
    if (hProv)
    {
        CryptReleaseContext(hProv, 0);
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 2015-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多