提问人:MashedPotato6587 提问时间:4/25/2023 最后编辑:MashedPotato6587 更新时间:5/1/2023 访问量:154
在 Python 中调用 CryptProtectData,然后在 C++ 中调用 CryptUnprotectData 返回错误 87(反之亦然)
Calling CryptProtectData in Python, then Calling CryptUnprotectData in C++ returns error 87 (and visa versa)
问:
在虚幻引擎5.1中使用Python 3.11.0和C++
我在 Python (3.11.0) 中使用 DPAPI 加密用户输入,并通过 C++ (UE 5.1) 解密它,但在 C++ 中运行 CryptUnprotectData 时出现错误 87(我认为这是一个无效的参数?如果我从 C++ 运行保护并从 Python 取消保护,我也会得到相同的结果,但是如果我从同一个地方执行这两个操作,一切都可以正常工作,即。两者都来自 c++ 或两者都来自 Python。
Python 代码:
# DPAPI access library
# This file uses code originally created by Crusher Joe:
# http://article.gmane.org/gmane.comp.python.ctypes/420
# And modified by Wayne Koorts:
# http://stackoverflow.com/questions/463832/using-dpapi-with-python
from ctypes import *
from ctypes.wintypes import DWORD
from getpass import getpass
import os
##
LocalFree = windll.kernel32.LocalFree
memcpy = cdll.msvcrt.memcpy
CryptProtectData = windll.crypt32.CryptProtectData
CryptUnprotectData = windll.crypt32.CryptUnprotectData
my_dir = 'C:\\Users\\User\\Desktop\\PythonCrypt'
file_name = 'EncryptedToken.txt'
encrypted_file_path = os.path.join(my_dir, file_name)
class DATA_BLOB(Structure):
_fields_ = [("cbData", DWORD), ("pbData", POINTER(c_char))]
def getData(blobOut):
cbData = int(blobOut.cbData)
pbData = blobOut.pbData
buffer = c_buffer(cbData)
memcpy(buffer, pbData, cbData)
LocalFree(pbData)
return buffer.raw
def Win32CryptProtectData(plainText):
bufferIn = c_buffer(plainText, len(plainText))
blobIn = DATA_BLOB(len(plainText), bufferIn)
blobOut = DATA_BLOB()
if CryptProtectData(byref(blobIn), None, None, None, None, 0, byref(blobOut)):
return getData(blobOut)
else:
print("CryptProtectData failed, Error: " + str(GetLastError()))
print("Returning an empty string")
return ""
def Win32CryptUnprotectData(cipherText):
bufferIn = c_buffer(cipherText, len(cipherText))
blobIn = DATA_BLOB(len(cipherText), bufferIn)
blobOut = DATA_BLOB()
if CryptUnprotectData(byref(blobIn), None, None, None, None, 0, byref(blobOut)):
print("CryptUnprotectData successful")
return getData(blobOut)
else:
print("CryptUnprotectData failed, Error: " + str(GetLastError()))
print("Returning an empty string")
return ""
# Encrypts bytes and saves to file
def cryptData(file_path, text_bytes):
WriteBytesToFile(file_path, Win32CryptProtectData(text_bytes))
# Reads byte file and returns decrypted bytes
def decryptData(file_path):
readFile = ReadBytesFromFile(file_path)
return Win32CryptUnprotectData(readFile)
def WriteBytesToFile(file_path, bytes_to_write):
with open(file_path, "wb") as wf:
wf.write(bytes_to_write)
def ReadBytesFromFile(file_path):
with open(file_path, "rb") as rf:
return rf.read()
if __name__ == '__main__':
# Prompt user for string password
Password = getpass("Enter your password: ")
# Convert string to bytes, and encrypt
cryptData(encrypted_file_path, bytes(Password, 'utf-8'))
虚幻 C++ 代码:
- XLOG 是用于日志记录而不是UE_LOG的宏。
- 我包含加密只是为了完整,但加密不会在 C++ 中发生。只有 Decrypt() 会运行。
#define ENCRYPTED_TOKEN_FILE_NAME TEXT("EncryptedToken.txt")
bool CryptUtil::Encrypt(FString InString)
{
// Encrypt data from DATA_BLOB DataIn to DATA_BLOB DataOut
DATA_BLOB DataIn, DataOut;
TArray<uint8> Data;
const int32 Size = InString.Len();
Data.AddUninitialized(Size);
// Create entropy data blob
DATA_BLOB entropyBlob;
// Convert FString to BYTE
StringToBytes(*InString, Data.GetData(), Size);
// Declare and initialize the DataIn structure
BYTE* PbDataInput = Data.GetData();
const DWORD CBDataInput = Size;
DataIn.pbData = PbDataInput;
DataIn.cbData = CBDataInput;
// Begin protect phase
if (CryptProtectData(
&DataIn, // Unencrypted data in
nullptr, // Optional description string
nullptr, // Optional entropy
nullptr, // Reserved
nullptr, // Optional prompt struct
0, // Flags
&DataOut)) // Encrypted data out
{
// If we're saving a new token, delete any existing encrypted files
const FString TokenFile = GetEncryptedTokenFile();
if (!TokenFile.IsEmpty())
{
if (FPaths::ValidatePath(TokenFile) && FPaths::FileExists(TokenFile))
{
IFileManager& FileManager = IFileManager::Get();
FileManager.Delete(*TokenFile);
}
}
// Convert from FString
const char* EncryptedTokenFile = StringCast<ANSICHAR>(*TokenFile).Get();
// Write encrypted data out to file
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();
// Release memory
SecureZeroMemory(DataOut.pbData, DataOut.cbData);
LocalFree(DataOut.pbData);
}
else
{
XLOG(LogConsoleResponse, Error, TEXT("Encryption error using CryptProtectData."));
return false;
}
return true;
}
bool CryptUtil::Decrypt(FString& OutString)
{
// Decrypt data from DATA_BLOB DataIn to DATA_BLOB DataOut
DATA_BLOB DataIn, DataOut;
// Create entropy data blob
DATA_BLOB entropyBlob;
// Convert from FString
const char* EncryptedTokenFile = StringCast<ANSICHAR>(*GetEncryptedTokenFile()).Get();
std::ifstream ReadEncryptedFile(EncryptedTokenFile, std::ios::in | std::ios::binary);
if (!ReadEncryptedFile.is_open())
{
XLOG(LogConsoleResponse, Error, TEXT("Cannot open {%s}."), *GetEncryptedTokenFile());
return false;
}
// Read encrypted data from file
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);
// Begin unprotect phase.
if (CryptUnprotectData(
&DataIn, // Encrypted data in
nullptr, // Optional description string
nullptr, // Optional entropy
nullptr, // Reserved
nullptr, // Optional prompt struct
0, // Flags
&DataOut)) // Unncrypted data out
{
// Convert BYTE to FString
OutString = BytesToString(DataOut.pbData, DataOut.cbData);
XLOG(LogConsoleResponse, Verbose, TEXT("CryptUnprotectData was successful!"));
// Release memory
SecureZeroMemory(DataOut.pbData, DataOut.cbData);
LocalFree(DataOut.pbData);
}
else
{
XLOG(LogConsoleResponse, Error, TEXT("CryptUnprotectData failed, Error: %s"), *GetLastError());
return false;
}
return true;
}
FString CryptUtil::GetEncryptedTokenFile()
{
const FString tokenFile = FPaths::Combine("C:\\Users\\User\\Desktop\\PythonCrypt", ENCRYPTED_TOKEN_FILE_NAME);
return tokenFile;
}
尝试将文件另存为字符串文件而不是二进制文件,但不起作用。 尝试在 Python 中加密和解密或在 C++ 中加密和解密,并且在每种情况下都有效。只有当我尝试从一个部分做第一部分,在另一个做第二部分时,我才会收到错误87
答:
0赞
MashedPotato6587
5/1/2023
#1
我找到了解决方案。Unreal 方法 BytesToString 和 StringToBytes 都删除了 null 终止符。我创建了自己的函数,用于在 FString 和允许它们的字节之间进行转换。这解决了我的问题。
在 StringToBytes 中,从 BytesToString 中删除 + 1,在 BytesToString 中删除OutBytes[NumBytes] = (int8)(*CharPos + 1);
Value += 1;
还使用 FileHelper 读取加密文件而不是 std::ifstream
TArray<uint8> EncryptedData;
const FString TokenFile = GetEncryptedTokenFile();
FFileHelper::LoadFileToArray(EncryptedData, *TokenFileFString);
评论