การแฮชสตริงด้วย 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 เพื่อนของฉันเช่นเดียวกับเครื่องกำเนิดไฟฟ้าออนไลน์ (เช่นเครื่องกำเนิดไฟฟ้านี้ )

ไม่มีใครรู้ว่าข้อผิดพลาดคืออะไร? ฐานที่แตกต่างกันอย่างไร


17
ปิดหัวข้อ แต่โปรดจำไว้ว่าการสร้าง StringBuilder และการใช้ AppendFormat แทนที่จะเป็น String.Format ในลูป foreach ของคุณจะป้องกันโค้ดของคุณจากการสร้างวัตถุสตริงจำนวนมากโดยไม่จำเป็น
Marcel Lamothe

คำตอบ:


155

Encoding.Unicodeเป็นชื่อที่สร้างความเข้าใจผิดของ Microsoft สำหรับ UTF-16 (การเข้ารหัสสองเท่าที่ใช้ในโลก Windows ด้วยเหตุผลทางประวัติศาสตร์ แต่ไม่ได้ใช้โดยบุคคลอื่น) http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

หากคุณตรวจสอบbytesอาร์เรย์ของคุณคุณจะเห็นว่าทุก ๆ ไบต์ที่สองคือ0x00(เนื่องจากการเข้ารหัสแบบสองเท่า)

คุณควรจะใช้Encoding.UTF8.GetBytesแทน

แต่คุณจะเห็นผลลัพธ์ที่แตกต่างกันไปขึ้นอยู่กับว่าคุณพิจารณาว่า'\0'ไบต์ที่กำลังจะสิ้นสุดนั้นเป็นส่วนหนึ่งของข้อมูลที่คุณกำลังแฮชหรือไม่ Hashing สองไบต์"Hi"จะให้ผลที่แตกต่างจาก hashing สาม"Hi"ไบต์ คุณจะต้องตัดสินใจเลือกสิ่งที่คุณต้องการ (สมมุติว่าคุณต้องการทำรหัส PHP ของเพื่อนคุณ)

สำหรับข้อความ ASCII Encoding.UTF8จะเหมาะสมอย่างแน่นอน หากคุณกำลังเล็งหาที่สมบูรณ์แบบเข้ากันได้กับรหัสของเพื่อนของคุณแม้ในปัจจัยการผลิตที่ไม่ใช่ ASCII คุณควรที่จะลองกรณีทดสอบน้อยกับอักขระที่ไม่ใช่ ASCII เช่นéและดูว่าผลลัพธ์ของคุณยังคงตรง ถ้าไม่คุณจะต้องคิดออกว่าการเข้ารหัสเพื่อนของคุณใช้อะไรอยู่ มันอาจเป็นหนึ่งใน "หน้ารหัส" 8 บิตที่เคยเป็นที่นิยมก่อนการประดิษฐ์ Unicode (อีกครั้งฉันคิดว่า Windows เป็นสาเหตุหลักที่ทุกคนยังคงต้องกังวลเกี่ยวกับ "หน้ารหัส")


3
@Elmue คุณอาจจะยินดีที่ได้เรียนรู้ว่า "การเรียงลำดับด้วย UTF8-encoded bytes" และ "sorting by Unicode codepoints" นั้นเทียบเท่ากัน! (เช่นเดียวกับ "การเรียงลำดับด้วย UTF16-encoded shorts" แต่ไม่ใช่ "การเรียงลำดับด้วย UTF16-encoded bytes" ยกเว้นว่าคุณอยู่ในระบบขนาดใหญ่ที่ Windows ไม่ได้ใช้) อย่างไรก็ตาม "การเรียงลำดับ" ใน Unicode นั้นเป็น หัวข้อที่ซับซ้อนที่ควรบันทึกอีกวัน
Quuxplusone

2
@Elue ไม่มั่นใจในคำตอบที่ผิดของคุณ ลองดูสิ คุณจะประหลาดใจ ไม่ว่าความประหลาดใจนั้นน่าพอใจหรือไม่เป็นที่พอใจนั้นขึ้นอยู่กับคุณ :)
Quuxplusone

2
@Elmue“ จะเป็นเช่นไรหากคุณต้องการเปรียบเทียบแบบตัวเล็กและตัวพิมพ์เล็ก ” คุณต้องแปลงไบต์เป็น UTF-16 หากคุณต้องการทำสิ่งนี้ ความจริงที่ว่าความยาวคงที่นั้นไม่ได้ช่วยอะไรเลยสักนิด
Arturo Torres Sánchez

