【问题标题】:Delphi Firemonkey LockBox3 AES-CBC, PC and Android result are different?Delphi Firemonkey LockBox3 AES-CBC,PC和Android结果不同?
【发布时间】:2019-01-01 13:52:45
【问题描述】:

我需要一个用于 devolop Firemonkey Moblie App 的 AES 库。我测试了 ElAES 和 LockBox3,一切正常,符合 PC,但在 FMX And​​roid 上,两个库都返回错误的密文。

测试数据(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;

【问题讨论】:

  • 考虑接受有帮助的答案,并查看以前的答案,接受是合适的。 1,请参阅reputation faq 投票给问题或答案向社区的其他人表明帖子很有趣、经过充分研究且有用。

标签: firemonkey delphi-xe lockbox-3


【解决方案1】:

加密和解密在原始字节上操作,而不是在字符上。

在加密 Unicode 字符串时,尤其是跨平台加密时,您必须在加密这些字节之前使用一致字节编码将字符编码为字节。

在解密 Unicode 字符串时,请确保在将解密的字节转换回字符时使用相同的字节编码。

在您的代码中,您使用 BytesOf() 将 Unicode 字符编码为字节。在内部,BytesOf() 使用TEncoding.Default 作为编码,在 Windows PC 上为TEncoding.ANSI,在其他平台上为TEncoding.UTF8。因此,如果您的输入字符串包含任何非 ASCII 字符,您最终会得到不同的结果。

我建议在所有平台上将BytesOf() 替换为TEncoding.UTF8.GetBytes()

plainBytes := TEncoding.UTF8.GetBytes(PKCS5Padding(edtPlaintext.Text, 16));

codec := NewCodec(TEncoding.UTF8.GetBytes(edtKey.Text));

【讨论】:

  • 谢谢回答,来自Debugger/Output: key/IV:30303030303030303030303030303030 plainBytes: 706C61696E5465787407070707070707,同一个两个平台,好像不是这个原因。
  • @86153 两个不同的加密库都不太可能在移动设备上都失败,更有可能是您在跨平台使用库的方式上做了不同的事情。您将不得不继续调试,以查看每个步骤中平台之间实际存在的差异。
  • @86153 顺便说一句,您不应该将 PKCS5 填充应用于您正在加密的 string,您应该在将其提供给库之前将填充应用于 plainBytes .同样,加密对字节进行操作,blocksize 适用于被加密的字节,而不是用于生成这些字节的字符,因此需要将填充添加到实际字节的末尾,而不是 string。这是一件微妙但重要的事情。
  • 感谢 Remy,最后我使用了 DCPcrypt2,它可以在 android 上运行。相关问题:stackoverflow.com/questions/43523262/…
  • @86153 其他问题与此问题不同。另一个问题对字节使用字符串缓冲区,并且遇到跨平台的索引问题。这个问题没有那样做。
猜你喜欢
  • 2015-05-18
  • 2012-06-06
  • 2017-03-18
  • 1970-01-01
  • 2018-12-31
  • 2022-11-16
  • 2015-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多