Какой аналог криптографической функции Java PBEKeySpec в Windows Microsoft cryptographic CNG API?

Какой аналог криптографической функции Java PBEKeySpec в Windows Microsoft cryptographic CNG API?

Я пытаюсь портировать код изначально из Java для генерации ключа AES с паролем и солью в Windows используя только (CNG). Вот исходный код на Java поторый я пытаюсь портировать:

byte[] ret = null;
ret = SecretKeyFactory.getInstance(PBKDF2_ALGORIGHTM).generateSecret(new PBEKeySpec(password, salt, 1000, 256)).getEncoded();

Пытаюсь сделать (на Delphi) так:

[...]

type
  BCRYPT_ALG_HANDLE   = Pointer;

[...]

const
  BCRYPT_AES_ALGORITHM:     WideString = 'AES';
  BCRYPT_SHA1_ALGORITHM:    WideString = 'SHA1';

[...] 

  BCRYPT_ALG_HANDLE_HMAC_FLAG = 8;

[...]

function CreateKey(
  const APassword, ASalt: AnsiString;
  var AKey: AnsiString): NTSTATUS;
var
  hAlg: BCRYPT_ALG_HANDLE;
  derivedKeyLength: DWORD;
  resultLength: DWORD;
  pbPassword: Cardinal;
  cbPassword: Cardinal;
  pbSalt: Cardinal;
  cbSalt: Cardinal;
  cIterations: Cardinal;
  pbDerivedKey: Cardinal;
  cbDerivedKey: Cardinal;
  dwFlags: Cardinal;
begin
  AKey := '';

  Result := BCryptOpenAlgorithmProvider(
    hAlg,
    @BCRYPT_SHA1_ALGORITHM[1],
    nil,
    BCRYPT_ALG_HANDLE_HMAC_FLAG);
  if Result >= 0 then begin
    Result := BCryptGetProperty(
                hAlg,
                @BCRYPT_OBJECT_LENGTH[1],
                  @derivedKeyLength,
                sizeof(DWORD),
                @resultLength,
                  0);
    if Result >= 0 then begin
      SetLength(AKey, derivedKeyLength);

      pbPassword := Cardinal(@APassword[1]);
      cbPassword := Length(APassword);
      pbSalt := Cardinal(@ASalt[1]);
      cbSalt := Length(ASalt);
      cIterations := 1000;
      pbDerivedKey := Cardinal(@AKey[1]);
      cbDerivedKey := derivedKeyLength ;
      dwFlags := 0;

      asm
        push [dwFlags]
        push [cbDerivedKey]
        push [pbDerivedKey]
        push [cIterations]
        push 0
        push [cbSalt]
        push [pbSalt]
        push [cbPassword]
        push [pbPassword]
        push [hAlg]
        call BCryptDeriveKeyPBKDF2
        mov  [Result],eax
      end;
      
      if Result >= 0 then
        Exit;
        
[...]

    end;
    BCryptCloseAlgorithmProvider(hAlg, 0);
  end;
end;

Пришлось применить inline ассемблер потому что функция BCryptDeriveKeyPBKDF2 из CNG должна получать один параметр ULONGLONG (количество итераций), но в Delphi 2007 нет типа данных 64 бит как сишный ULONGLONG, максимальный размер типа в Delphi 2007 только 32 бит (кардинал). Поэтому и пришлось сделать ассемблерную вставку для вызова этой функции чтобы положить в стек корректное значение количества итераций: сначала младшее слово, а затем старшее (всегда 0)

Но почему-то функция BCryptDeriveKeyPBKDF2 мне всегда возвращает INVALID_PARAMETER если я указываю провайдера как BCRYPT_SHA1_ALGORITHM, и INVALID_HANDLE, если я указываю в BCryptOpenAlgorithmProvider криптографического провайдера BCRYPT_AES_ALGORITHM.

Что я делаю не так? Не тот криптографический провайдер указываю? Какой вообще аналог функции PBEKeySpec из Java на Windows? Как я могу получить ключ AES из пароля и соли с помощью криптографического API Windows Microsoft CNG?


Ответы (0 шт):