2
"ไม่ได้ใช้โดยคนอื่น" เป็นคำกล่าวอ้างที่น่าสนใจเนื่องจาก Java จัดการกับสตริงภายในเช่นเดียวกับ UTF-16 ...
Sami Kuhmonen

4
@Elmue "ความคิดเห็นของคุณไม่ถูกต้อง: UTF16 เป็น Unicode" คุณผิด. "Unicode" เป็นมาตรฐานที่กำหนดตัวเลข (รหัสจุด) ให้กับร่ายมนตร์ ยกเว้นคู่ตัวแทนก็ไม่ได้ระบุวิธีการแสดงตัวเลขเหล่านั้นเป็นไบต์ UTF16 ระบุจุดโค้ด <--> ไบต์ Unicode ระบุสัญลักษณ์รหัสจุด <-->
antiduh

103

ฉันยังมีปัญหานี้กับการใช้งานรูปแบบอื่น แต่ฉันลืมที่ฉันได้รับมาตั้งแต่เมื่อ 2 ปีที่แล้ว

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 และเปลี่ยนการเข้ารหัสจากASCIIไปUTF8แล้วในที่สุดมันก็ทำงาน!

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

ฉันไม่แน่ใจจริงๆว่าคุณหมายถึงอะไร นี่เป็นฟังก์ชั่นการแฮ็กที่ง่ายมากและคุณสามารถเพิ่ม / ปรับแต่งได้ตามที่คุณต้องการ คุณหมายถึงการเติมเกลือไหม? นั่นเป็นวิธีที่ดีวิธีหนึ่งในการปรับแต่งเพื่อความปลอดภัยเพิ่มเติม
Nico Dumdum

ไม่แนะนำให้ใช้เพียงแค่การแฮช SHA โดยไม่มีปัจจัยการทำงานสำหรับการจัดเก็บรหัสผ่าน กล่าวอีกนัยหนึ่งกระบวนการแฮ็ชของรหัสผ่านจะต้องช้าลงอย่างมากเพื่อป้องกันแฮ็คเกอร์ไม่ให้เดาเร็ว ใช้ Bcrypt หรือ Scrypt เพื่อความปลอดภัยที่ดีขึ้น
Ton Snoei

@TonSnoei ใช่ว่าเป็นจริง อย่างไรก็ตามนี่เป็นโค้ดเก่าจากแอปพลิเคชั่นระบบภายในโบราณที่ไม่มีใครใช้แล้วและฉันจะไม่แนะนำสิ่งนี้ด้วยตัวเอง นอกจากนี้เธรดนี้มีเฉพาะเกี่ยวกับการเข้ารหัส SHA256 และไม่เกี่ยวกับรหัสผ่านโดยตรง แม้ว่าฉันจะไม่คิดแก้ไขมันเพื่อลบการอ้างอิงถึงรหัสผ่านหากนั่นทำให้คุณนึกถึง
Nico Dumdum

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 คุณสามารถส่ง 'จริง' ในพารามิเตอร์สุดท้าย แต่ค่าเริ่มต้นคือ 'เท็จ' อัลกอริทึมต่อไปนี้เทียบเท่ากับฟังก์ชันแฮช PHP เริ่มต้นเมื่อผ่าน 'sha256' เป็นพารามิเตอร์แรก:

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

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

-2

สิ่งนี้ใช้ได้กับฉันใน. NET Core 3.1
แต่ไม่ใช่ใน. NET 5 ดูตัวอย่าง 7

using System;
using System.Security.Cryptography;
using System.Text;

namespace PortalAplicaciones.Shared.Models
{
    public class Encriptar
    {
        public static string EncriptaPassWord(string Password)
        {
            try
            {
                SHA256Managed hasher = new SHA256Managed();

                byte[] pwdBytes = new UTF8Encoding().GetBytes(Password);
                byte[] keyBytes = hasher.ComputeHash(pwdBytes);

                hasher.Dispose();
                return Convert.ToBase64String(keyBytes);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }
        }  
    }
}
 

1
ยินดีต้อนรับสู่ StackOverflow โปรดให้คำอธิบายว่าทำไมคุณคิดว่าโซลูชันที่เสนอมาอาจช่วยแก้ปัญหา OP ได้
Peter Csala

1
โปรดพยายามหลีกเลี่ยงข้อยกเว้นการขว้างปาซ้ำ เมื่อคุณปล่อย StackTrace ดั้งเดิม
Peter Csala
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.