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를 만들고 String.Format 대신 AppendFormat을 사용하면 코드에서 많은 문자열 객체를 불필요하게 만들 수 없습니다.
Marcel Lamothe

답변:


155

Encoding.UnicodeUTF-16 (역사적 이유로 Windows 세계에서는 사용되었지만 다른 사람은 사용하지 않는 이중 너비 인코딩)에 대한 Microsoft의 오해의 소지가 있습니다. http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx

bytes배열 을 검사 하면 두 번째 바이트가 0x00(두 배 너비 인코딩 때문에) 모든 바이트임을 알 수 있습니다 .

Encoding.UTF8.GetBytes대신 사용해야합니다 .

또한 종료 '\0'바이트를 해시하는 데이터의 일부로 간주하는지 여부에 따라 다른 결과가 표시됩니다 . 두 바이트 "Hi"를 해시하면 바이트 를 해시하는 것과 다른 결과가 나타납니다 "Hi". 원하는 것을 결정해야합니다. (아마도 친구의 PHP 코드 중 하나를 수행하려고합니다.)

ASCII 텍스트의 경우 Encoding.UTF8확실히 적합합니다. 당신이 목표라면 완벽한 심지어 비 ASCII 입력에, 친구의 코드와의 호환성, 당신은 더 나은 같은 비 ASCII 문자와 몇 가지 테스트 케이스를 시도 할 것입니다 é당신의 결과는 여전히 일치 여부를 참조하십시오. 그렇지 않은 경우 친구가 실제로 사용하는 인코딩을 파악해야합니다. 유니 코드가 발명되기 전에 널리 사용되었던 8 비트 "코드 페이지"중 하나 일 수 있습니다. (또, Windows는 누구나 "코드 페이지"에 대해 여전히 걱정해야하는 주된 이유라고 생각합니다.)


3
@Elmue, "UTF8로 인코딩 된 바이트로 정렬"과 "유니 코드 코드 포인트로 정렬"이 동일하다는 것을 알게되어 기쁩니다. (AS는 "UTF16 인코딩에 의해 정렬됩니다 short의"하지만 되지 는 Windows가 아닌 빅 엔디안 시스템에 아니라면 "UTF16 인코딩 된 바이트로 정렬".) 그러나, 유니 코드에서 "정렬"되어 정말 다른 날에 저장해야하는 복잡한 주제입니다.
Quuxplusone

2
@Elmue는 오답에 대해 확신을 가지지 않습니다. 사용해보십시오. 당신은 놀랄 것입니다. 놀람이 유쾌하거나 불쾌한 지 여부는 전적으로 귀하에게 달려 있습니다. :)
Quuxplusone

2
@Elmue,“ 대소 문자를 구분하지 않고 비교하려면 어떻게해야합니까? 이런 종류의 작업을하려면 바이트를 UTF-16으로 변환해야합니다. 고정 길이라는 사실은 1 비트에 도움이되지 않습니다.
Arturo Torres Sánchez

2
Java는 내부적으로 문자열을 UTF-16으로 처리하기 때문에 "다른 사람이 사용하지 않음"은 상당히 흥미로운 주장입니다.
Sami Kuhmonen

4
@Elmue "댓글이 잘못되었습니다 : UTF16은 유니 코드입니다." 당신은 잘못. "유니 코드"는 숫자 (코드 포인트)를 글리프에 할당하는 표준입니다. 대리 쌍을 제외하고는 해당 숫자를 바이트로 표시하는 방법을 설명하지 않습니다. UTF16은 코드 포인트 <-> 바이트를 지정합니다. 유니 코드는 글리프 <-> 코드 포인트를 지정합니다.
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 2016 년

@TonSnoei 그렇습니다. 그러나 이것은 더 이상 아무도 사용하지 않는 대학의 고대 내부 시스템 응용 프로그램에서 가져온 오래된 코드이므로 실제로 권장하지는 않습니다. 또한이 스레드는 구체적으로 SHA256 인코딩에 관한 것이며 암호에 대한 것은 아닙니다. 그럼에도 불구하고 암호에 대한 참조를 제거하기 위해 편집하는 것은 마음에 들지 않습니다.
Nico Dumdum 2016 년

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

다른 결과를 얻는 이유는 동일한 문자열 인코딩을 사용하지 않기 때문입니다. SHA256을 계산하는 온라인 웹 사이트에 넣은 링크는 UTF8 인코딩을 사용하지만 예제에서는 유니 코드 인코딩을 사용했습니다. 그것들은 두 가지 다른 인코딩이므로 동일한 결과를 얻지 못합니다. 위의 예제를 사용하면 연결된 웹 사이트의 동일한 SHA256 해시가 나타납니다. PHP에서도 동일한 인코딩을 사용해야합니다.

절대적으로 모든 소프트웨어 개발자는 절대적으로, 유니 코드 및 문자 집합에 대해 반드시 알아야합니다 (변명 없음).

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

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

가장 짧고 빠른 방법. 단 한 줄!

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.