Шифрование библиотеки 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 проблемы с моим кодом:
из-за моего плохого понимания mcrypt и шифров в целом, MCRYPT_RIJNDAEL_256 относится к 128 битам блок и не относится к keysize. Мой правильный выбор должен был быть MCRYPT_RIJNDAEL_128, который является стандартом AES и также поддерживается DEC 3.0.
DEC имеет собственный вывод ключа по умолчанию, поэтому мне нужно было обойти его, чтобы мне не пришлось реализуйте его также в PHP. На самом деле я использую свой собственный алгоритм вывода ключей, который было легко воспроизвести в PHP (первые 32 символа sha1(key)).
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!