复制加密算法

4

(我曾尝试在密码学堆栈交换上提问,但他们认为那里不合适。)

我正在尝试使用标准.NET库在C#中复制一个用Delphi编写的算法。据说这个Delphi算法通过了标准参考测试。它应该是AES/CBC;但是,我一直无法使用System.Security.Cryptography.AesManaged来复制结果。

以下是使用简单输入调用它的示例:

function StringToHex(S: string): string;
var
  i: integer;
begin
  Result := '';
  for i := 1 to Length(S) do
    Result := Result + IntToHex(Ord(S[i]), 2);
end;

var
  PlainText:     AnsiString;
  AES:           TAES;
  Key:           TAESKey256;  // array[0..31] of byte;
  InitialVector: TAESBuffer;  // array[0..15] of byte;
  InputStream:   TStringStream;
  OutputStream:  TStringStream;

begin
  PlainText := 'test';
  FillChar(Key, SizeOf(Key), 1);
  FillChar(InitialVector, SizeOf(InitialVector), 2);

  AES          := TAES.Create;
  InputStream  := TStringStream.Create(PlainText);
  OutputStream := TStringStream.Create('');

  try
    AES.EncryptAESStreamCBC(InputStream, 0, Key, InitialVector, OutputStream);

    Writeln(StringToHex(OutputStream.DataString));
    Readln;
  finally
    InputStream.Free;
    OutputStream.Free;
    AES.Free;
  end;
end.

输出:

CEE3684F05D02E5A0930CED21F76075A

这段代码似乎基于Delphi Spring Framework

我该如何在.NET中复制它?

我已经尝试过的方法(甚至转换为字节以避免任何Unicode问题):

public byte[] Encrypt()
{
    using (var algorithm = new AesManaged())
    {
        // Default is CBC
        algorithm.Key = Enumerable.Repeat((byte)0x01, 32).ToArray();
        algorithm.IV = Enumerable.Repeat((byte)0x02, 16).ToArray();

        using (ICryptoTransform encryptor = algorithm.CreateEncryptor(algorithm.Key, algorithm.IV))
        {
            using (var stream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
                {
                    using (var writer = new BinaryWriter(cryptoStream))
                    {
                        writer.Write(new byte[] {0x74, 0x65, 0x73, 0x74});
                    }
                    return stream.ToArray();
                }
            }
        }
    }
}

这会导致不同的输出结果,但是(第一个字节是x6D)。

我不知道具体问题是什么,但你可以考虑提供一个Delphi dll给C#作为快速解决方案。在Delphi中完成工作,在C#中调用它。 - Mehmet Fide
你确定算法(AesManaged的实例)默认具有正确的块大小/密钥大小吗?应该分别为128/256。 - Andrei Galatyn
3个回答

3

在另一个任务中,有人提到了C#的默认设置:

BlockSize = 128;
Mode = System.Security.Cryptography.CipherMode.CBC;
Padding = System.Security.Cryptography.PaddingMode.PKCS7

在Delphi中,我找不到更改PaddingMode的选项,我猜它用零填充。 如果是正确的话,那么你应该在C#中切换填充。
System.Security.Cryptography.PaddingMode.Zeros

抱歉,无法检查,这只是假设。

更新:正如shunty所提到的那样,它可以工作,只需添加一行:

algorithm.Padding = PaddingMode.Zeros;

2
尽管我很想赚点功劳,但这就是答案。我用LINQPad快速编写了一个脚本进行了检查,你只需要添加 "algorithm.Padding = PaddingMode.Zeros;" (不要加e!)即可。完成啦。 - shunty
确认。我只需要在解密时添加 TrimEnd('\0')。非常感谢你! - TrueWill
1
@TrueWill 没关系 :) 但我认为 TrimEnd 不是100%安全的。如果真实数据的最后一个字节/字节是0,则您将与填充一起删除它们。可能更好的方法是使用 PaddingMode 属性。 - Andrei Galatyn
同意 - 使用二进制数据削减不安全。我处理的是文本数据;在使用PaddingMode.Zeros时,解密结果在这种情况下会填充到长度为16。 - TrueWill

3

如果你想在Delphi中使用Spring框架,我的建议是创建一个Win32 [Library] DLL,然后在你的.NET应用程序中使用该DLL。这听起来比在C#中重新编写代码(重复造轮子?)更为合理。


+1 - 我正在考虑这个。我希望这个库是在使用标准的AES(或Rijndael)算法,而我只是没有正确地设置.NET。能够使用标准、经过充分测试的Microsoft库会很好。 - TrueWill
4
Delphi库的结果可以从一个我假定不使用同一库的工具得到相同的结果:http://aes.online-domain-tools.com/link/228f72gGd89UfPMaS/。在C#中重写代码似乎并不像是重新发明轮子。相反,它是找到使用系统中已经构建的轮子的方法的问题。 - Rob Kennedy

2
您确定TStringStream返回的AnsiString 'test' 正好是4个字节吗? 多字节编码和/或零终止会导致与C#代码中使用的另一个纯文本字节数组不同。

+1 对于一个非常好的问题;我刚刚验证了InputStream.Bytes是($74,$65,$73,$74)。 - TrueWill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接