Шифрование библиотеки Delphi DEC (Rijndael)

Я пытаюсь использовать библиотека DEC 3.0 (Delphi Шифрование Compedium Часть I) для шифрования данных в Delphi 7 и отправки его в php-скрипт через POST, где я расшифровываю его с помощью mcrypt (RIJNDAEL_256, режим ECB).

Delphi часть:

uses Windows, DECUtil, Cipher, Cipher1;

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create(KeyStr, nil);
  RCipher.Mode:= cmECB;
  Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
  RCipher.Free;
end;

PHP-части:

function decryptMsgContent($msgContent, $sKey) {
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
}

проблема в том, что дешифрование с PHP не работает, а вывод является тарабарщиной, отличающейся от фактического данные.

конечно, Дельфы Key и PHP $Key - это та же строка из 24 символов.

теперь я знаю, что DEC 3.0 старый и устаревший, и я не эксперт в шифровании и не могу сказать, является ли inplementation на самом деле Rijndael 256. Возможно, кто-то может сказать мне, чем эта реализация отличается от PHP mcrypt w/ RIJNDAEL_256. Возможно, размер ключа отличается или размер блока, но не может сказать это из кода. Вот отрывок из Cipher1.pas:

const
{ don’t change this }
  Rijndael_Blocks =  4;
  Rijndael_Rounds = 14;

class procedure TCipher_Rijndael.GetContext(var ABufSize, AKeySize, AUserSize: Integer);
begin
  ABufSize := Rijndael_Blocks * 4;
  AKeySize := 32;
  AUserSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(Integer) * 2;
end;

сторона вопроса:

Я знаю, что режим ECB не рекомендуется, и я буду использовать CBC, как только я получу работу ECB. Вопрос в том, должен ли я передавать сгенерированный IV в Delphi скрипту PHP? Или достаточно знать ключ, как для ЕЦБ?

3 ответов


вы вызываете TCipher.Create (const Password: String; AProtection: TProtection); конструктор, который вычислит хэш пароля перед передачей его методу Init, который выполняет стандартное расписание ключей реализованного алгоритма. Чтобы переопределить этот вывод ключа, используйте:

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('', nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmECB;
  Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
  RCipher.Free;

конец;


хорошо, Итак, чтобы подвести итог, было 3 проблемы с моим кодом:

  1. из-за моего плохого понимания mcrypt и шифров в целом, MCRYPT_RIJNDAEL_256 относится к 128 битам блок и не относится к keysize. Мой правильный выбор должен был быть MCRYPT_RIJNDAEL_128, который является стандартом AES и также поддерживается DEC 3.0.

  2. DEC имеет собственный вывод ключа по умолчанию, поэтому мне нужно было обойти его, чтобы мне не пришлось реализуйте его также в PHP. На самом деле я использую свой собственный алгоритм вывода ключей, который было легко воспроизвести в PHP (первые 32 символа sha1(key)).

  3. DEC не заполняет открытый текст кратным размеру блока шифра, как ожидает mcrypt, поэтому мне пришлось сделать это вручную.

обеспечивать работая код ниже:

Delphi:

uses Windows, DECUtil, Cipher, Cipher1, CryptoAPI;

function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
    KeyStr: string;
begin
  Result:= '';
  try
    // key derivation; just making sure to feed the cipher a 24 chars key
    HashStr(HASH_SHA1, Key, KeyStr);
    KeyStr:= Copy(KeyStr, 1, 24);
    RCipher:= TCipher_Rijndael.Create('', nil);
    RCipher.Init(Pointer(KeyStr)^, Length(KeyStr), nil);
    RCipher.Mode:= cmECB;
    Result:= RCipher.CodeString(MsgData + StringOfChar(#0,16-(Length(MsgData) mod 16)), paEncode, fmtMIME64);
    RCipher.Free;
  except
  end;
end;

PHP:

function decryptMsgContent($msgContent, $sKey) {
    $sKey = substr(sha1(sKey), 0, 24);
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}

256-битный ключ, который я нашел, - это 32 charachters или 32 байта. Не 24. Это может быть проблемой.

[EDIT]

Я объединил все идеи (ansistring и т. д.) в одну идею с исправлением.

кроме того, вы используете codestring (--это должен быть Encodestring (

я вставил рабочий источник шифрования и дешифрования ниже:


function EncryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('', nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmCBC;
  Result:= RCipher.EncodeString(MsgData);
  RCipher.Free;
end;

function DecryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
  RCipher:= TCipher_Rijndael.Create('',nil);
  RCipher.Init(Pointer(Key)^,Length(Key),nil);
  RCipher.Mode:= cmCBC;
  Result:= RCipher.DecodeString(MsgData);
  RCipher.Free;
end;

используйте это с ключом 32 charachter, и вы получите правильное шифрование и дешифрование.

для хранения и использования зашифрованных данных в качестве строки вы можете использовать Base64Encode (

но не забудьте Base64Decode перед расшифровкой.

Это тот же самый метод, необходимый для Blowfish. Иногда charachters на самом деле похожи на backspace и выполняют функцию, а не отображаются на экране. Base64Encode в основном преобразует charachters в то, что вы можете отображать в тексте.

перед передачей закодированного данные через интернет или в другое приложение на том же или другом языке, необходимо base64encode и декодировать, чтобы не потерять данные. Не забывайте об этом и в PHP!