PHP для Delphi и обратно Шифрование-дешифрование с использованием Rijndael - PullRequest
2 голосов
/ 13 августа 2011

У меня проблемы с расшифровкой строк, отправляемых из PHP в Delphi с использованием шифра rijndael.Я использую mcrypt на стороне PHP и DCP_rijndael на стороне Delphi.

На данный момент у меня есть следующий код.

PHP:

function encRJ($key, $iv, $data)
{
    $r = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
    $r = base64_encode($r);
    return $r;
}

И вDelphi:

function decRJ(Data: string; Key: string; IV: string): string;
var ciph: TDCP_rijndael;
begin
  Data := Base64DecodeStr(Data);

  ciph:= TDCP_rijndael.Create(Self);
  ciph.Init(Key[1], 256, @IV[1]);
  ciph.DecryptCBC(Data[1], Data[1], Length(Data));
  ciph.Free;

  Result := Data;
end;

Я попытался использовать несколько модулей в Интернете, реализующих шифр, и обнаружил, что большинство людей говорят о компонентах DCP.Несмотря на это, мне не удалось заставить его правильно расшифровать.Я пытался использовать байтовые массивы для параметров, AnsiStrings, WideStrings и т. Д., Но, к сожалению, не повезло.

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

Ответы [ 2 ]

8 голосов
/ 16 августа 2011

Кажется, я потратил слишком много времени на это, но ...

Ваша проблема в размере блока.TDCP_rijndael эквивалентен MCRYPT_RIJNDAEL_128 (не _256).Значение '256' в вызове ciph.Init (...) все еще корректно.Кроме того, это выглядит довольно хорошо.То есть, предполагая, что вы используете ansistrings для ключа / iv или используете не-Unicode Delphi.Для версий Unphode Delphi я был бы склонен использовать TBytes и ключ [0] / iv [0].

Заполнение может все еще быть проблемой.Если так, то вот что я исказил на основе страниц справочника по PHP и некоторых проб и ошибок.

PHP:

function Encrypt($src, $key, $iv)
{
  $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
  //echo "Block size: " . $block . "\r\n";
  $pad = $block - (strlen($src) % $block);
  $src .= str_repeat(chr($pad), $pad);  

  $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $src, MCRYPT_MODE_CBC, $iv);
  $r = base64_encode($enc);
  return $r;
}

function Decrypt($src, $key, $iv)
{
  $enc = base64_decode($src);
  $dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv);

  $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, 'cbc');
  $pad = ord($dec[($len = strlen($dec)) - 1]);
  return substr($dec, 0, strlen($dec) - $pad);
}

Delphi:

function DecryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
  key, iv, src, dest: TBytes;
  cipher: TDCP_rijndael;
  slen, pad: integer;
begin
  //key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
  //iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
  key := TEncoding.ASCII.GetBytes(AKey);
  iv := TEncoding.ASCII.GetBytes(AIv);

  src := Base64DecodeBytes(TEncoding.UTF8.GetBytes(Data));

  cipher := TDCP_rijndael.Create(nil);
  try
    cipher.CipherMode := cmCBC;
    slen := Length(src);
    SetLength(dest, slen);
    cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES
    cipher.Decrypt(src[0], dest[0], slen);
    // Remove the padding. Get the numerical value of the last byte and remove
    // that number of bytes
    pad := dest[slen - 1];
    SetLength(dest, slen - pad);

    // Base64 encode it
    result := TEncoding.Default.GetString(dest);
  finally
    cipher.Free;
  end;
end;

function EncryptData(Data: string; AKey: AnsiString; AIv: AnsiString): string;
var
  cipher: TDCP_rijndael;
  key, iv, src, dest, b64: TBytes;
  index, slen, bsize, pad: integer;
begin
  //key := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AKey));
  //iv := Base64DecodeBytes(TEncoding.UTF8.GetBytes(AIv));
  key := TEncoding.ASCII.GetBytes(AKey);
  iv := TEncoding.ASCII.GetBytes(AIv);

  src := TEncoding.UTF8.GetBytes(Data);

  cipher := TDCP_rijndael.Create(nil);
  try
    cipher.CipherMode := cmCBC;
    // Add padding.
    // Resize the Value array to make it a multiple of the block length.
    // If it's already an exact multiple then add a full block of padding.
    slen := Length(src);
    bsize := (cipher.BlockSize div 8);
    pad := bsize - (slen mod bsize);
    Inc(slen, pad);
    SetLength(src, slen);
    for index := pad downto 1 do
    begin
      src[slen - index] := pad;
    end;

    SetLength(dest, slen);
    cipher.Init(key[0], 256, @iv[0]); // DCP uses key size in BITS not BYTES
    cipher.Encrypt(src[0], dest[0], slen);

    b64 := Base64EncodeBytes(dest);
    result := TEncoding.Default.GetString(b64);
  finally
    cipher.Free;
  end;
end;

Функции PHP и Delphi теперь дают мне один и тот же ответ.

EDIT

Base64DecodeBytes - это фрагмент кода, который я добавил в модуль DCP Base64:

function Base64DecodeBytes(Input: TBytes): TBytes;
var
  ilen, rlen: integer;
begin
  ilen := Length(Input);
  SetLength(result, (ilen div 4) * 3);
  rlen := Base64Decode(@Input[0], @result[0], ilen);
  // Adjust the length of the output buffer according to the number of valid
  // b64 characters
  SetLength(result, rlen);
end; 

РЕДАКТИРОВАТЬ 2018 (Воскрешение мертвых ...):

По запросу, вот метод кодирования, не проверенный и извлеченный прямо из старого файла, который я нашел.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: ему много лет, и он не проверен в недавней памяти и не используется со времени Delphi 2010. Вероятно, сейчас есть много лучших альтернатив.Используйте на свой страх и риск.

function Base64EncodeBytes(Input: TBytes): TBytes;
var
  ilen: integer;
begin
  ilen := Length(Input);
  SetLength(result, ((ilen + 2) div 3) * 4);
  Base64Encode(@Input[0], @result[0], ilen);
end;
0 голосов
/ 14 августа 2011

Ни ваш PHP, ни ваши методы Delphi не отображают никаких отступов.Если отступы по умолчанию отличаются, у вас будут проблемы.Явно укажите PKCS7 (или PKCS5) для обоих.

Комментарий Грега о результате декодирования Base64 является правильным.Вы предоставляете зашифрованный зашифрованный текст для вашего метода decRJ ().Это будут случайные байты.Попытка преобразовать его в UTF-8 будет искажать его настолько, что его невозможно будет расшифровать.Входящий зашифрованный текст должен быть преобразован из Base64 напрямую в байтовый массив.Cyphertext не является символьной строкой, поэтому его необходимо преобразовать в Base64 для передачи в виде текста.Это будет только текст снова после того, как он был расшифрован обратно в открытый текст.

...