首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

CryptoAPI 導出會話密鑰純文本值:私鑰解密 SIMPLEBLOB 技巧

     編輯:VC 教學

CAPI 中 CryptExportKey 導出會話密鑰時,輸出為 **SIMPLEBLOB** 格式:前 12 字節為 BLOBHEADER + ALG_ID,後接 **用公鑰加密的會話密鑰明文**。若需獲取會話密鑰**原始字節值** (如跨進程共享、調試、非標準協議),可用**對應私鑰解密**該 Blob 尾部密文。本文給出最小化代碼流程。

SIMPLEBLOB 結構

+------------------+------------------+------------------------+
| BLOBHEADER (8B)  | ALG_ID (4B)      | Encrypted Key Material |
| (type, version,  | (CALG_3DES 等)   | (RSA 公鑰加密)         |
|  reserved)       |                  |                        |
+------------------+------------------+------------------------+
  12 字節固定頭部                    可變長 (等於 RSA 密鑰長度/8)

核心步驟

  1. 生成可導出會話密鑰 (CRYPT_EXPORTABLE);
  2. 獲取密鑰交換私鑰句柄 (AT_KEYEXCHANGE);
  3. CryptExportKey(hSession, hPubKey, SIMPLEBLOB, 0, ...) 導出 Blob;
  4. 跳過前 12 字節頭部,取加密部分;
  5. CryptDecrypt(hPrivKey, 0, TRUE, 0, encryptedPart, &len) 解密得明文會話密鑰;
  6. 明文長度 = 算法密鑰長度 (如 3DES 168bit=21B, AES-256 32B)。

完整可編譯代碼 (VC6/VC2005+ 驗證)


#define _WIN32_WINNT 0x0500  // 至少 Win2000
#include 
#include 
#include 
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "advapi32.lib")

int main() {
    HCRYPTPROV hProv = 0;
    HCRYPTKEY hSessionKey = 0, hExchKey = 0;
    BYTE *pBlob = NULL, *pEncKey = NULL, *pKeyMat = NULL;
    DWORD blobLen = 0, keyMatLen = 0;

    __try {
        // 1. 獲取 CSP (機器級容器,持久化)
        if (!CryptAcquireContext(&hProv, L"TestContainer", MS_ENHANCED_PROV,
                                 PROV_RSA_FULL, CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) {
            if (GetLastError() == NTE_EXISTS)
                CryptAcquireContext(&hProv, L"TestContainer", MS_ENHANCED_PROV,
                                    PROV_RSA_FULL, CRYPT_MACHINE_KEYSET);
            else __leave;
        }

        // 2. 生成可導出 3DES 會話密鑰
        if (!CryptGenKey(hProv, CALG_3DES, CRYPT_EXPORTABLE, &hSessionKey)) __leave;

        // 3. 獲取密鑰交換公/私鑰對
        if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hExchKey)) __leave;

        // 4. 導出 SIMPLEBLOB (用公鑰加密會話密鑰)
        if (!CryptExportKey(hSessionKey, hExchKey, SIMPLEBLOB, 0, NULL, &blobLen)) __leave;
        pBlob = new BYTE[blobLen];
        if (!CryptExportKey(hSessionKey, hExchKey, SIMPLEBLOB, 0, pBlob, &blobLen)) __leave;

        // 5. 去除 12 字節頭部
        DWORD encKeyLen = blobLen - 12;
        pEncKey = new BYTE[encKeyLen];
        memcpy(pEncKey, pBlob + 12, encKeyLen);

        // 6. 用私鑰解密得會話密鑰明文
        keyMatLen = encKeyLen;  // 解密後長度同密文 (RSA 無填充模式)
        pKeyMat = new BYTE[keyMatLen];
        memcpy(pKeyMat, pEncKey, keyMatLen);
        if (!CryptDecrypt(hExchKey, 0, TRUE, 0, pKeyMat, &keyMatLen)) __leave;

        // 7. 輸出十六進制明文
        std::cout << "Session Key (" << keyMatLen << " bytes): ";
        for (DWORD i = 0; i < keyMatLen; ++i)
            printf("%02X", pKeyMat[i]);
        std::cout << std::endl;

    }
    __finally {
        if (pKeyMat) delete[] pKeyMat;
        if (pEncKey) delete[] pEncKey;
        if (pBlob) delete[] pBlob;
        if (hSessionKey) CryptDestroyKey(hSessionKey);
        if (hExchKey) CryptDestroyKey(hExchKey);
        if (hProv) CryptReleaseContext(hProv, 0);
    }
    return 0;
}

關鍵編譯設置

  • 預處理器定義:_WIN32_WINNT=0x0500 (Win2000+) 或 0x0400 (NT4);
  • 鏈接庫:crypt32.lib、advapi32.lib;
  • 字符集:Unicode (寬字符容器名) 或多字節均可。

安全警示

  • 🔴 **會話密鑰明文極其敏感** — 僅用於調試/教學/極受控環境,**生產代碼嚴禁導出明文**;
  • 🔴 內存中明文使用後**立即 `SecureZeroMemory` 擦除**;
  • 🔴 容器 CRYPT_MACHINE_KEYSET 需管理員權限,用戶級用 0;
  • 🔴 3DES 已棄用,**實際項目請用 AES-256** (CALG_AES_256);
  • 🟢 現代 CNG (BCryptExportKey/BCryptDecrypt) 流程類似,算法標識改為 BCRYPT_AES_ALGORITHM 等。

理解 SIMPLEBLOB 結構與私鑰解密邏輯,有助於調試密鑰交換協議、實現自定義密鑰包裝格式、對接非標準加密庫。實際開發中**堅持「密鑰不落地、不明文在內存停留」** 原則。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved