【问题标题】:How can I convert this XOR-encryption function from Delphi to C#?如何将此 XOR 加密函数从 Delphi 转换为 C#?
【发布时间】:2025-12-10 07:40:01
【问题描述】:

以下 Delphi 例程最初来自很久以前的 CompuServe 帖子,用于加密我们数据库中的各种信息。下面是 Delphi 2007 和(感谢一些 SO 对 Unicode 差异的帮助)Delphi XE 版本。

我们一直在尝试将其转换为 C#,并且已经接近了,但我们在某个地方遗漏了一些东西。不幸的是,我们的 Delphi 人(我)不懂 C#,而 C# 人是 Delphi 的新手。 C# 没有(似乎)有 AnsiString 的概念,所以解决方案可能会涉及到 byte 或 char 数组?

我们非常感谢在将其转换为 C# 方面的任何帮助。

Delphi 2007 版 (ASCII)

function EncodeDecode(Str: string): string;
const
  Hash: string = '^%12hDVjED1~~#29afdmSD`6ZvUY@hbkDBC3fn7Y7euF|R7934093*7a-|-  Q`';
var
  I: Integer;
begin
  for I := 1 to Length (Str) do
    Str[I] := chr (ord (Str[I]) xor not (ord (Hash[I mod Length (Hash) + 1])));
  Result := Str;
end;

Delphi XE 版 (Unicode)

function TfrmMain.EncodeDecode(Str: AnsiString): AnsiString;
const
  Hash: string = '^%12hDVjED1~~#29afdmSD`6ZvUY@hbkDBC3fn7Y7euF|R7934093*7a-|-  Q`';
var
  I: Integer;
begin
  Result := Str;
  for I := 1 to Length (Result) do
    Result[I] := AnsiChar (ord (Result[I]) xor not (Ord (Hash[I mod Length (Hash) + 1])));
end;

【问题讨论】:

  • 请注意:由于现在您的加密方法包括密钥(这是您的“哈希”字符串)是完全公开的,您应该尽快更改此字符串,否则您的数据在都针对攻击者。但是你可以完全切换到更好的算法。
  • 谢谢 Paŭlo - 在发帖之前我弄坏了钥匙。我们的实际密钥大不相同。

标签: c# delphi encryption


【解决方案1】:

我也不懂 C#,所以这可能是非常不习惯的。

static string EncodeDecode(string str)
{
    byte[] hash = new byte[63] { 94, 37, 49, 50, 104, 68, 86, 106, 69, 68, 49, 126, 
        126, 35, 50, 57, 97, 102, 100, 109, 83, 68, 96, 54, 90, 118, 85, 89, 64, 
        104, 98, 107, 68, 66, 67, 51, 102, 110, 55, 89, 55, 101, 117, 70, 124, 82, 
        55, 57, 51, 52, 48, 57, 51, 42, 55, 97, 45, 124, 45, 32, 32, 81, 96 };

    Encoding ANSI = Encoding.GetEncoding(1252);
    byte[] input = ANSI.GetBytes(str);
    byte[] output = new byte[input.Length];
    for (int i = 0; i < input.Length; i++)
        output[i] = (byte)(input[i] ^ ~hash[(i + 1) % hash.Length]);
    return ANSI.GetString(output);
}

我假设您的 ANSI 字符串是使用 Windows 1252 编码的,但如果您碰巧使用不同的代码页对旧数据进行了编码,那么如何更改它就很明显了。

由于 C# 没有 Delphi 的 8 位字符串类型,我个人非常想使用 byte[] 而不是 string

这样做看起来像这样:

