Unreal中使用DPAPI加解密用户输入时解密结果不符的问题
问题根源分析
- 错误使用
strlen计算数据长度:strlen会在遇到第一个0x00字节时停止计数,若安全令牌包含多字节字符、不可见字符或0x00,会导致加密的数据不完整,解密时DPAPI返回的内容可能混入随机内存数据。 - FString转字节数组的长度处理错误:
InString.Len()获取的是字符串的字符数,而非转换后的字节数。直接用字符数初始化字节数组会导致长度不足,引发数据越界或截断,进而导致解密异常。 - 编码转换不明确:未指定
StringToBytes和BytesToString的编码格式,不同编码下字节长度和内容差异会导致转换丢失数据或引入无效字节。
修复后的代码实现
加密函数
bool AuthManager::Encrypt(FString InString) { // 将FString转为UTF-8字节数组,明确编码避免歧义 TArray<uint8> Data; FTCHARToUTF8 Convert(*InString); Data.Append((uint8*)Convert.Get(), Convert.Length()); DATA_BLOB DataIn, DataOut; DataIn.pbData = Data.GetData(); DataIn.cbData = Data.Num(); // 使用真实字节数,替代strlen if(CryptProtectData( &DataIn, nullptr, nullptr, nullptr, nullptr, 0, &DataOut)) { const char* EncryptedTokenFile = "path\\to\\encryptedTokenFile.txt"; std::ofstream EncryptedFile(EncryptedTokenFile, std::ios::out | std::ios::binary); EncryptedFile.write(reinterpret_cast<char*>(&DataOut.cbData), sizeof(DataOut.cbData)); EncryptedFile.write(reinterpret_cast<char*>(DataOut.pbData), DataOut.cbData); EncryptedFile.close(); LocalFree(DataOut.pbData); } else { UE_LOG(LogConsoleResponse, Error, TEXT("Encryption error using CryptProtectData.")); return false; } return true; }
解密函数
bool AuthManager::Decrypt(FString& OutString) { const char* EncryptedTokenFile = "path\\to\\encryptedTokenFile.txt"; std::ifstream ReadEncryptedFile(EncryptedTokenFile, std::ios::in | std::ios::binary); if(!ReadEncryptedFile.is_open()) { UE_LOG(LogConsoleResponse, Error, TEXT("Cannot open %s. Please verify the file exists and is not read-protected."), *GetTokenFilePath()); return false; } DATA_BLOB DataIn, DataOut; LPWSTR DataDesc = nullptr; ReadEncryptedFile.read(reinterpret_cast<char*>(&DataIn.cbData), sizeof(DataIn.cbData)); DataIn.pbData = new BYTE[DataIn.cbData]; ReadEncryptedFile.read(reinterpret_cast<char*>(DataIn.pbData), DataIn.cbData); if (CryptUnprotectData( &DataIn, &DataDesc, nullptr, nullptr, nullptr, 0, &DataOut)) { // 将UTF-8字节数组转回FString OutString = FString(UTF8_TO_TCHAR(reinterpret_cast<const char*>(DataOut.pbData))); UE_LOG(LogConsoleResponse, Display, TEXT("The OutString is: %s"), *OutString); LocalFree(DataOut.pbData); LocalFree(DataDesc); } else { UE_LOG(LogConsoleResponse, Error, TEXT("Decryption error using CryptUnprotectData.")); return false; } delete[] DataIn.pbData; // 释放动态分配的内存,避免泄漏 return true; }
关键修复说明
- 明确编码转换:用
FTCHARToUTF8和UTF8_TO_TCHAR统一编码为UTF-8,消除编码歧义,保证字节长度和内容准确。 - 使用真实字节数:加密时直接取字节数组的实际长度,避免
strlen截断数据的问题。 - 修复内存泄漏:解密时手动释放动态分配的
DataIn.pbData,避免内存泄漏。
内容的提问来源于stack exchange,提问作者MashedPotato6587
相关产品推荐
相关产品推荐

