You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CryptoAPI的KP_IV加密后解密异常,如何随密文发送IV?

原错误代码

#include <iostream>
#include <windows.h>
#include <wincrypt.h>
using std::string;
const char *key = "yM7TbMuL3TNUmeBs1Mqdw73G2BmMBHHV7cDAF3V5v9FAOqYc0mF133T8WE5foqAG3rJAK8dU1hKc";
HCRYPTPROV hCryptProv = NULL; 
HCRYPTKEY hKey = NULL;
#define AES_BLOCK_SIZE 16

BOOL AesInitialization()
{
// Set hCryptProv and hKey
HCRYPTHASH hHash = NULL;
BOOL result = FALSE;
if (CryptAcquireContextW(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
    if (CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash))
        if (CryptHashData(hHash, (BYTE*)key, strlen(key), 0))
            if (CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey))
                {
                DWORD dwMode = CRYPT_MODE_CBC;
                result = CryptSetKeyParam(hKey, KP_MODE, (BYTE *)&dwMode, 0);
                }

if (hHash != NULL) 
    CryptDestroyHash(hHash);

return result;
}

BOOL AesEncrypt(string &data)
{
BOOL result = TRUE;
string iv;
iv.resize(AES_BLOCK_SIZE);  // allocate space for random IV

result = CryptGenRandom(hCryptProv, AES_BLOCK_SIZE, (BYTE *)&iv[0]);
if (result) 
    {
    if (CryptSetKeyParam(hKey, KP_IV, (BYTE *) iv.c_str(), 0))            
        {
        data = data + iv;       // Append the random string
        DWORD dwDataLen = data.length();
        data.resize(dwDataLen + AES_BLOCK_SIZE); // give enough space for padding
        result = CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE *)&data[0], &dwDataLen, data.length());
        data.resize(dwDataLen);  // resize to actual ciphertext length
        }
    }
return result;
}

BOOL AesDecrypt(string &data)
{
BOOL success = TRUE;

string iv;
iv.resize(AES_BLOCK_SIZE);  // ensure IV has the correct size
iv.assign(key);
if (CryptSetKeyParam(hKey, KP_IV, (BYTE*)iv.c_str(), 0))
    {
    if (CryptSetKeyParam(hKey, KP_IV, (BYTE*)&iv[0], 0))
        {
        DWORD dwDataLen = data.length();
        success = CryptDecrypt(hKey, NULL, TRUE, 0, (BYTE *)&data[0], &dwDataLen);
        data.resize(dwDataLen - AES_BLOCK_SIZE);  // resize to actual ciphertext length
        }
    }
return success;
}

int main()
{
if (!AesInitialization()) 
    return 1;

string message = "my mother & your mother were hanging up clothes";
string iv;

if (!AesEncrypt(message))
    return 1;

if (!AesDecrypt(message)) 
    return 1;

return 0;
}

错误分析

  1. 加密逻辑错误:将明文与IV拼接后再加密,导致IV被当作明文的一部分参与加密,而非作为CBC模式的初始化向量使用
  2. 解密逻辑错误:未从密文中提取加密时使用的随机IV,反而用密钥作为IV,完全不符合CBC模式的解密要求

修正方案

核心思路

  • 加密时:先生成随机IV,用该IV加密明文,再将IV附加到密文末尾(或开头),形成最终传输数据
  • 解密时:先从传输数据中提取IV,再用该IV解密剩余的密文内容

修正后的完整代码

#include <iostream>
#include <windows.h>
#include <wincrypt.h>
using std::string;
const char *key = "yM7TbMuL3TNUmeBs1Mqdw73G2BmMBHHV7cDAF3V5v9FAOqYc0mF133T8WE5foqAG3rJAK8dU1hKc";
HCRYPTPROV hCryptProv = NULL; 
HCRYPTKEY hKey = NULL;
#define AES_BLOCK_SIZE 16

BOOL AesInitialization()
{
    HCRYPTHASH hHash = NULL;
    BOOL result = FALSE;
    if (CryptAcquireContextW(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
        if (CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash))
            if (CryptHashData(hHash, (BYTE*)key, strlen(key), 0))
                if (CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey))
                {
                    DWORD dwMode = CRYPT_MODE_CBC;
                    result = CryptSetKeyParam(hKey, KP_MODE, (BYTE *)&dwMode, 0);
                }

    if (hHash != NULL) 
        CryptDestroyHash(hHash);
    return result;
}

BOOL AesEncrypt(string &data)
{
    BOOL result = TRUE;
    string iv(AES_BLOCK_SIZE, 0);
    
    // 生成随机IV
    if (!CryptGenRandom(hCryptProv, AES_BLOCK_SIZE, (BYTE*)&iv[0]))
        return FALSE;

    // 设置密钥的IV参数
    if (!CryptSetKeyParam(hKey, KP_IV, (BYTE*)iv.c_str(), 0))
        return FALSE;

    // 保存原始明文长度,准备加密
    DWORD dwDataLen = data.length();
    DWORD dwBufferLen = dwDataLen + AES_BLOCK_SIZE; // 预留填充空间
    data.resize(dwBufferLen);

    // 加密明文
    if (!CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE*)&data[0], &dwDataLen, dwBufferLen))
        return FALSE;
    data.resize(dwDataLen);

    // 将IV附加到密文末尾,一起传输
    data += iv;
    return TRUE;
}

BOOL AesDecrypt(string &data)
{
    // 检查数据长度至少包含IV和密文
    if (data.length() <= AES_BLOCK_SIZE)
        return FALSE;

    // 从密文末尾提取IV
    string iv = data.substr(data.length() - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
    // 移除IV,剩余部分是加密后的密文
    data = data.substr(0, data.length() - AES_BLOCK_SIZE);

    // 设置密钥的IV参数为提取出的随机IV
    if (!CryptSetKeyParam(hKey, KP_IV, (BYTE*)iv.c_str(), 0))
        return FALSE;

    // 解密密文
    DWORD dwDataLen = data.length();
    if (!CryptDecrypt(hKey, NULL, TRUE, 0, (BYTE*)&data[0], &dwDataLen))
        return FALSE;
    data.resize(dwDataLen);

    return TRUE;
}

int main()
{
    if (!AesInitialization()) 
    {
        std::cout << "初始化失败" << std::endl;
        return 1;
    }

    string message = "my mother & your mother were hanging up clothes";
    string encrypted = message;

    if (!AesEncrypt(encrypted))
    {
        std::cout << "加密失败" << std::endl;
        return 1;
    }

    string decrypted = encrypted;
    if (!AesDecrypt(decrypted)) 
    {
        std::cout << "解密失败" << std::endl;
        return 1;
    }

    std::cout << "原始明文: " << message << std::endl;
    std::cout << "解密结果: " << decrypted << std::endl;

    // 清理资源
    if (hKey) CryptDestroyKey(hKey);
    if (hCryptProv) CryptReleaseContext(hCryptProv, 0);

    return 0;
}

关键修正点

  1. 加密时:先加密明文,再将IV附加到密文末尾,而非把IV和明文拼接后加密
  2. 解密时:先从密文末尾提取IV,再用该IV解密剩余内容
  3. 增加了资源清理逻辑,避免内存泄漏
  4. 增加了长度检查,防止非法数据导致崩溃

内容的提问来源于stack exchange,提问作者JeffR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 21:57:04