为何Windows平台CryptDecrypt无论传入何种密钥均返回成功?
问题排查:CryptDecrypt始终返回TRUE无法验证密钥正确性
核心原因
- AES默认模式特性:代码未指定加密模式,Windows CryptoAPI默认使用ECB模式。ECB模式下解密仅对每个数据块独立运算,无论密钥是否正确,都会输出对应长度的内容,不会返回错误。若搭配默认的PKCS#7填充,密钥错误时解密后的最后一块填充可能恰好符合格式要求,导致CryptDecrypt返回成功,但实际内容为乱码。
- CryptDecrypt本身不做密钥验证:该API仅负责完成解密运算,不会主动校验密钥是否匹配原始加密密钥,必须额外添加完整性验证逻辑才能确认密钥正确性。
解决方案
1. 改用带认证的加密模式(推荐)
使用AES-GCM这类带认证的加密模式,加密时生成认证标签,解密时需验证标签,密钥错误或数据篡改会直接导致CryptDecrypt返回失败。修改代码示例:
void DecryptAES(char* content, DWORD contentlen, char* key, DWORD keyLen, BYTE* authTag, DWORD authTagLen) { HCRYPTPROV hProv = NULL; HCRYPTHASH hHash = NULL; HCRYPTKEY hKey = NULL; if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { printf("Failed in CryptAcquireContextW (%u)\n", GetLastError()); goto cleanup; } if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { printf("Failed in CryptCreateHash (%u)\n", GetLastError()); goto cleanup; } if (!CryptHashData(hHash, (BYTE*)key, keyLen, 0)) { printf("Failed in CryptHashData (%u)\n", GetLastError()); goto cleanup; } // 派生GCM模式的AES密钥 if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, CRYPT_MODE_GCM, &hKey)) { printf("Failed in CryptDeriveKey (%u)\n", GetLastError()); goto cleanup; } // 设置需要验证的GCM认证标签(加密时需保存该标签) if (!CryptSetKeyParam(hKey, KP_GCM_AUTH_TAG, authTag, 0)) { printf("Failed in CryptSetKeyParam (%u)\n", GetLastError()); goto cleanup; } // 执行解密,密钥错误或标签不匹配会返回失败 if (!CryptDecrypt(hKey, NULL, TRUE, 0, (BYTE*)content, &contentlen)) { printf("Failed in CryptDecrypt (invalid key or tampered data) (%u)\n", GetLastError()); goto cleanup; } cleanup: if (hKey) CryptDestroyKey(hKey); if (hHash) CryptDestroyHash(hHash); if (hProv) CryptReleaseContext(hProv, 0); }
2. 添加哈希完整性校验(兼容现有加密逻辑)
若无法修改加密逻辑,可在加密时计算明文的SHA-256哈希并与密文一起存储;解密后重新计算解密内容的哈希,对比两者是否一致,不一致则判定密钥错误:
// 解密后添加哈希校验步骤 BYTE storedHash[32]; // 假设从存储中读取的原始明文哈希 DWORD hashLen = sizeof(storedHash); HCRYPTHASH hVerifyHash = NULL; if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hVerifyHash)) { printf("Failed in CryptCreateHash for verify (%u)\n", GetLastError()); goto cleanup; } if (!CryptHashData(hVerifyHash, (BYTE*)content, contentlen, 0)) { printf("Failed in CryptHashData for verify (%u)\n", GetLastError()); goto cleanup; } BYTE decryptedHash[32]; if (!CryptGetHashParam(hVerifyHash, HP_HASHVAL, decryptedHash, &hashLen, 0)) { printf("Failed in CryptGetHashParam (%u)\n", GetLastError()); goto cleanup; } if (memcmp(decryptedHash, storedHash, 32) != 0) { printf("Invalid key: decrypted content hash mismatch\n"); // 处理密钥错误逻辑 } CryptDestroyHash(hVerifyHash);
3. 避免使用不安全的ECB模式
ECB模式存在安全隐患,至少改用CBC模式并使用随机IV(初始化向量)。即使如此,CBC模式本身仍不会验证密钥正确性,仍需搭配上述完整性校验步骤。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

