Delphi Firemonkey LockBox3 AES-CBC, ПК и Android результат отличается?
Мне нужна библиотека AES для Devolop Firemonkey Moblie App. Я тестировал ElAES и LockBox3, все отлично работает на ПК, но на FMX Android обе библиотеки возвращают неправильный зашифрованный текст.
Тестовые данные (AES128CBC PKCS5Padding):
plainText: 'plainText' - edtPlaintext.Text
key: '0000000000000000' - edtKey.Text
IV: '0000000000000000' - edtIV.Text
cipherText: hex - 'DD0A2A20616162697B8B4DF53483F1D2',
base64 - '3QoqIGFhYml7i031NIPx0g=='
Тестовый код:
Это тестовый код, включенный в LockBox3, связанный с: https://github.com/TurboPack/LockBox3, функцией 'EncryptMemory', которая каждый раз возвращает Android незафиксированный зашифрованный текст, что-то нужно заметить?
uses uTPLb_Codec, uTPLb_CryptographicLibrary, uTPLb_Constants, uTPLb_StreamUtils;
type
TCustomPadder = class(TObject)
private
FIV: TBytes;
public
constructor Create(const AIV: TBytes);
procedure OnSetIV(Value: TMemoryStream);
end;
constructor TCustomPadder.Create(const AIV: TBytes);
begin
FIV := AIV
end;
procedure TCustomPadder.OnSetIV(Value: TMemoryStream);
begin
Value.Size := Length(FIV);
Value.Position := 0;
Value.WriteBuffer(FIV, Length(FIV))
end;
function NewCodec(key: TBytes): TCodec;
var
codec: TCodec;
cryptographicLibrary: TCryptographicLibrary;
keyStream: TStream;
padder: TCustomPadder;
begin
cryptographicLibrary := TCryptographicLibrary.Create(nil);
// basic
codec := TCodec.Create(nil);
codec.BlockCipherId := Format(AES_ProgId, [128]);
codec.ChainModeId := CBC_ProgId;
codec.CryptoLibrary := cryptographicLibrary;
codec.StreamCipherId := BlockCipher_ProgId;
// extend
padder := TCustomPadder.Create(bytesof('0000000000000000'));
keyStream := TMemoryStream.Create;
keyStream.WriteBuffer(key, Length(key));
keyStream.Position := 0;
codec.OnSetIV := padder.OnSetIV;
codec.InitFromStream(keyStream);
result := codec;
end;
function PKCS5Padding(ciphertext: string; blocksize: integer): string;
var
builder: TStringBuilder;
padding: integer;
i: integer;
begin
builder := TStringBuilder.Create(ciphertext);
padding := blocksize - (builder.Length mod blocksize);
for i := 1 to padding do
begin
builder.Append(Char(padding));
end;
result := builder.ToString;
builder.DisposeOf;
end;
function BytesToHexStr(bytes: TBytes): string;
var
i: integer;
begin
result := '';
for i := 0 to Length(bytes) - 1 do
result := result + bytes[i].ToHexString(2);
end;
procedure TformAEST.btnEncryptClick(Sender: TObject);
var
codec: TCodec;
plainBytes, cipherBytes: TBytes;
cipherMemory: TStream;
cipherBytesLen: integer;
begin
cipherMemory := TMemoryStream.Create;
plainBytes := bytesof(PKCS5Padding(edtPlaintext.Text, 16));
codec := NewCodec(bytesof(edtKey.Text));
codec.Begin_EncryptMemory(cipherMemory);
codec.EncryptMemory(plainBytes, Length(plainBytes));
codec.End_EncryptMemory;
cipherMemory.Position := 8;
cipherBytesLen := cipherMemory.Size - 8;
SetLength(cipherBytes, cipherBytesLen);
cipherMemory.ReadBuffer(cipherBytes, cipherBytesLen);
edtCiphertext.Text := BytesToHexStr(cipherBytes);
end;
2 ответа
Шифрование и дешифрование работают с необработанными байтами, а не с символами.
При шифровании строк Unicode, особенно на разных платформах, вы должны кодировать символы в байты, используя согласованное кодирование байтов, а затем шифровать эти байты.
А при дешифровании строк Unicode обязательно используйте ту же самую байтовую кодировку при преобразовании дешифрованных байтов обратно в символы.
В вашем коде вы используете BytesOf()
кодировать символы Юникода в байты. Внутренне BytesOf()
использования TEncoding.Default
в качестве кодировки, которая TEncoding.ANSI
на ПК с Windows, но есть TEncoding.UTF8
на других платформах. Таким образом, если ваши входные строки содержат не-ASCII-символы, вы получите другие результаты.
Я предлагаю заменить BytesOf()
с TEncoding.UTF8.GetBytes()
на всех платформах:
plainBytes := TEncoding.UTF8.GetBytes(PKCS5Padding(edtPlaintext.Text, 16));
codec := NewCodec(TEncoding.UTF8.GetBytes(edtKey.Text));
По вашему требованию мне нужно создать мобильное приложение с Delphi в последние недели и попытаться выяснить, как шифровать в Delphi и расшифровывать в серверном приложении.
Я выбираю Laravel 8 в качестве фреймворка серверных приложений, Delphi Alexandria в качестве клиентского RAD-инструмента.
С помощью некоторых тестов я обнаружил, что Laravel использует функцию openssl_decrypt для расшифровки шифра, а байты заполнения подчиняются специальному правилу.
Следовательно, я использую DEC (компендиум Delphi Encryption Compendium, вы можете скачать его бесплатно с помощью диспетчера пакетов Delphi GitIt или с GitHub, ссылка, которую я прикрепил) для генерации шифра с APP_Key, сгенерированным в Laravel (или вы можете сгенерировать 32-байтовый ключ самостоятельно ), сгенерированный шифр может быть успешно расшифрован Laravel.
Я также загружаю свой образец проекта в свой репозиторий GitHub , если вам нужно использовать AES-256-CBC в вашем проекте Delphi FireMonkey, пожалуйста, наслаждайтесь им.