将system.string作为函数参数或常量传递的结果不同

时间:2015-01-13 23:26:49

标签: string delphi delphi-xe7

我一直在为一个非常奇怪的现象而奋斗数小时。首先,我正在使用TurboPower LockBox v10加密库(使用TLbRijndael),但我不相信这是错误的。我需要将密钥传递给加密/解密函数,所以我自然而然地创建了这样的函数:

function EncryptText(const S: String; const Key: String): String;
function DecryptText(const S: String; const Key: String): String;

奇怪的是,当加密似乎工作时,当我尝试解密时,它会返回完全垃圾。所以,在我长达数小时的旅途中寻找原因,我发现的东西绝对令我感到困惑。当我使用定义为常量的加密密钥时,一切正常。但是传递完全相同的值(也是system.string)作为函数中的参数,它不会。

总结一下:

  • 将密钥作为常量(system.string)传递时,一切正常。
  • 将密钥作为参数(system.string)传递时,结果是随机且不正确的(并且无法解密)。

这是我放在一起展示正在发生的事情的演示(我的第三个测试应用程序试图解决这个问题):

program EncDecDemo;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.Classes, System.SysUtils,
  LbCipher, LbClass, LbAsym, LbRSA;

const
  ENC_KEY = 'abcdefghijklmnopqrstubwxyz123456';

var
  EncComp: TLbRijndael;

function EncAsConst(const S: String): String;
begin
  EncComp.SetKey(ENC_KEY); //<-- works as expected
  Result:= EncComp.EncryptString(S);
end;

function DecAsConst(const S: String): String;
begin
  EncComp.SetKey(ENC_KEY); //<-- works as expected
  Result:= EncComp.DecryptString(S);
end;

function EncAsParam(const S: String; const Key: String): String;
begin
  EncComp.SetKey(Key); //<-- produces random results
  Result:= EncComp.EncryptString(S);
end;

function DecAsParam(const S: String; const Key: String): String;
begin
  EncComp.SetKey(Key); //<-- produces random results
  Result:= EncComp.DecryptString(S);
end;

var
  InputStr: String;
  EncrStr: String;
  DecrStr: String;
  Ctr: Integer;

begin
  EncComp:= TLbRijndael.Create(nil);
  try
    EncComp.Encoding:= TEncoding.UTF8;
    EncComp.KeySize:= ks256;
    WriteLn('Enter a string to encrypt: ');
    ReadLn(Input, InputStr);
    WriteLn;
    WriteLn('Encrypting as constants...');
    EncrStr:= EncAsConst(InputStr);
    DecrStr:= DecAsConst(EncrStr);
    WriteLn('Pass 1:   Enc = '+EncrStr+'   Dec = '+DecrStr);
    EncrStr:= EncAsConst(InputStr);
    DecrStr:= DecAsConst(EncrStr);
    WriteLn('Pass 2:   Enc = '+EncrStr+'   Dec = '+DecrStr);
    EncrStr:= EncAsConst(InputStr);
    DecrStr:= DecAsConst(EncrStr);
    WriteLn('Pass 3:   Enc = '+EncrStr+'   Dec = '+DecrStr);
    WriteLn;
    WriteLn('Encrypting as parameters...');
    EncrStr:= EncAsParam(InputStr, ENC_KEY);
    WriteLn('Pass 1:   Enc = '+EncrStr);
    EncrStr:= EncAsParam(InputStr, ENC_KEY);
    WriteLn('Pass 2:   Enc = '+EncrStr);
    EncrStr:= EncAsParam(InputStr, ENC_KEY);
    WriteLn('Pass 3:   Enc = '+EncrStr);
    EncrStr:= EncAsParam(InputStr, ENC_KEY);
    WriteLn('Pass 4:   Enc = '+EncrStr);
    EncrStr:= EncAsParam(InputStr, ENC_KEY);
    WriteLn('Pass 5:   Enc = '+EncrStr);
    EncrStr:= EncAsParam(InputStr, ENC_KEY);
    WriteLn('Pass 6:   Enc = '+EncrStr);
    WriteLn;
    WriteLn('Press enter to exit');
    ReadLn;
  finally
    EncComp.Free;
  end;
end.

enter image description here

如您所见,当在函数中传递与参数完全相同的值时,每次都会产生不同的结果。让我重申一下,完全相同的值

我唯一的结论是system.string当用作常量(const ENC_KEY = 'value';)时,在某种程度上是一个不同的大小或类型,而不是用作变量。

当我查看SetKey程序时,这是一个简单的Move电话......

procedure TLbRijndael.SetKey(const Key);
begin
  Move(Key, FKey, FKeySizeBytes);
end;

(其中FKeyarray [0..31] of Byte;

这里出了什么问题以及如何解决?

1 个答案:

答案 0 :(得分:3)

procedure TLbRijndael.SetKey(const Key);
begin
  Move(Key, FKey, FKeySizeBytes);
end;

您传递给此无类型参数的问题是什么。传递true常量时,将传递文本。传递字符串变量时,将传递文本的地址,而不是文本本身。那是因为字符串变量实际上是指向文本的指针。

我相信您需要停止使用字符串变量作为键并提供二进制缓冲区。

考虑这个程序来说明问题:

{$APPTYPE CONSOLE}

uses
  SysUtils;

procedure Foo(const Key);
var
  buff: Pointer;
begin
  Move(Key, buff, SizeOf(buff));
  Writeln(Format('%p', [buff]));
end;

const
  ENC_KEY = 'abcdefghijklmnopqrstubwxyz123456';

var
  str: string = ENC_KEY;

begin
  Foo(ENC_KEY);
  Foo(str);
  Writeln(Format('%p', [Pointer(str)]));
end.

在我的机器上输出为:

00620061
00419D48
00419D48

第一行是编码为UTF-16LE的文本'ab'。第二行是str的地址,由第三行确认。

我想强调文本和二进制之间区别的重要性。