BCryptDecrypt(CNG)API无法正确解密原CryptDecrypt加密数据
问题分析与修复方案
你的代码存在多个关键逻辑错误,导致解密结果乱码,以下是具体问题和修复方法:
1. IV 使用错误
解密必须使用加密时的同一初始向量(IV),但你当前代码调用BCryptGenRandom生成了新的IV,这会直接导致解密失败。通常加密流程中会将IV附加在密文头部,解密时需要先从密文中提取原始IV,而不是重新生成。
2. 链模式设置时机错误
BCryptSetProperty设置BCRYPT_CHAINING_MODE必须在创建密钥(BCryptGenerateSymmetricKey或BCryptImportKey)之前执行,因为密钥对象会绑定算法的链模式参数。你当前是先创建密钥再设置链模式,导致CBC模式并未实际生效。
3. 密钥处理逻辑混乱
代码中同时调用了BCryptGenerateSymmetricKey和BCryptImportKey,且BCryptImportKey的参数pbKeyObject、cbKeyObject均为0,这会导致密钥状态异常。如果你的原始密钥是字节数组,直接使用BCryptGenerateSymmetricKey即可,无需额外调用BCryptImportKey。
4. 未明确指定填充模式
CryptDecrypt默认使用PKCS#7填充,而BCryptDecrypt需要显式指定填充模式(除非加密时未使用填充)。你需要在算法上下文设置填充属性,或在BCryptDecrypt的最后一个参数传入BCRYPT_PADDING_PKCS7。
5. 其他代码问题
- 未定义变量
_Data、Result,属于逻辑漏洞 wprinft拼写错误,应为wprintf- 未清理
hKey、m_Context、pbIV等资源,会导致内存泄漏
修正后的示例代码
#include <windows.h> #include <bcrypt.h> #include <vector> #pragma comment(lib, "bcrypt.lib") bool decrypt(PBYTE pbCipherText, DWORD cbCipherText, const BYTE* KeyData, DWORD KeySize, std::vector<BYTE>& outPlainText) { BCRYPT_ALG_HANDLE hAlg = NULL; BCRYPT_KEY_HANDLE hKey = NULL; PBYTE pbIV = nullptr; DWORD cbBlockLen = 0; DWORD cbData = 0; NTSTATUS status = 0; bool ret = false; // 1. 打开算法提供者,先设置链模式和填充模式 if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0))) { wprintf(L"BCryptOpenAlgorithmProvider failed: 0x%x\n", status); goto Cleanup; } // 设置CBC链模式(必须在创建密钥前) if (!NT_SUCCESS(status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0))) { wprintf(L"BCryptSetProperty(chain mode) failed: 0x%x\n", status); goto Cleanup; } // 设置PKCS#7填充模式(匹配CryptDecrypt默认行为) if (!NT_SUCCESS(status = BCryptSetProperty(hAlg, BCRYPT_PADDING_SCHEMES, (PBYTE)BCRYPT_PADDING_PKCS7, sizeof(BCRYPT_PADDING_PKCS7), 0))) { wprintf(L"BCryptSetProperty(padding) failed: 0x%x\n", status); goto Cleanup; } // 获取块长度,用于IV处理 if (!NT_SUCCESS(status = BCryptGetProperty(hAlg, BCRYPT_BLOCK_LENGTH, (PBYTE)&cbBlockLen, sizeof(DWORD), &cbData, 0))) { wprintf(L"BCryptGetProperty(block length) failed: 0x%x\n", status); goto Cleanup; } // 检查密文长度:至少包含IV + 加密数据 if (cbCipherText < cbBlockLen) { wprintf(L"Invalid ciphertext length\n"); goto Cleanup; } // 提取加密时使用的IV(假设IV存储在密文头部) pbIV = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlockLen); if (!pbIV) { wprintf(L"HeapAlloc for IV failed\n"); goto Cleanup; } memcpy(pbIV, pbCipherText, cbBlockLen); // 密文主体是IV之后的部分 PBYTE pbActualCipherText = pbCipherText + cbBlockLen; DWORD cbActualCipherText = cbCipherText - cbBlockLen; // 2. 生成对称密钥 if (!NT_SUCCESS(status = BCryptGenerateSymmetricKey(hAlg, &hKey, nullptr, 0, (PBYTE)KeyData, KeySize, 0))) { wprintf(L"BCryptGenerateSymmetricKey failed: 0x%x\n", status); goto Cleanup; } // 3. 获取明文所需长度 DWORD cbPlainText = 0; if (!NT_SUCCESS(status = BCryptDecrypt(hKey, pbActualCipherText, cbActualCipherText, NULL, pbIV, cbBlockLen, NULL, 0, &cbPlainText, BCRYPT_PADDING_PKCS7))) { wprintf(L"BCryptDecrypt(get length) failed: 0x%x\n", status); goto Cleanup; } // 分配明文内存 outPlainText.resize(cbPlainText); if (!NT_SUCCESS(status = BCryptDecrypt(hKey, pbActualCipherText, cbActualCipherText, NULL, pbIV, cbBlockLen, outPlainText.data(), cbPlainText, &cbPlainText, BCRYPT_PADDING_PKCS7))) { wprintf(L"BCryptDecrypt failed: 0x%x\n", status); goto Cleanup; } ret = true; Cleanup: // 清理资源 if (hKey) BCryptDestroyKey(hKey); if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0); if (pbIV) HeapFree(GetProcessHeap(), 0, pbIV); return ret; }
关键说明
- 确保加密时将IV附加在密文头部,解密时先提取该IV
- 链模式和填充模式必须与
CryptDecrypt的加密参数完全匹配 - 所有资源必须在退出前清理,避免内存泄漏
内容的提问来源于stack exchange,提问作者RituV
相关产品推荐
相关产品推荐

