firemonkey - Delphi Firemonkey LockBox3 AES-CBC,PC和Android结果不同?
问题描述
我需要一个用于 devolop Firemonkey Moblie App 的 AES 库。我测试了 ElAES 和 LockBox3,一切正常,符合 PC,但在 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;
解决方案
加密和解密对原始字节而不是字符进行操作。
在加密 Unicode 字符串时,尤其是跨平台加密时,您必须使用一致的字节编码将字符编码为字节,然后再加密这些字节。
并且在解密 Unicode 字符串时,确保在将解密的字节转换回字符时使用相同的字节编码。
在您的代码中,您用于BytesOf()
将 Unicode 字符编码为字节。在内部,BytesOf()
用作TEncoding.Default
编码,TEncoding.ANSI
在 Windows PC 上但TEncoding.UTF8
在其他平台上。因此,如果您的输入字符串包含任何非 ASCII 字符,您最终会得到不同的结果。
我建议在所有平台上替换BytesOf()
为:TEncoding.UTF8.GetBytes()
plainBytes := TEncoding.UTF8.GetBytes(PKCS5Padding(edtPlaintext.Text, 16));
codec := NewCodec(TEncoding.UTF8.GetBytes(edtKey.Text));
推荐阅读
- cmake - Visual Studio 2017 - 如何设置安装目录
- node.js - 即使是一个简单的空管道也无法显示我的阶段
- javascript - 如何在ovh上对一个js页面收费?
- javascript - 收到 TS 错误,“类型 'TPage' 上不存在属性 'children'”
- jenkins - 如何检查 Jenkins 声明性管道中的密码参数是否为空?
- vb.net - system.net.mail.attachments 添加错误“抱歉出了点问题...”
- node.js - 尝试在 Pug 中迭代 JSON 但不断收到长度错误
- c# - 在 Jetbrains Rider IDE 中更改 .NET Framework
- scala - 时间戳到字符串转换数据框
- rust - Why does RefCell:borrow_mut result in a BorrowMutError when used on both sides of a short-circuiting boolean AND (&&)?