如何导出使用CryptoAPI派生的AES密钥

时间:2015-04-12 04:56:27

标签: c encryption cryptography aes cryptoapi

我想使用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;
}

2 个答案:

答案 0 :(得分:2)

编辑:如果密钥派生在软件中,那么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 字节的XOR运算结果,其中哈希值由输入参数{{表示1}}。
  2.   
  3. 通过重复常量hBaseData 64次来形成64字节缓冲区。将缓冲区的第一个 k 字节设置为缓冲区的第一个 k 字节的 XOR 操作的结果,其中哈希值为由输入参数0x5C
  4. 表示   
  5. 使用与用于计算hBaseData参数表示的哈希值相同的哈希算法来哈希步骤1的结果。
  6.   
  7. 使用与用于计算hBaseData参数表示的哈希值相同的哈希算法来哈希步骤2的结果。
  8.   
  9. 将步骤3的结果与步骤4的结果连接起来。
  10.   
  11. 使用步骤5结果的第一个 n 字节作为派生密钥。
  12.   

答案 1 :(得分:2)

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

如果您使用的第三方CSP不允许密钥导出,即使使用CRYPT_EXPORTABLE标志,也会出现例外情况,在这种情况下,请参阅Maarten Bodewes' answer重播密钥派生在CSP之外自行完成。

如果您遵循CryptExportKey MSDN link,您会看到一个示例函数,显示如何使用该函数在平面中导出它们的关键材质。如果需要,还可以使用另一个密钥(即由另一个密钥加密)导出密钥 wrapped ,方法是将密钥句柄传递给hExpKey(第二个参数)到{{1 }}。将CryptExportKey传递给此参数将改为以普通方式导出。

我已使用上面MSDN链接中的示例代码更新了您的示例程序(如下)以导出密钥,并使用CryptBinaryToString函数将密钥材料打印为base64字符串。我已经导出为PLAINTEXTKEYBLOB blob类型,它使用数据结构NULL所以还有一些工作要做,以提取我们感兴趣的原始密钥材料那个blob:

BLOBHEADER|key length|key material