我想使用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;
}
答案 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,则密钥的派生方式如下:
- 通过重复常量
0x36
64次来形成64字节缓冲区。设 k 为输入参数hBaseData
表示的哈希值的长度。将缓冲区的第一个 k 字节设置为缓冲区的第一个 k 字节的XOR运算结果,其中哈希值由输入参数{{表示1}}。- 通过重复常量
表示hBaseData
64次来形成64字节缓冲区。将缓冲区的第一个 k 字节设置为缓冲区的第一个 k 字节的 XOR 操作的结果,其中哈希值为由输入参数0x5C
。- 使用与用于计算
hBaseData
参数表示的哈希值相同的哈希算法来哈希步骤1的结果。- 使用与用于计算
hBaseData
参数表示的哈希值相同的哈希算法来哈希步骤2的结果。- 将步骤3的结果与步骤4的结果连接起来。
- 使用步骤5结果的第一个 n 字节作为派生密钥。
醇>
答案 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