I want to use the Windows CryptoAPI functions for AES encryption.
As you know, the API has a lot of functions to create, hash and change the entered key; it derives the key and you get a handle to it.
My problem is that I want to know what the derived key is?
#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;
}
EDIT: if the key derivation is in software then the answer of softwariness is probably better. So that's a more generic answer and should be preferred. You can use this replay method if the method of softwariness fails. This could be the case if the token does not allow plaintext export.
In general these method have been created in such a way that retrieving the resulting secret is hard if not impossible. However, the method to derive the key is described in the Remarks section of the API documentation of CryptDeriveKey
. So you can replay the creation of you have the base data.
The API doesn't describe what happens if SHA-2 is used, but I presume it just uses the leftmost bits of the SHA-256 result for the key.
After derivation you can of course test by encrypting/decrypting or some data.
Let n be the required derived key length, in bytes. The derived key is the first n bytes of the hash value after the hash computation has been completed by
CryptDeriveKey
. If the hash is not a member of the SHA-2 family and the required key is for either 3DES or AES, the key is derived as follows:
- Form a 64-byte buffer by repeating the constant
0x36
64 times. Let k be the length of the hash value that is represented by the input parameterhBaseData
. Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value that is represented by the input parameterhBaseData
.- Form a 64-byte buffer by repeating the constant
0x5C
64 times. Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value that is represented by the input parameterhBaseData
.- Hash the result of step 1 by using the same hash algorithm as that used to compute the hash value that is represented by the
hBaseData
parameter.- Hash the result of step 2 by using the same hash algorithm as that used to compute the hash value that is represented by the
hBaseData
parameter.- Concatenate the result of step 3 with the result of step 4.
- Use the first n bytes of the result of step 5 as the derived key.
As you have made your derived key exportable by passing CRYPT_EXPORTABLE
to the CryptDeriveKey
function, it is possible to use the CryptExportKey
function to export the derived key material.
The exception to this would be if you are using a third-party CSP which does not permit key export, even with the CRYPT_EXPORTABLE
flag, in which case see Maarten Bodewes' answer to replay the key derivation steps yourself outside the CSP.
If you follow that CryptExportKey
MSDN link you'll see there's an example function showing how to use that function to export they key material in the plain. It is also possible to export the key wrapped using another key (i.e. encrypted by another key) if you want, by passing a key handle to the hExpKey
(second parameter) to CryptExportKey
. Passing NULL
to this parameter exports in the plain instead.
I've updated your example program (below) to export the key using the example code from the MSDN link above, and to use the CryptBinaryToString
function to print the key material as a base64 string. I've exported as the PLAINTEXTKEYBLOB
blob-type, which uses the data structure BLOBHEADER|key length|key material
so there is also a little work to be done to pull out the raw key material that we're interested in out of that 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;
}
来源:https://stackoverflow.com/questions/29586097/how-to-export-aes-key-derived-using-cryptoapi