You have a symmetric key stored in a CryptoAPI key object that you want to use with another API, such as OpenSSL.
The Microsoft CryptoAPI is designed to prevent unintentional disclosure of sensitive key information. To do this, key information is stored in opaque data objects by the Cryptographic Service Provider (CSP) used to create the key object. Key data is exportable from key objects, but the data must be encrypted with another key to prevent accidental disclosure of the raw key data.
To extract the raw key data from a CryptoAPI key, you must first
export the key using the CryptoAPI function CryptoExportKey(
)
. The key data obtained from this function
will be encrypted with another key, which you can then use to decrypt
the encrypted key data to obtain the raw key data that another API,
such as OpenSSL, can use.
To export a key using the CryptoExportKey( )
function, you must provide the function with another key that will be
used to encrypt the key data that's to be exported.
Recipe 5.26 includes a function, SpcGetExportableContext(
)
, that obtains a handle to a CSP context object suitable
for exporting keys created with it. The CSP context object uses a
"container" to store public key
pairs. Every public key container can have a special public key pair
known as an exchange key, which is the key that
we'll use to decrypt the exported key data.
The function CryptGetUserKey(
)
is used to obtain the exchange key. If it
doesn't exist, SpcExportKeyData(
)
, listed later in this section, will create a
1,024-bit exchange key, which will be stored as the exchange key in
the public key container so future attempts to get the key will
succeed. The special algorithm identifier
AT_KEYEXCHANGE
is used to reference the exchange
key.
Symmetric keys are always exported via CryptExportKey(
)
in "simple
blob" format, specified by the
SIMPLEBLOB
constant passed to
CryptExportKey( )
. The data returned in the buffer
from CryptExportKey( )
will have a
BLOBHEADER
structure, followed by an
ALG_ID
for the algorithm used to encrypt the key
data. The raw key data will follow the BLOBHEADER
and ALG_ID
header information. For extracting the
raw key data from a CryptoAPI key, the data in the
BLOBHEADER
structure and the
ALG_ID
are of no interest, but you must be aware
of their existence so that you can skip over them to find the
encrypted key data.
Finally, the encrypted key data can be decrypted using
CryptDecrypt( )
and the exchange key. The
CryptDecrypt( )
function is described in more
detail in Recipe 5.25. The decrypted data is the raw key data that
can now be passed off to other APIs or used in protocols that already
provide their own protection for the key. The return from
SpcExportKeyData( )
will be a buffer allocated
with LocalAlloc( )
that contains the unencrypted symmetric key
if no errors occur; otherwise, NULL
will be
returned.
#include <windows.h> #include <wincrypt.h> BYTE *SpcExportKeyData(HCRYPTPROV hProvider, HCRYPTKEY hKey, DWORD *cbData) { BOOL bResult = FALSE; BYTE *pbData = 0, *pbKeyData; HCRYPTKEY hExpKey = 0; if (!CryptGetUserKey(hProvider, AT_KEYEXCHANGE, &hExpKey)) { if (GetLastError( ) != NTE_NO_KEY) goto done; if (!CryptGenKey(hProvider, AT_KEYEXCHANGE, (1024 << 16), &hExpKey)) goto done; } if (!CryptExportKey(hKey, hExpKey, SIMPLEBLOB, 0, 0, cbData)) goto done; if (!(pbData = (BYTE *)LocbalAlloc(LMEM_FIXED, *cbData))) goto done; if (!CryptExportKey(hKey, hExpKey, SIMPLEBLOB, 0, pbData, cbData)) goto done; pbKeyData = pbData + sizeof(BLOBHEADER) + sizeof(ALG_ID); (*cbData) -= (sizeof(BLOBHEADER) + sizeof(ALG_ID)); bResult = CryptDecrypt(hExpKey, 0, TRUE, 0, pbKeyData, cbData); done: if (hExpKey) CryptDestroyKey(hExpKey); if (!bResult && pbData) LocalFree(pbData); else if (pbData) MoveMemory(pbData, pbKeyData, *cbData); return (bResult ? (BYTE *)LocalReAlloc(pbData, *cbData, 0) : 0); }