static byte[] EncodeDecode(byte[] input)
{
    byte[] hash = new byte[63] { 94, 37, 49, 50, 104, 68, 86, 106, 69, 68, 49, 126, 
        126, 35, 50, 57, 97, 102, 100, 109, 83, 68, 96, 54, 90, 118, 85, 89, 64, 
        104, 98, 107, 68, 66, 67, 51, 102, 110, 55, 89, 55, 101, 117, 70, 124, 82, 
        55, 57, 51, 52, 48, 57, 51, 42, 55, 97, 45, 124, 45, 32, 32, 81, 96 };

    byte[] output = new byte[input.Length];
    for (int i = 0; i < input.Length; i++)
        output[i] = (byte)(input[i] ^ ~hash[(i + 1) % hash.Length]);
    return output;
}

@Groo 指出哈希可以更清晰地初始化这一点:

byte[] hash = ANSI.GetBytes(@"^%12hDVjED1~~#29afdmSD`6ZvUY@hbkDBC3fn7Y7euF|R7934093*7a-|-  Q`");

【讨论】:

  • 我相信 ~hash[(i + 1 %... 应该只是 ~hash[i %... 因为 C# 字符串索引是从 0 开始的。
  • @John 你相信错了。需要 (i+1) 来匹配 Delphi 版本。解决它。在 Delphi 中,我们有 I mod Length (Hash) + 1,对于 I=1,第一次循环,给出值 2,第二个字符。因此,我们需要 C# 中的 (i+1) % lengthi=0 第一轮时获取第二个字符(索引 1)。
  • 非惯用语,也许。 (就像我知道的那样)。但是我将字符串版本插入到我在 VS 中处理的乱码中,瞧,它似乎可以工作。现在对百万随机短语测试进行编码。非常感谢!
  • @Eric, @David:我的 2c 会在方法之外的某处写类似 byte[] hash = yourEncoding.GetBytes(yourHashString); 的东西(你不想在每次调用时重复它,但我什至不想问大卫是如何从最初的帖子中得到这些数字的:-D)。
  • @Groo 我在 Delphi 中使用了 for 循环来获取它们,但我同意 yourEncoding.GetBytes() 更好。
【解决方案2】:

C# 中的String 是用UTF-16 编码的字符序列,如this article by Jon Skeet 中所述。这实际上根本不应该关心您,直到您决定将其序列化为二进制(即将其转换为字节数组)。在这种情况下,System.Text 命名空间中有一个名为 Encoding 的类,它支持将 String 编码为您想要的任何编码。

Delphi 中的AnsiString基本上是一个 ASCII 字符串(不,它实际上是一个 ANSI 字符串,就像名字所说的那样),其中每个字符都保证有 8 位。这是一种相对“简单”的编码方式,因为它具有固定大小、被广泛接受并与旧系统兼容(但它不允许编码超过 255 个字符)。

换句话说,您的两个版本都处理相同类型的编码,但 unicode 版本现在将遗留字符串明确定义为 AnsiStrings。这意味着后一个版本实际上并不支持 Unicode 字符串,但需要使用新的 Delphi 版本更改类型。

@David 在我写这个冗长的废话时所做的基本上就是我要写的,除了我会使用Encoding.ASCII 编码而不是([edit] 并且会由于以下原因而惨遭失败)事实上,ASCII 仅使用低 7 位来编码字符,如下面的 David 所述)。 Windows-1252 是最常被称为“ANSI”的编码(不过,如果您查看该 Wiki 文章,您会发现,根据 Microsoft 的说法,用于表示 Windows 代码页的术语 ANSI 是历史参考,但如今在 Windows 社区中仍然是一个误称)。

【讨论】:

  • Encoding.ASCII 不起作用。它只有 7 位宽。您需要 8 位编码。合理的猜测是它是 1252。
  • @David:你是对的。 ASCII 是一个 7 位字符集。 Delphi的AnsiString是8位集合,我弄错了。
  • 其实8位ANSI编码有很多,1252是最普遍的英文。请注意,我发表此声明并不是在认可 1252 的卓越地位!
  • @David: :) 最后是检查文档中的正确编码并使用它。此数据可能很容易仅使用最低 7 位(例如哈希字符串)。我只是想解释一下编码在 C# 中的基本工作原理。整件事是臭名昭著的humanity fails之一。