使用Sha256散列字符串


141

我尝试使用SHA256哈希字符串,我使用以下代码:

using System;
using System.Security.Cryptography;
using System.Text;
 public class Hash
    {
    public static string getHashSha256(string text)
    {
        byte[] bytes = Encoding.Unicode.GetBytes(text);
        SHA256Managed hashstring = new SHA256Managed();
        byte[] hash = hashstring.ComputeHash(bytes);
        string hashString = string.Empty;
        foreach (byte x in hash)
        {
            hashString += String.Format("{0:x2}", x);
        }
        return hashString;
    }
}

但是,与我的朋友php和在线生成器(例如This generator)相比,此代码给出的结果明显不同。

有人知道错误是什么吗?不同的基础?


17
脱离主题,但请记住,在foreach循环中创建StringBuilder并使用AppendFormat而不是String.Format将防止您的代码不必要地创建许多字符串对象。
Marcel Lamothe 2014年

Answers:


154

Encoding.Unicode是Microsoft对UTF-16(双宽编码,出于历史原因在Windows世界中使用,但未被其他人使用)的误导性名称。http://msdn.microsoft.com/zh-CN/library/system.text.encoding.unicode.aspx

如果检查bytes数组,则会发现第二个字节是0x00(由于双倍宽编码)。

您应该使用 Encoding.UTF8.GetBytes改为。

而且,根据是否将终止'\0'字节视为要散列的数据的一部分,您将看到不同的结果。哈希两个字节"Hi"与哈希三个字节会得到不同的结果"Hi"。您必须决定要执行的操作。(大概您想做您朋友的PHP代码正在执行的任何一项。)

对于ASCII文本,Encoding.UTF8绝对合适。如果您希望与朋友的代码完全兼容,即使在非ASCII输入上,则最好尝试使用非ASCII字符(例如é和)的一些测试用例,并查看结果是否仍然匹配。如果不是,则必须弄清楚朋友真正使用的编码方式。它可能是Unicode发明之前曾经流行的8位“代码页”之一。(再次,我认为Windows是仍然有人需要担心“代码页”的主要原因。)


3
@Elmue,您可能会高兴地发现“按UTF8编码字节排序”和“按Unicode代码点排序”是等效的!(就像“按UTF16编码的shorts 排序”,而不是 “按UTF16编码的字节排序”一样,除非您使用的是Big-endian系统,而Windows则不是。)但是,Unicode中的“排序”实际上是复杂的主题,应另存一天。
Quuxplusone

2
@Elmue对错误答案不要​​那么自信。试试看; 你会惊讶的。惊喜是令人愉快还是不愉快完全取决于您。:)
Quuxplusone 2014年

2
@Elmue,“ 如果要进行不区分大小写的比较怎么办?如果您想执行此类操作,则还需要转换UTF-16中的字节。它是固定长度的事实一点也没有帮助。
Arturo TorresSánchez2014年

2
“没有被其他人使用”是一个很有趣的说法,因为Java在内部也像UTF-16一样处理字符串...
Sami Kuhmonen

4
@Elmue“您的注释不正确:UTF16是Unicode。” 你错了。“ Unicode”是为字形分配数字(代码点)的标准。除代理对外,它没有说明如何将这些数字表示为字节。UTF16指定代码点<->字节。Unicode指定字形<->代码点。
antiduh

103

我也遇到了另一种实现方式的问题,但是我忘记了从两年前开始的问题。

static string sha256(string randomString)
{
    var crypt = new SHA256Managed();
    string hash = String.Empty;
    byte[] crypto = crypt.ComputeHash(Encoding.ASCII.GetBytes(randomString));
    foreach (byte theByte in crypto)
    {
        hash += theByte.ToString("x2");
    }
    return hash;
}

当我abcdefghi2013出于某种原因输入类似的内容时,它会给出不同的结果,并导致我的登录模块出错。然后,我尝试按照Quuxplusone的建议修改代码,并将编码从更改为ASCIIUTF8然后终于可以了!

