Kopiuj
type
NTSTATUS = Longint;
ULONG = Cardinal;
PUCHAR = PByte;
BCRYPT_ALG_HANDLE = Pointer;
BCRYPT_KEY_HANDLE = Pointer;
function BCryptOpenAlgorithmProvider(out phAlgorithm: BCRYPT_ALG_HANDLE; pszAlgId, pszImplementation: PWideChar; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptGetProperty(hObject: Pointer; pszProperty: PWideChar; pbOutput: PUCHAR; cbOutput: ULONG; out pcbResult: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptSetProperty(hObject: Pointer; pszProperty: PWideChar; pbInput: PUCHAR; cbInput: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptImportKey(hAlgorithm: BCRYPT_ALG_HANDLE; hImportKey: BCRYPT_KEY_HANDLE; pszBlobType: PWideChar; out phKey: BCRYPT_KEY_HANDLE; pbKeyObject: PUCHAR; cbKeyObject: ULONG; pbInput: PUCHAR; cbInput: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptEncrypt(hKey: BCRYPT_KEY_HANDLE; pbInput: PUCHAR; cbInput: ULONG; pPaddingInfo: Pointer; pbIV: PUCHAR; cbIV: ULONG; pbOutput: PUCHAR; cbOutput: ULONG; out pcbResult: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptDestroyKey(hKey: BCRYPT_KEY_HANDLE): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
function lstrlenW(ps: PWideChar): Integer; stdcall; external 'kernel32.dll' name 'lstrlenW';
function BCryptGenerateSymmetricKey(hAlgorithm: BCRYPT_ALG_HANDLE; out phKey: BCRYPT_KEY_HANDLE; pbKeyObject: PUCHAR; cbKeyObject: ULONG; pbSecret: PUCHAR; cbSecret: ULONG; dwFlags: ULONG): NTSTATUS; stdcall; external 'bcrypt.dll';
const
STATUS_SUCCESS = 0;
STATUS_BUFFER_TOO_SMALL = $C0000023;
STATUS_INVALID_PARAMETER = NTSTATUS($C000000D);
BCRYPT_AES_ALGORITHM : PWideChar = 'AES';
BCRYPT_OBJECT_LENGTH : PWideChar = 'ObjectLength';
BCRYPT_CHAINING_MODE : PWideChar = 'ChainingMode';
BCRYPT_CHAIN_MODE_CBC : PWideChar = 'ChainingModeCBC';
BCRYPT_BLOCK_PADDING = $00000001;
BCRYPT_KEY_DATA_BLOB : PWideChar = 'KeyDataBlob';
BCRYPT_KEY_DATA_BLOB_MAGIC = $4279654B; // 'KeyB'
BCRYPT_KEY_DATA_BLOB_VERSION = 1;
type
BCRYPT_KEY_DATA_BLOB_HEADER = packed record
dwMagic : ULONG;
dwVersion : ULONG;
cbKeyData : ULONG;
end;
type
ECNG = class(Exception);
class function TKsefInvoiceSender.AES_CBC_Encrypt_PKCS7_BCrypt(const Plain, Key, IV: TBytes): TBytes;
var
hAlg: BCRYPT_ALG_HANDLE;
hKey: BCRYPT_KEY_HANDLE;
st: NTSTATUS;
objLen, need, got, cb: ULONG;
keyObj: TBytes;
hdr: BCRYPT_KEY_DATA_BLOB_HEADER;
blob, ivLocal: TBytes;
procedure Check(const Where: string; Code: NTSTATUS);
begin
if Code <> STATUS_SUCCESS then
raise ECNG.CreateFmt('%s failed: 0x%.8x', [Where, Cardinal(Code)]);
end;
procedure SetCBCOnKey(const KeyHandle: BCRYPT_KEY_HANDLE);
var L: ULONG;
begin
L := (lstrlenW(BCRYPT_CHAIN_MODE_CBC)+1) * SizeOf(WideChar);
Check('BCryptSetProperty(ChainingModeCBC)',
BCryptSetProperty(KeyHandle, BCRYPT_CHAINING_MODE, PUCHAR(BCRYPT_CHAIN_MODE_CBC), L, 0));
end;
function DoEncrypt(const KeyHandle: BCRYPT_KEY_HANDLE): TBytes;
begin
ivLocal := Copy(IV);
need := 0;
Check('BCryptEncrypt(size)',
BCryptEncrypt(KeyHandle, PUCHAR(@Plain[0]), Length(Plain),
nil, PUCHAR(@ivLocal[0]), Length(ivLocal),
nil, 0, need, BCRYPT_BLOCK_PADDING));
SetLength(Result, need);
ivLocal := Copy(IV);
got := 0;
Check('BCryptEncrypt',
BCryptEncrypt(KeyHandle, PUCHAR(@Plain[0]), Length(Plain),
nil, PUCHAR(@ivLocal[0]), Length(ivLocal),
PUCHAR(@Result[0]), need, got, BCRYPT_BLOCK_PADDING));
SetLength(Result, got);
end;
function TryImportKey(NeedKeyObj: Boolean; out KeyHandle: BCRYPT_KEY_HANDLE): NTSTATUS;
begin
if NeedKeyObj then
begin
objLen := 0; cb := 0;
Result := BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, PUCHAR(@objLen), SizeOf(objLen), cb, 0);
if (Result <> STATUS_SUCCESS) then Exit;
if (cb <> SizeOf(objLen)) or (objLen = 0) then
begin
Result := STATUS_INVALID_PARAMETER;
Exit;
end;
SetLength(keyObj, objLen);
Result := BCryptImportKey(hAlg, nil, BCRYPT_KEY_DATA_BLOB, KeyHandle,
PUCHAR(@keyObj[0]), objLen,
PUCHAR(@blob[0]), Length(blob), 0);
end
else
begin
Result := BCryptImportKey(hAlg, nil, BCRYPT_KEY_DATA_BLOB, KeyHandle,
nil, 0,
PUCHAR(@blob[0]), Length(blob), 0);
end;
end;
function GenerateKey(out KeyHandle: BCRYPT_KEY_HANDLE): NTSTATUS;
begin
objLen := 0; cb := 0;
Result := BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, PUCHAR(@objLen), SizeOf(objLen), cb, 0);
if Result <> STATUS_SUCCESS then Exit;
if (cb <> SizeOf(objLen)) or (objLen = 0) then
begin
Result := STATUS_INVALID_PARAMETER;
Exit;
end;
SetLength(keyObj, objLen);
Result := BCryptGenerateSymmetricKey(hAlg, KeyHandle, PUCHAR(@keyObj[0]), objLen,
PUCHAR(@Key[0]), Length(Key), 0);
end;
begin
if Length(Key) <> 32 then raise ECNG.Create('AES key must be 32 bytes');
if Length(IV) <> 16 then raise ECNG.Create('IV must be 16 bytes');
hdr.dwMagic := BCRYPT_KEY_DATA_BLOB_MAGIC;
hdr.dwVersion := BCRYPT_KEY_DATA_BLOB_VERSION;
hdr.cbKeyData := Length(Key);
SetLength(blob, SizeOf(hdr) + Length(Key));
Move(hdr, blob[0], SizeOf(hdr));
Move(Key[0], blob[SizeOf(hdr)], Length(Key));
hAlg := nil;
Check('BCryptOpenAlgorithmProvider',
BCryptOpenAlgorithmProvider(hAlg, BCRYPT_AES_ALGORITHM, nil, 0));
try
hKey := nil;
st := TryImportKey(False, hKey);
if st = STATUS_SUCCESS then
begin
try
SetCBCOnKey(hKey);
Exit(DoEncrypt(hKey));
finally
BCryptDestroyKey(hKey);
end;
end;
hKey := nil;
st := TryImportKey(True, hKey);
if st = STATUS_SUCCESS then
begin
try
SetCBCOnKey(hKey);
Exit(DoEncrypt(hKey));
finally
BCryptDestroyKey(hKey);
end;
end;
hKey := nil;
Check('BCryptGenerateSymmetricKey', GenerateKey(hKey));
try
SetCBCOnKey(hKey);
Exit(DoEncrypt(hKey));
finally
BCryptDestroyKey(hKey);
end;
finally
BCryptCloseAlgorithmProvider(hAlg, 0);
end;
end;