PHP 等效于 C# SHA1 Unicode 哈希



我有一个正在运行的带有用户身份验证的 C# 应用程序。我使用 Encoding.Unicode 使用 SHA1Managed 类加密密码。现在,我们正在开发一个新的 PHP Web 应用程序,似乎 PHP sha1 方法使用 ASCII,因此加密的密码略有不同。

我无法更改我的 C# 代码,因为它在许多不同的服务器中运行,并且所有用户密码都已加密。这不能更改,因为这意味着询问所有用户的密码(这是不可行的(。

我已经阅读了以下解决方案,但是我无法解决我的问题:

C# SHA-1
  1. 与.PHP SHA-1...不同的结果?在这篇文章中,他们建议使用ASCIIEncoding。但是,正如我之前所说,这是无法改变的。
  2. SHA1:PHP 和 C# 的不同结果,并生成 PHP UTF-16 SHA1 哈希以匹配 C# 方法,这些方法使用 base64 编码,这与我的 C# 编码不兼容(我认为(
  3. PHP 相当于以下 SHA1 加密 C# 代码,这个代码也使用 ASCIIEncoding。

这是我无法更改的 C# 代码:

byte[] msgBytes = Encoding.Unicode.GetBytes(data);
SHA1Managed sha1 = new SHA1Managed();
byte[] hashBytes = sha1.ComputeHash(msgBytes);
StringBuilder hashRet = new StringBuilder("");
foreach (byte b in hashBytes)
hashRet.AppendFormat("{0:X}", b);
return hashRet.ToString();

这是到目前为止实现的PHP代码:

$str=mb_convert_encoding($password.$SALT, 'UTF-16LE');
$result=strtoupper(sha1($str));

在 C# 中为示例字符串"1981"获得的结果 D4CEDCADB729F37C30EBF41BC8F2929AF526AD3

在PHP中,我得到: D4CEDCADB729F37C30EBF41BC8F29209AF526AD3

可以看出,每个字符之间的唯一区别是 0(零(字符。这发生在所有字符串中,但 0 出现在不同的位置。

编辑

如 https://stackoverflow.com/users/1839439/dharman 所述,结果无法完全复制,因为我的字符串具有 SALT。但是,即使没有SALT,结果也应该不同。

解决方案一位用户提出了一个建议,解决了我的问题。不幸的是,在我发布它起作用之前,他删除了答案。当我在字符串生成器中将字节解析为字符串时,问题出在 C# 中。格式为 {0:X},字节中的任何前导 0 都将被忽略。例如,01 将解析为 1,0D 将解析为 D。

因此,我不得不遍历在 PHP 中获得的结果对,并删除其中的前导零。

PHP 中的最终代码如下:

$str=mb_convert_encoding($password.$SALT, 'UTF-16LE');
$result=strtoupper(sha1($str));
$array = str_split($result, 2);
foreach ($array as &$valor) {
$valor = ltrim($valor, '0');
}
unset($valor);
$result = implode($array);

你不会喜欢我的回答,但它就在这里。
首先,问题不在于PHP,PHP很简单,并且可以正常工作。您的 C# 代码中存在严重的错误。它需要尽快以一种或另一种方式修复。

这不能更改,因为这意味着询问所有用户的密码(这是不可行的(。

不幸的是,这是必须发生的事情。不过,您可以在不引起大规模恐慌的情况下做到这一点。我假设您的数据库有某种方法可以知道密码是否已被管理员重置;如果没有,则需要添加此类列(最好是时间戳类型(。下次用户登录时,他们必须提供他们的密码,因为您已经重置了所有密码,因此请获取该密码并正确重新哈希并将新哈希存储在数据库中。明智的做法是使用新列作为新哈希,或者至少有一种方法来识别更正的哈希。该列至少应为 VARCHAR(60(,但最好是将其设置为 VARCHAR(255(,它应该容纳所有流行的哈希值。

当然,SHA1 不是适合密码的哈希方法,即使使用 salts。由于您无论如何都需要重新散列所有密码,因此最好切换到bcrypt或类似的安全散列函数。

由于原始应用程序引入了此错误,因此需要一些解决方法。在新应用程序中支持错误行为似乎不是一个好主意,所以我建议不要在 PHP 中寻找解决方法。您当然可以尝试以与问题中建议的相同方式破坏密码,但这只会将相同的错误拖到新应用程序中,而无需修复它。

在开始执行任何操作之前,您应该分析数据库中损坏了多少密码。正确的 SHA1 哈希长度应为 40 个字符。如果您能找到小于40的密码数量,您将知道需要修复哪些密码以及需要修复多少密码。

修复密码会很困难,但绝对值得。

关于字符编码的说明:
PHP 大部分时间使用 UTF-8 编码。它是网络上最常用的编码,我建议将其用于字符串。当涉及到哈希时,字符串的编码并不重要,因为哈希是根据字节计算的。这就是使用Encoding.Unicode.GetBytes(data)将 C# 字符串转换为 UTF-16 格式的字节的原因。

最新更新