static string sha256(string randomString)
{
    var crypt = new System.Security.Cryptography.SHA256Managed();
    var hash = new System.Text.StringBuilder();
    byte[] crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(randomString));
    foreach (byte theByte in crypto)
    {
        hash.Append(theByte.ToString("x2"));
    }
    return hash.ToString();
}

再次感谢Quuxplusone的精彩而详尽的回答!:)


您的解决方案为我工作。但我有不同的情况。它与sha512一起使用,并且解决了我的问题的代码行是hash += bit.ToString("x2");我这里的一个问题:我曾经使用过从Convert.ToBase64String(byte[] encryptedBytes)字节转换回字符串的方法。那给了我不同的结果。那么这两种从字节转换为字符串的方法有什么区别?
Keval Langalia

是否可以在此处使用一些自定义设置(例如我自己的初始化向量),还是仅在随机字符串后面添加/添加选项?
FrenkyB '16

我不太确定你的意思。这只是一个非常简单的哈希函数,您随时可以根据需要添加/自定义它。通过添加/添加随机字符串,您是说盐腌吗?嗯,这是自定义它以提高安全性的一种好方法。
Nico Dumdum

不建议仅使用没有工作因数的SHA哈希来存储密码。换句话说,密码的哈希处理需要非常慢,以防止黑客快速猜测。使用Bcrypt或Scrypt可获得更好的安全性。
Ton Snoei

@TonSnoei是的,是的。但是,这是大学里一些古老的内部系统应用程序中的一些旧代码,现在没人再使用了,我本人也不推荐这样做。此外,该线程专门针对SHA256编码,而不直接针对密码。虽然,我不介意对其进行编辑以删除对密码的引用(如果您很喜欢)。
尼科达姆

6
public static string ComputeSHA256Hash(string text)
{
    using (var sha256 = new SHA256Managed())
    {
        return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
    }                
}

之所以得到不同的结果,是因为您没有使用相同的字符串编码。您为计算SHA256的在线网站放置的链接使用UTF8编码,而在您的示例中,您使用Unicode编码。它们是两种不同的编码,因此您不会获得相同的结果。在上面的示例中,您将获得链接网站的相同SHA256哈希。您还需要在PHP中使用相同的编码。

每个软件开发人员绝对必须绝对了解Unicode和字符集(没有任何借口!)

https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/


4

在PHP版本中,您可以在最后一个参数中发送“ true”,但默认值为“ false”。当传递“ sha256”作为第一个参数时,以下算法等效于默认的PHP哈希函数:

public static string GetSha256FromString(string strData)
    {
        var message = Encoding.ASCII.GetBytes(strData);
        SHA256Managed hashString = new SHA256Managed();
        string hex = "";

        var hashValue = hashString.ComputeHash(message);
        foreach (byte x in hashValue)
        {
            hex += String.Format("{0:x2}", x);
        }
        return hex;
    }

4
我不会使用ASCII而做byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData)
c00000fd 2015年

3
public string EncryptPassword(string password, string saltorusername)
        {
            using (var sha256 = SHA256.Create())
            {
                var saltedPassword = string.Format("{0}{1}", salt, password);
                byte[] saltedPasswordAsBytes = Encoding.UTF8.GetBytes(saltedPassword);
                return Convert.ToBase64String(sha256.ComputeHash(saltedPasswordAsBytes));
            }
        }

1
我喜欢您添加一些盐的事实^^
Fabian

1

有史以来最短,最快的方法。只有1行!

public static string StringSha256Hash(string text) =>
    string.IsNullOrEmpty(text) ? string.Empty : BitConverter.ToString(new System.Security.Cryptography.SHA256Managed().ComputeHash(System.Text.Encoding.UTF8.GetBytes(text))).Replace("-", string.Empty);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.