Băm một chuỗi với Sha256


141

Tôi cố gắng băm một chuỗi bằng SHA256, tôi đang sử dụng đoạn mã sau:

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;
    }
}

Tuy nhiên, mã này cho kết quả khác biệt đáng kể so với php bạn bè của tôi, cũng như các trình tạo trực tuyến (như Trình tạo này )

Có ai biết lỗi là gì không? Căn cứ khác nhau?


17
Tắt chủ đề nhưng hãy nhớ rằng việc tạo StringBuilder và sử dụng AppendFormat thay vì String.Format trong vòng lặp foreach của bạn sẽ ngăn mã của bạn không cần tạo nhiều đối tượng chuỗi.
Marcel Lamothe

Câu trả lời:


154

Encoding.Unicodelà tên gây hiểu lầm của Microsoft cho UTF-16 (mã hóa rộng gấp đôi, được sử dụng trong thế giới Windows vì lý do lịch sử nhưng không được sử dụng bởi bất kỳ ai khác). http://msdn.microsoft.com/en-us/l Library / system.text.encoding.unicode.aspx

Nếu bạn kiểm tra bytesmảng của mình , bạn sẽ thấy rằng mỗi byte thứ hai là0x00 (vì mã hóa rộng gấp đôi).

Bạn nên sử dụng Encoding.UTF8.GetBytes thay thế.

Nhưng ngoài ra, bạn sẽ thấy các kết quả khác nhau tùy thuộc vào việc bạn có coi '\0'byte kết thúc là một phần của dữ liệu bạn đang băm hay không. Băm hai byte "Hi"sẽ cho kết quả khác với băm ba byte "Hi". Bạn sẽ phải quyết định những gì bạn muốn làm. (Có lẽ bạn muốn làm bất cứ điều gì mà mã PHP của bạn bè bạn đang làm.)

Đối với văn bản ASCII, Encoding.UTF8chắc chắn sẽ phù hợp. Nếu bạn đang nhắm đến hoàn hảo phù hợp với mã của bạn bè, thậm chí trên đầu vào phi ASCII, bạn muốn thử tốt hơn một vài trường hợp thử nghiệm với các ký tự ASCII như évà xem kết quả của bạn vẫn còn phù hợp. Nếu không, bạn sẽ phải tìm ra cách mã hóa mà bạn của bạn đang thực sự sử dụng; nó có thể là một trong những "trang mã" 8 bit đã từng phổ biến trước khi phát minh ra Unicode. (Một lần nữa, tôi nghĩ Windows là lý do chính mà bất cứ ai vẫn cần phải lo lắng về "trang mã".)


3
@Elmue, bạn có thể hài lòng khi biết rằng "sắp xếp theo byte được mã hóa UTF8" và "sắp xếp theo mã điểm Unicode" là tương đương! (Như là "sắp xếp theo UTF16 mã hóa shorts", nhưng không "sắp xếp theo byte UTF16 mã hóa", trừ khi bạn đang ở trên một hệ thống lớn về cuối nhỏ, mà Windows không phải là.) Tuy nhiên, "sắp xếp" trong Unicode được thực sự là một chủ đề phức tạp nên được lưu cho một ngày khác.
Quuxplusone

2
@Elmue đừng quá tự tin vào câu trả lời sai của bạn. Hãy thử nó; bạn sẽ bất ngờ. Cho dù sự ngạc nhiên là dễ chịu hay khó chịu là hoàn toàn tùy thuộc vào bạn. :)
Quuxplusone

2
@Elmue, xông Nếu bạn muốn làm một trường hợp so sánh không nhạy cảm thì sao? Bạn cũng cần phải chuyển đổi byte trong UTF-16 nếu bạn muốn làm loại công cụ này. Thực tế là chiều dài cố định không giúp được một chút nào.
Arturo Torres Sánchez

2
"Không được sử dụng bởi bất cứ ai khác" là tuyên bố khá thú vị, vì Java trong nội bộ xử lý chuỗi như UTF-16 cũng ...
Sami Kuhmonen

4
@Elmue "Nhận xét của bạn không chính xác: UTF16 là Unicode." Bạn sai rồi. "Unicode" là một tiêu chuẩn gán các số (điểm mã) cho glyphs. Ngoại trừ các cặp thay thế, không nêu cách biểu diễn các số đó dưới dạng byte. UTF16 chỉ định các điểm mã <-> byte. Unicode chỉ định glyphs <-> điểm mã.
antiduh

