CryptoAPI 的 CryptExportKey / CryptImportKey 要求以另一把金鑰加密/解密 Session Key,無法直接匯出明文金鑰。MSDN 範例使用私鑰解密,流程冗長。本文提供更簡潔高效的方法:利用金鑰交換對的私鑰直接解密 SIMPLEBLOB 取得明文 Session Key。
_WIN32_WINNT=0x0500 (Win2K) 或 0x0400 (NT4)、_CRYPT32_crypt32.libCryptAcquireContext 取得 CSP Handle (建立/使用金鑰容器)CryptGenKey 產生可匯出的 3DES Session Key (CALG_3DES | CRYPT_EXPORTABLE)CryptGetUserKey 取得金鑰交換對 Handle (AT_KEYEXCHANGE)CryptExportKey 以交換對公鑰加密 Session Key 為 SIMPLEBLOBCryptDecrypt 以交換對私鑰解密,獲得明文 Key Material
#include
#include
#include
#define KEY_PAIR_SIZE (dwSize - 12)
#define SESSION_KEY_SIZE dwKeyMaterial
void main() {
HCRYPTPROV hProv = 0;
HCRYPTKEY hExchangeKeyPair = 0;
HCRYPTKEY hSessionKey = 0;
BYTE *pbKeyMaterial = NULL;
DWORD dwKeyMaterial;
BYTE *pbExportedKeyBlob = NULL;
BYTE *pbEncryptedKey = NULL;
DWORD dwSize;
unsigned int c;
__try {
// 1. 取得 CSP
if (!CryptAcquireContext(&hProv, "Container Name", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
__leave;
// 2. 產生 Session Key
if (!CryptGenKey(hProv, CALG_3DES, CRYPT_EXPORTABLE, &hSessionKey))
__leave;
// 3. 取得金鑰交換對
if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hExchangeKeyPair))
__leave;
// 4. 匯出加密後的 Session Key (SIMPLEBLOB)
if (!CryptExportKey(hSessionKey, hExchangeKeyPair, SIMPLEBLOB, 0, NULL, &dwSize))
__leave;
pbExportedKeyBlob = new BYTE[dwSize];
if (!CryptExportKey(hSessionKey, hExchangeKeyPair, SIMPLEBLOB, 0, pbExportedKeyBlob, &dwSize))
__leave;
// 5. 移除 12 字節 Header
pbEncryptedKey = new BYTE[KEY_PAIR_SIZE];
for (c = 0; c < KEY_PAIR_SIZE; c++)
pbEncryptedKey[c] = pbExportedKeyBlob[c + 12];
// 6. 以私鑰解密取得明文 Key Material
if (!CryptDecrypt(hExchangeKeyPair, 0, TRUE, 0, pbEncryptedKey, &dwKeyMaterial))
__leave;
pbKeyMaterial = new BYTE[SESSION_KEY_SIZE];
for (c = 0; c < SESSION_KEY_SIZE; c++)
pbKeyMaterial[c] = pbEncryptedKey[c];
// 此時 pbKeyMaterial 即為純文字 Session Key
}
__finally {
if (pbKeyMaterial) LocalFree(pbKeyMaterial);
if (hSessionKey) CryptDestroyKey(hSessionKey);
if (hExchangeKeyPair) CryptDestroyKey(hExchangeKeyPair);
if (hProv) CryptReleaseContext(hProv, 0);
}
}
CryptDecrypt 最後參數 pdwDataLen 為輸入緩衝區大小,呼叫後回傳解密後資料長度。__finally 區塊,確保異常時也能釋放。