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

Windows CryptoAPI 數據加密實戰:會話密鑰、公私鑰對、哈希、數字簽名全流程

     編輯:VC 教學

Microsoft CryptoAPI (CAPI) 是 Windows 內建的加密服務接口,通過 **CSP (Cryptographic Service Provider)** 提供算法實現。本文系統講解 CAPI 核心對象模型、會話密鑰生成/導入導出/加密解密、公私鑰對管理、哈希計算、數字簽名驗證、密鑰存儲持久化,附完整文件加密解密示例代碼。

CAPI 核心對象與句柄

句柄類型代表獲取方式
HCRYPTPROVCSP 上下文 (提供者容器)CryptAcquireContext
HCRYPTKEY密鑰對象 (會話/公鑰/私鑰)CryptGenKey/CryptGetUserKey/CryptImportKey
HCRYPTHASH哈希對象CryptCreateHash

提供者與算法常量


// 提供者
#define MS_ENHANCED_PROV  "Microsoft Enhanced Cryptographic Provider v1.0"
#define MS_STRONG_PROV    "Microsoft Strong Cryptographic Provider"  // 高強度
#define PROV_RSA_FULL     1   // RSA 全功能
#define PROV_RSA_AES      24  // RSA + AES

// 常用算法 ID (ALG_ID)
#define CALG_3DES         0x00006603  // 3DES 對稱加密
#define CALG_AES_256      0x00006610  // AES-256
#define CALG_RSA_KEYX     0x0000a400  // RSA 密鑰交換
#define CALG_RSA_SIGN     0x0000a401  // RSA 簽名
#define CALG_SHA1         0x00008004  // SHA-1 (已弱)
#define CALG_SHA_256      0x0000800c  // SHA-256
#define CALG_MD5          0x00008003  // MD5 (不可用於安全)

會話密鑰全生命週期


// 1. 獲取 CSP 句柄
HCRYPTPROV hProv;
CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);  // 無持久化容器

// 2. 生成會話密鑰 (如 AES-256)
HCRYPTKEY hSessionKey;
CryptGenKey(hProv, CALG_AES_256, CRYPT_EXPORTABLE, &hSessionKey);

// 3. 導出會話密鑰 (用公鑰加密,傳給對方)
// 3.1 獲取對方公鑰句柄 (或從證書導入)
HCRYPTKEY hPubKey;  // 已有

// 3.2 導出為 SIMPLEBLOB (含加密的會話密鑰)
DWORD blobLen = 0;
CryptExportKey(hSessionKey, hPubKey, SIMPLEBLOB, 0, NULL, &blobLen);
BYTE* blob = new BYTE[blobLen];
CryptExportKey(hSessionKey, hPubKey, SIMPLEBLOB, 0, blob, &blobLen);
// 發送 blob 給對方...

// 4. 對方導入會話密鑰 (用私鑰解密)
HCRYPTKEY hImportedSessionKey;
CryptImportKey(hProv, blob, blobLen, hPrivKey, 0, &hImportedSessionKey);

// 5. 加密數據
BYTE* data = ...; DWORD dataLen = ...;
DWORD bufLen = dataLen + 16;  // 預留填充
BYTE* buf = new BYTE[bufLen];
memcpy(buf, data, dataLen);
CryptEncrypt(hImportedSessionKey, 0, TRUE, 0, buf, &dataLen, bufLen);
// buf 前 dataLen 字節為密文

// 6. 解密數據
CryptDecrypt(hImportedSessionKey, 0, TRUE, 0, buf, &dataLen);
// buf 前 dataLen 字節為明文

// 7. 清理
CryptDestroyKey(hSessionKey);
CryptDestroyKey(hImportedSessionKey);
CryptReleaseContext(hProv, 0);
delete[] blob; delete[] buf;

公私鑰對持久化容器管理


// 創建/獲取持久化密鑰容器
CryptAcquireContext(&hProv, L"MyAppContainer", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET);  // 首次創建
// 後續打開
CryptAcquireContext(&hProv, L"MyAppContainer", MS_ENHANCED_PROV, PROV_RSA_FULL, 0);

