LockBox 3 Шифрование не соответствует онлайн-инструменту, подозрительное заполнение или проблема с ключом - PullRequest
1 голос
/ 13 марта 2019

У меня есть клиент, предоставляющий API, который предписывает отправлять им данные, которые должны быть зашифрованы с помощью AES, 128-битного ключа, режима ECB и PKCS5Padding . Я пытаюсь использовать LockBox 3 в Delphi 10.3 Rio и не получаю ту же зашифрованную строку, что и онлайн-инструмент тестирования , на который они указывали для проверки. Это близко, но не совсем там.

С большим количеством чтения здесь о Unicode , PKCS5Padding и связанных вопросах , я пришел к концу того, что попробовать. Я должен признать, что я не особо разбираюсь в шифровании и столько читал, сколько смог, чтобы обдумать это, прежде чем задавать вопросы.

Есть пара вещей, на которые я хотел бы получить подтверждение:

  • Разница между паролем и ключом . Я читал, что LB3 использует пароль для генерации ключа, но у меня есть конкретные инструкции от клиента о том, как должен генерироваться ключ, поэтому я создал свой собственный ключ в кодировке Base64 и вызываю InitFromStream для его инициализации. Я полагаю, что это занимает место установки пароля, правильно? Или, может быть, пароли используются только асимметричными шифрами (не симметричными, как AES)?
  • PKCS5Padding : меня беспокоило то, что я прочитал на справочном сайте LB3 , что заполнение выполняется разумно, в зависимости от выбора шифра, режима цепочки и т. Д. значит нет способа заставить его использовать определенный метод заполнения? Я преобразовал данные в байтовый массив и реализовал его с помощью собственного PKCS5Padding, но я думаю, что LB3 все еще может быть дополнен. (Я пытался просмотреть код и не нашел никаких доказательств того, что он делает.)

Должен ли я использовать другую библиотеку шифрования в Delphi для этого? Я извлек DelphiEncryptionCompendium и DcPCryptV2 , но я обнаружил, что LB3, похоже, имеет большинство поддержки, и я чувствовал, что это было легче всего работать, особенно в моей версии Delphi для Unicode. Кроме того, я использовал LockBox 2 довольно давно в прошлых годах, поэтому я подумал, что он будет более знакомым (оказалось, что это не так).

Чтобы проиллюстрировать то, что я пробовал, я извлек свой код из проекта в консольное приложение. Возможно, мои предположения выше верны, и в моем коде или в параметре LB3 есть явная ошибка, я не понимаю, что кто-то укажет:

program LB3ConsoleTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes, System.NetEncoding,
  uTPLb_Codec, uTPLb_CryptographicLibrary,
  uTPLb_StreamUtils, uTPLb_Constants;

var
  Codec: TCodec;
  CryptographicLibrary: TCryptographicLibrary;

function PKCS5PadStringToBytes(RawData: string; const PadSize: Integer): TBytes;
{ implement our own block padding }
var
  DataLen: Integer;
  PKCS5PaddingCount: ShortInt;
begin
  Result := TEncoding.UTF8.GetBytes(RawData);
  DataLen := Length(RawData);

  PKCS5PaddingCount := PadSize - DataLen mod PadSize;
  if PKCS5PaddingCount = 0 then
    PKCS5PaddingCount := PadSize;
  Inc(DataLen, PKCS5PaddingCount);

  SetLength(Result, DataLen);
  FillChar(Result[DataLen - PKCS5PaddingCount], PKCS5PaddingCount, PKCS5PaddingCount);
end;

procedure InitializeAESKey(const AESKey: string);
{ convert the string to a byte array,
  use that to initialize a ByteStream,
  and call LB3's InitFromStream }
var
  AESKeyBytes: TBytes;
  AESKeyStream: TBytesStream;
begin
  AESKeyBytes := TEncoding.UTF8.GetBytes(AESKey);
  AESKeyStream := TBytesStream.Create(AESKeyBytes);
  Codec.InitFromStream(AESKeyStream);
end;

const
  RawData = '{"invoice_id":"456456000018047","clerk_id":"0023000130234234","trans_amount":1150034534,"cust_code":"19455605000987890641","trans_type":"TYPE1"}';
  AESKeyStr = 'CEAA31AD1EE4BDC8';
var
  DataBytes: TBytes;
  DataStream: TBytesStream;
  ResultStream: TBytesStream;
  ResultBytes: TBytes;
  Base64Encoder: TBase64Encoding;
begin
  // create the LockBox3 objects
  Codec := TCodec.Create(nil);
  CryptographicLibrary := TCryptographicLibrary.Create(nil);
  try
    // setup LB3 for AES, 128-bit key, ECB
    Codec.CryptoLibrary := CryptographicLibrary;
    Codec.StreamCipherId := uTPLb_Constants.BlockCipher_ProgId;
    Codec.BlockCipherId  := Format(uTPLb_Constants.AES_ProgId, [128]);
    Codec.ChainModeId    := uTPLb_Constants.ECB_ProgId;

    // prep the data, the key, and the resulting stream
    DataBytes := PKCS5PadStringToBytes(RawData, 8);
    DataStream := TBytesStream.Create(DataBytes);
    InitializeAESKey(AESKeyStr);
    ResultStream := TBytesStream.Create;

    // ENCRYPT!
    Codec.EncryptStream(DataStream, ResultStream);

    // take the result stream, convert it to a byte array
    ResultStream.Seek(0, soFromBeginning);
    ResultBytes := Stream_to_Bytes(ResultStream);

    // convert the byte array to a Base64-encoded string and display
    Base64Encoder := TBase64Encoding.Create(0);
    Writeln(Base64Encoder.EncodeBytesToString(ResultBytes));

    Readln;
  finally
    Codec.Free;
    CryptographicLibrary.Free;
  end;
end.

Эта программа генерирует зашифрованную строку длиной 216 символов, и только последние 25 отличаются от того, что создает онлайн-инструмент .

Почему?

1 Ответ

1 голос
/ 13 марта 2019

AES использует 16-байтовые блоки, а не 8-байтовые.

Таким образом, вам нужен PKCS7 с заполнением 16 байтов, а не PKCS5, который установлен на 8 байтов.

Пожалуйста, попробуйте

DataBytes := PKCS5PadStringToBytes(RawData, 16);

Также рассмотрите возможность изменения режима сцепления с ECB, , который является довольно слабым, поэтому его следует избегать при любой серьезной работе .

...