103

Tôi cũng gặp vấn đề này với một phong cách thực hiện khác nhưng tôi đã quên mất nơi tôi đã nhận nó từ 2 năm trước.

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;
}

Khi tôi nhập một cái gì đó như abcdefghi2013vì một số lý do, nó cho kết quả khác nhau và dẫn đến lỗi trong mô-đun đăng nhập của tôi. Sau đó, tôi đã thử sửa đổi mã theo cách tương tự như được đề xuất bởi Quuxplusone và thay đổi mã hóa từ đó ASCIIđến UTF8cuối cùng nó đã hoạt động!

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();
}

Cảm ơn một lần nữa Quuxplusone cho câu trả lời tuyệt vời và chi tiết! :)


giải pháp của bạn đã làm việc cho tôi. nhưng tôi có trường hợp khác nhau. đó là với sha512 và dòng mã đã giải quyết vấn đề của tôi là hash += bit.ToString("x2");một câu hỏi ở đây: Tôi đang sử dụng Convert.ToBase64String(byte[] encryptedBytes)để chuyển đổi lại từ byte thành chuỗi. đó là cho tôi kết quả khác nhau. Vậy có gì khác nhau giữa hai phương thức chuyển đổi từ byte thành chuỗi ..?
Keval Langalia 11/03/2015

Có thể sử dụng một số tùy chỉnh ở đây (như vectơ khởi tạo của riêng tôi) hoặc là tùy chọn chỉ thêm / thêm chuỗi ngẫu nhiên?
FrenkyB

Tôi thực sự không chắc ý của bạn là gì. Đây chỉ là một chức năng băm rất đơn giản và bạn luôn có thể thêm / tùy chỉnh nó theo cách bạn muốn. Bằng cách nối thêm / chuẩn bị chuỗi ngẫu nhiên, bạn có nghĩa là muối? Đó là một cách tốt để tùy chỉnh nó để bảo mật hơn.
Nico Dumdum

Không nên sử dụng băm SHA mà không có yếu tố công việc để lưu trữ mật khẩu. Nói cách khác, quá trình băm mật khẩu cần phải chậm đáng kể, để ngăn chặn tin tặc đoán nhanh. Sử dụng Bcrypt hoặc Scrypt để bảo mật tốt hơn.
Ton Snoei

@TonSnoei Đúng vậy. Tuy nhiên, đây là một số mã cũ từ một số ứng dụng hệ thống nội bộ cổ xưa từ thời đại học mà không ai sử dụng nữa và tôi thực sự sẽ không đề xuất điều này cho mình. Hơn nữa, chủ đề này đặc biệt về mã hóa SHA256 và không trực tiếp về mật khẩu. Mặc dù vậy, tôi không ngại chỉnh sửa nó để xóa các tham chiếu đến mật khẩu nếu điều đó làm bạn thích thú.
Nico Dumdum

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

Lý do tại sao bạn nhận được các kết quả khác nhau là vì bạn không sử dụng cùng một mã hóa chuỗi. Liên kết bạn đặt cho trang web trực tuyến tính toán SHA256 sử dụng Mã hóa UTF8, trong khi trong ví dụ của bạn, bạn đã sử dụng Mã hóa Unicode. Chúng là hai bảng mã khác nhau, vì vậy bạn không nhận được cùng một kết quả. Với ví dụ trên, bạn nhận được cùng hàm băm SHA256 của trang web được liên kết. Bạn cần sử dụng cùng một mã hóa trong PHP.

Tối thiểu tuyệt đối Mỗi nhà phát triển phần mềm Tuyệt đối, Tích cực phải biết về bộ ký tự và Unicode (Không có lý do!)

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


4

Trong phiên bản PHP, bạn có thể gửi 'true' trong tham số cuối cùng, nhưng mặc định là 'false'. Thuật toán sau tương đương với hàm băm mặc định của PHP khi truyền 'sha256' làm tham số đầu tiên:

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
Tôi sẽ không sử dụng ASCIIvà làm byte[] arrBytes = System.Text.Encoding.UTF8.GetBytes(strData)thay thế.
c00000fd

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
tôi thích thực tế là bạn đã thêm một chút muối ^^
Fabian

1

Cách ngắn nhất và nhanh nhất từ ​​trước đến nay. Chỉ có 1 dòng!

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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.