// 獲取密鑰對句柄
HCRYPTKEY hKeyEx, hSign;
CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKeyEx);   // 密鑰交換對
CryptGetUserKey(hProv, AT_SIGNATURE, &hSign);      // 簽名對

// 若不存在則生成
if (!hKeyEx) CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKeyEx);
if (!hSign)  CryptGenKey(hProv, AT_SIGNATURE, 0, &hSign);

哈希與數字簽名


// 計算文件 SHA-256 哈希
HCRYPTHASH hHash;
CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
while (ReadFile(hFile, buf, sizeof(buf), &read, NULL) && read) {
    CryptHashData(hHash, buf, read, 0);
}
BYTE hashVal[32]; DWORD hashLen = 32;
CryptGetHashParam(hHash, HP_HASHVAL, hashVal, &hashLen, 0);
CryptDestroyHash(hHash);

// 數字簽名 (用私鑰簽名哈希)
CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
CryptHashData(hHash, data, dataLen, 0);
DWORD sigLen = 0;
CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &sigLen);
BYTE* sig = new BYTE[sigLen];
CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, sig, &sigLen);
CryptDestroyHash(hHash);

// 驗證簽名 (用公鑰)
CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
CryptHashData(hHash, data, dataLen, 0);
BOOL ok = CryptVerifySignature(hHash, sig, sigLen, hPubKey, NULL, 0);
CryptDestroyHash(hHash);

完整文件加密解密示例 (AES-256 + 密碼派生)


// 加密文件
bool EncryptFile(const wstring& inPath, const wstring& outPath, const wstring& password) {
    HCRYPTPROV hProv; CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
    HCRYPTHASH hHash; CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
    CryptHashData(hHash, (BYTE*)password.c_str(), password.size()*2, 0);  // 簡易密碼哈希
    HCRYPTKEY hKey; CryptDeriveKey(hProv, CALG_AES_256, hHash, CRYPT_EXPORTABLE, &hKey);
    CryptDestroyHash(hHash);

    HANDLE hIn = CreateFile(inPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    HANDLE hOut = CreateFile(outPath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    BYTE buf[4096]; DWORD read, written;
    bool first = true, last = false;
    while (ReadFile(hIn, buf, sizeof(buf), &read, NULL) && read) {
        if (!ReadFile(hIn, NULL, 0, &read, NULL)) last = true;  // 檢查是否最後一塊
        DWORD len = read;
        CryptEncrypt(hKey, 0, last, 0, buf, &len, sizeof(buf));
        WriteFile(hOut, buf, len, &written, NULL);
    }
    CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0);
    CloseHandle(hIn); CloseHandle(hOut);
    return true;
}

// 解密文件 (對稱操作,將 CryptEncrypt 換為 CryptDecrypt)

避坑指南與現代替代

陷阱說明對策
CRYPT_VERIFYCONTEXT 容器不持久化密鑰生成後進程結束即丟失持久化需指定容器名 + 刪除 CRYPT_VERIFYCONTEXT
會話密鑰未導出屬性無法 CryptExportKeyCryptGenKey 時加 CRYPT_EXPORTABLE
緩衝區大小不足CryptEncrypt 可能擴充 (填充/分組)分配 dataLen + blockSize,分塊處理大文件
密碼直接做鑰材極不安全,無鹽、無迭代用 CryptDeriveKey + PBKDF2 (CAPI 2.0+) 或改用 CNG
SHA-1/MD5/3DES 已弱不符合合規要求強制 AES-256 + SHA-256/384/512

現代替代:CNG (Cryptography Next Generation)

  • Windows Vista+ 內建,API 以 `BCrypt*`/`NCrypt*` 開頭;
  • 支持 AES-GCM、ECDSA、ECDH、SHA-3、PBKDF2、HKDF 等現代算法;
  • 併發友好、可遷移性更強、符合 FIPS 140-2/3;
  • **新項目首選 CNG**,CAPI 僅維舊項目兼容。

CryptoAPI 是 Windows 系統級加密基石。掌握「容器→提供者→密鑰→哈希/加密/簽名」對象鏈、Blob 格式導入導出、密鑰派生與持久化,能在無第三方依賴下構建合規的數據保護方案。

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