Reputation: 1
I need a AES library for devolop Firemonkey Moblie App. I tested ElAES and LockBox3, everything works fine complie to PC, But on FMX Android both library return wrong ciphertext.
Test Data (AES128CBC PKCS5Padding):
plainText: 'plainText' - edtPlaintext.Text
key: '0000000000000000' - edtKey.Text
IV: '0000000000000000' - edtIV.Text
cipherText: hex - 'DD0A2A20616162697B8B4DF53483F1D2',
base64 - '3QoqIGFhYml7i031NIPx0g=='
Test Code:
This is test code reley on LockBox3, related: https://github.com/TurboPack/LockBox3, function 'EncryptMemory' return unfixed ciphertext each time on Android, something need to notice?
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;
Upvotes: -1
Views: 1192
Reputation: 582
As your requirement, I need to create a mobile app with Delphi in recent weeks, and try to figure out how to encrypt in Delphi, and decrypt in server side application.
I choose Laravel 8 as my server side application framework, Delphi Alaxandria as client RAD tool.
With some tests, I found that Laravel used openssl_decrypt function to decrypt cipher, and padding byte is under special rule.
Hence, I use DEC (Delphi Encryption Compendium, you can download it free with Delphi GitIt package manager or from GitHub, the link I attached) to generate the cipher, with the APP_Key generated in Laravel (or you can generate 32 bytes key by yourself), the generated cipher can be decrypted by Laravel successfully.
I also upload my sample project to my GitHub repository, if you need to use AES-256-CBC in your Delphi FireMonkey project, please enjoy it.
Upvotes: 0
Reputation: 596527
Encryption and decryption operate on raw bytes, not on characters.
When encrypting Unicode strings, especially across platforms, you have to encode the characters to bytes using a consistent byte encoding before then encrypting those bytes.
And when decrypting Unicode strings, make sure to use that same byte encoding when converting the decrypted bytes back into characters.
In your code, you are using BytesOf()
to encode Unicode characters to bytes. Internally, BytesOf()
uses TEncoding.Default
as the encoding, which is TEncoding.ANSI
on Windows PCs but is TEncoding.UTF8
on other platforms. So, if your input strings contain any non-ASCII characters, you will end up with different results.
I suggest replacing BytesOf()
with TEncoding.UTF8.GetBytes()
on all platforms:
plainBytes := TEncoding.UTF8.GetBytes(PKCS5Padding(edtPlaintext.Text, 16));
codec := NewCodec(TEncoding.UTF8.GetBytes(edtKey.Text));
Upvotes: 2