Приветствую, коллеги. Есть программа, функционал которой расширяется плагинами, одно из таких расширений должно поддерживать шифрование с помощью CryptProtectData/CryptUnprotectData из Data Protection API (Crypt32.dll). Сперва написали тестовый модуль и статически прописали загрузку Crypt32.dll - всё работает отлично, написали обёртку - и тут случился облом. Операционка возвращает "System Error. Code: 127. The specified procedure could not be found" после не успешного вызова CryptProtectData() - по идее CryptUnprotectData() делает то же самое, просто до него дело не доходит. Сделал динамическую загрузку Crypt32.dll - результат тот же, если функции Crypt32.dll вызываются непосредственно из EXE - всё замечательно работает, если вызываем через обёртку из DLL - опять проблема. Иногда вместо ошибки 127 возвращается "System Error. Code: 87. The parameter is incorrect", но эта ошибка зачастую лечится перезапуском Делфи и ребилдом проекта.
Если вдруг имеет какое значение, то проект собирался на Делфи 10.2. Буду рад любой подсказке, ибо сам весь в смущении и непонятности :)
Выглядит вся эта кухня вот так:
DPCrypt.pas
unit DPCrypt;
{$DEFINE STATIC_LINK} // comment for dynamic DLL loading
interface
uses
EncdDecd, Winapi.Windows, System.SysUtils;
type
TLargeByteArray = array [0..Pred(MaxInt)] of Byte;
PLargeByteArray = ^TLargeByteArray;
_CRYPTOAPI_BLOB = packed record
cbData: DWORD;
pbData: PByte;
end;
TCryptoApiBlob = _CRYPTOAPI_BLOB;
PCrypyoApiBlob = ^TCryptoApiBlob;
CRYPT_INTEGER_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_INTEGER_BLOB = ^CRYPT_INTEGER_BLOB;
CRYPT_UINT_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_UINT_BLOB = ^CRYPT_INTEGER_BLOB;
CRYPT_OBJID_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_OBJID_BLOB = ^CRYPT_INTEGER_BLOB;
CERT_NAME_BLOB = _CRYPTOAPI_BLOB;
PCERT_NAME_BLOB = ^CRYPT_INTEGER_BLOB;
CERT_RDN_VALUE_BLOB = _CRYPTOAPI_BLOB;
PCERT_RDN_VALUE_BLOB = ^CRYPT_INTEGER_BLOB;
CERT_BLOB = _CRYPTOAPI_BLOB;
PCERT_BLOB = ^CRYPT_INTEGER_BLOB;
CRL_BLOB = _CRYPTOAPI_BLOB;
PCRL_BLOB = ^CRYPT_INTEGER_BLOB;
DATA_BLOB = _CRYPTOAPI_BLOB;
PDATA_BLOB = ^CRYPT_INTEGER_BLOB;
CRYPT_DATA_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_DATA_BLOB = ^CRYPT_INTEGER_BLOB;
CRYPT_HASH_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_HASH_BLOB = ^CRYPT_INTEGER_BLOB;
CRYPT_DIGEST_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_DIGEST_BLOB = ^CRYPT_INTEGER_BLOB;
CRYPT_DER_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_DER_BLOB = ^CRYPT_INTEGER_BLOB;
CRYPT_ATTR_BLOB = _CRYPTOAPI_BLOB;
PCRYPT_ATTR_BLOB = ^CRYPT_INTEGER_BLOB;
_CRYPTPROTECT_PROMPTSTRUCT = packed record
cbSize: DWORD;
dwPromptFlags: DWORD;
hwndApp: HWND;
szPrompt: LPCWSTR;
end;
TCryptProtectPromptStruct = _CRYPTPROTECT_PROMPTSTRUCT;
PCryptProtectPromptStruct = ^TCryptProtectPromptStruct;
CRYPTPROTECT_PROMPTSTRUCT = _CRYPTPROTECT_PROMPTSTRUCT;
PCRYPTPROTECT_PROMPTSTRUCT = ^_CRYPTPROTECT_PROMPTSTRUCT;
function CryptProtectString(S: PWideChar): PWideChar; //stdcall;
function CryptUnprotectString(S: PWideChar): PWideChar; //stdcall;
implementation
{$IFDEF STATIC_LINK}
function CryptProtectData(pDataIn: PDATA_BLOB; szDataDescr: LPCWSTR; pOptionalEntropy: PDATA_BLOB; pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall; external 'Crypt32.dll';
function CryptUnprotectData(pDataIn: PDATA_BLOB; var ppszDataDescr: LPWSTR; pOptionalEntropy: PDATA_BLOB; pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall; external 'Crypt32.dll';
{$ELSE}
type
TCryptProtectData = function(pDataIn: PDATA_BLOB; szDataDescr: LPCWSTR; pOptionalEntropy: PDATA_BLOB; pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
TCryptUnprotectData = function(pDataIn: PDATA_BLOB; var ppszDataDescr: LPWSTR; pOptionalEntropy: PDATA_BLOB; pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
var
CryptProtectData: TCryptProtectData;
CryptUnprotectData: TCryptUnprotectData;
Crypt32DLL: THandle;
function LoadCrypt32DLL: Boolean;
begin
Crypt32DLL := LoadLibrary('Crypt32.dll');
Result := Crypt32DLL <> 0;
if Result then
begin
CryptProtectData := GetProcAddress(Crypt32DLL, 'CryptProtectData');
Result := @CryptProtectData <> nil;
if not Result then
Exit;
CryptUnprotectData := GetProcAddress(Crypt32DLL, 'CryptUnprotectData');
Result := @CryptUnprotectData <> nil;
end;
end;
{$ENDIF}
function dpApiProtectData(var fpDataIn: TBytes): tBytes;
var
dataIn, // Input buffer (clear-text/data)
dataOut: DATA_BLOB; // Output buffer (encrypted)
begin
dataOut.cbData := 0;
dataOut.pbData := nil;
dataIn.cbData := Length(fpDataIn); // How much data (in bytes) we want to encrypt
dataIn.pbData := @fpDataIn[0]; // Pointer to the data itself - the address of the first element of the input byte array
if not CryptProtectData(@dataIn, nil, nil, nil, nil, 0, @dataOut) then
RaiseLastOSError;
SetLength(Result, dataOut.cbData);
Move(dataOut.pbData^, Result[0], dataOut.cbData);
LocalFree(HLOCAL(dataOut.pbData)); // http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261(v=vs.85).aspx
end;
function dpApiUnprotectData(fpDataIn: TBytes): tBytes;
var
dataIn, // Input buffer (clear-text/data)
dataOut: DATA_BLOB; // Output buffer (encrypted)
lpwszDesc: PWideChar;
begin
dataOut.cbData := 0;
dataOut.pbData := nil;
dataIn.cbData := Length(fpDataIn);
dataIn.pbData := @fpDataIn[0];
if not CryptUnprotectData(@dataIn, lpwszDesc, nil, nil, nil, 0, @dataOut) then
RaiseLastOSError;
SetLength(Result, dataOut.cbData);
Move(dataOut.pbData^, Result[0], dataOut.cbData);
LocalFree(HLOCAL(dataOut.pbData)); // http://msdn.microsoft.com/en-us/library/windows/desktop/aa380882%28v=vs.85%29.aspx
end;
function CryptProtectString(S: PWideChar): PWideChar; //stdcall;
var
strIn: String;
bytesEncrypted: TBytes;
strOut: AnsiString;
begin
{$IFNDEF STATIC_LINK}if not LoadCrypt32DLL then Exit;{$ENDIF}
Result := '';
strIn := S;
SetLength(bytesEncrypted, Length(strIn) * SizeOf(Char));
Move(strIn[1], bytesEncrypted[0], Length(strIn) * SizeOf(Char));
bytesEncrypted := dpApiProtectData(bytesEncrypted); // Encrypt data
SetString(strOut, PAnsiChar(bytesEncrypted), Length(bytesEncrypted));
Result := StrNew(PWideChar(EncodeString(strOut)));
{$IFNDEF STATIC_LINK}if Crypt32DLL <> 0 then FreeModule(Crypt32DLL);{$ENDIF}
end;
function CryptUnprotectString(S: PWideChar): PWideChar; //stdcall;
var
strIn: AnsiString;
bytesEncrypted: TBytes;
strOut: String;
begin
{$IFNDEF STATIC_LINK}if not LoadCrypt32DLL then Exit;{$ENDIF}
Result := '';
strIn := DecodeString(S);
SetLength(bytesEncrypted, Length(strIn));
Move(strIn[1], bytesEncrypted[0], Length(strIn));
bytesEncrypted := dpApiUnprotectData(bytesEncrypted); // Decrypt data
SetLength(strOut, Length(bytesEncrypted) div SizeOf(Char));
Move(bytesEncrypted[0], strOut[1], length(bytesEncrypted));
Result := StrNew(PWideChar(strOut));
{$IFNDEF STATIC_LINK}if Crypt32DLL <> 0 then FreeModule(Crypt32DLL);{$ENDIF}
end;
end.
TestApp.dpr
program TestApp;
{$APPTYPE CONSOLE}
{.$DEFINE USE_DLL} // remove the dot for DLL
uses
{$IFNDEF USE_DLL} DPCrypt, {$ENDIF}
Windows, Messages, SysUtils;
{$IFDEF USE_DLL}
type
TPluginFn = function(S: PWideChar): PWideChar; stdcall;
var
DPAPI: THandle;
dpApiProtectString,
dpApiUnprotectString: TPluginFn;
function LoadDPAPI_DLL: Boolean;
begin
DPAPI := LoadLibrary('DPAPI.dll');
Result := DPAPI <> 0;
if Result then
begin
dpApiProtectString := GetProcAddress(DPAPI, 'dpApiProtectString');
dpApiUnprotectString := GetProcAddress(DPAPI, 'dpApiUnprotectString');
end
else
WriteLn(SysErrorMessage(GetLastError));
end;
{$ENDIF}
var
S: String;
begin
S := 'Hello world';
WriteLn('original_string: ' + S);
{$IFDEF USE_DLL}
if LoadDPAPI_DLL then
try
S := dpApiProtectString(PWideChar(S));
WriteLn('protected_string: ' + S);
S := dpApiUnprotectString(PWideChar(S));
WriteLn('unprotected_string: ' + S);
finally
if DPAPI <> 0 then
FreeLibrary(DPAPI);
end;
{$ELSE}
S := CryptProtectString(PWideChar(S));
WriteLn('protected_string: ' + S);
S := CryptUnprotectString(PWideChar(S));
WriteLn('unprotected_string: ' + S);
{$ENDIF}
ReadLn;
end.
DPAPI.dpr
library DPAPI;
uses
DPCrypt;
{$R *.res}
function dpApiProtectString(S: PWideChar): PWideChar; stdcall;
begin
Result := CryptProtectString(S);
end;
function dpApiUnprotectString(S: PWideChar): PWideChar; stdcall;
begin
Result := CryptUnprotectString(S);
end;
exports
dpApiProtectString,
dpApiUnprotectString;
end.