Convert string to Guid using SHA256 without truncating - c#

Is there a way to convert a string to a GUID using SHA256 without truncating the 16 bytes?
Currently I have this:
using SHA256 sha2 = SHA256.Create())
{
var hash = sha2.ComputeHash(Encoding.Default.GetBytes(string));
return new Guid(hash.Take(16).ToArray());
}

A hash is not the same as a Guid. Trying to equate the 2 is incorrect.
If you want a unique identifier:
return new Guid();
That'll give you one.
If you want the hash, store is as bytes or a string, not a Guid

Related

How to validate salted and hashed password with C#

I have the following problem that I've been dealing for a couple of hours by now and it's driving nuts.
Context
I have a legacy database that stored passwords using the following algorithm. The legacy code used a Python library.
PBKDF2 with SHA256
1000 iterations
Salt has a length of 8
Password is stored like this $salt$hashedPassword
I'm switching login flow for the new system and I need to migrate that old algorithm to a new one. New system uses .netcore
Question
What I'm trying to do is even possible?. How can I achieve it?
What my logic dictates is that I can take the salt and recreate the hashing algorithm using .netcore Crypto library but its not working and the function returns always false.
Legacy Code
from werkzeug.security import generate_password_hash, check_password_hash
def setPassword(self, password):
self.password = generate_password_hash(password, method='pbkdf2:sha256')
Where generate_password_hash comes from the library, this is the code
SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def generate_password_hash(password, method="pbkdf2:sha256", salt_length=8):
"""Hash a password with the given method and salt with a string of
the given length. The format of the string returned includes the method
that was used so that :func:`check_password_hash` can check the hash.
The format for the hashed string looks like this::
method$salt$hash
This method can **not** generate unsalted passwords but it is possible
to set param method='plain' in order to enforce plaintext passwords.
If a salt is used, hmac is used internally to salt the password.
If PBKDF2 is wanted it can be enabled by setting the method to
``pbkdf2:method:iterations`` where iterations is optional::
pbkdf2:sha256:80000$salt$hash
pbkdf2:sha256$salt$hash
:param password: the password to hash.
:param method: the hash method to use (one that hashlib supports). Can
optionally be in the format ``pbkdf2:<method>[:iterations]``
to enable PBKDF2.
:param salt_length: the length of the salt in letters.
"""
salt = gen_salt(salt_length) if method != "plain" else ""
h, actual_method = _hash_internal(method, salt, password)
return "%s$%s$%s" % (actual_method, salt, h)
def gen_salt(length):
"""Generate a random string of SALT_CHARS with specified ``length``."""
if length <= 0:
raise ValueError("Salt length must be positive")
return "".join(_sys_rng.choice(SALT_CHARS) for _ in range_type(length))
Code
using System;
using System.Security.Cryptography;
using System.Text;
namespace test_pwd
{
class Program
{
static void Main(string[] args)
{
var res = SameHash("Qwerty12", "84e8c8a5dbdafaf23523ffa5dfecf29d53522a35ca4c76fa877c5fcf9eb4b654", "laSgSC6R");
Console.WriteLine(res);
}
public static bool SameHash(string userpwd, string storedHash, string storedSalt)
{
var saltByte = Encoding.UTF8.GetBytes(storedSalt);
var rfc = new Rfc2898DeriveBytes(userpwd, saltByte, 1000);
var baseString = Convert.ToBase64String(rfc.GetBytes(64));
return baseString == storedHash;
}
}
}
Base string is converted into
k6vhCweBNz8ymMeEdhi+1czrea+oTTYLrW1OuwdinA78AFyEXKitpKUGLCt1ZdyS1Vka8Cptzd5u5Uzdbi4MbA==
Which is not the same as the stored password hash I'm sending. What I'm doing wrong or this idea is even feasible?.

How to properly hash a password

I want to re-create the MyBB hashing process so I can use its database to authenticate users on a 3rd party app (written on C#).
MyBB uses:
md5(md5($salt).password)
My problem is that the result I get on C# is nowhere similar to the one MyBB gets.
What I've done on C#:
public string HashPass(string password, string salt)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] saltHash =md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(salt));
string passwordAndSalt = password + System.Text.Encoding.ASCII.GetString(saltHash);
byte[] finalHash = md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(passwordAndSalt));
string final = System.Text.Encoding.ASCII.GetString(finalHash);
return final;
}
The result I get from using that function for password "Test123" and salt "0fYR6mEE" (gathered from MyBB db) is: "??R?????s??" while the actual result should look like "VaHffsyzJeEa4dB3bbMWeUlJObAfN5I9rf1CuNRXCa6xPJTzXL".
Most likely I'm missing something obvious, sorry about that.
There are unknowns here. Which encoding does MyBB use to the password bytes? It could be ASCII, ANSI, or UTF8 or it could get the string bytes directly, i.e., without encoding. So I will write it partly as pseudo code
byte[] passwordBytes = GetBytes(password); // Where you have to define GetBytes
byte[] saltBytes = System.Convert.FromBase64String(salt); // Assuming it is given as base64
// Merge the password bytes and the salt bytes
var mergedBytes = new byte[passwordBytes.Length + saltBytes.Length];
Array.Copy(passwordBytes, mergedBytes, passwordBytes.Length);
Array.Copy(saltBytes, 0, mergedBytes, passwordBytes.Length, saltBytes.Length);
var md5 = new MD5CryptoServiceProvider();
byte[] finalHash = md5.ComputeHash(mergedBytes);
string final = System.Convert.ToBase64String(finalHash);
Note that I'm merging the password bytes and the salt bytes, not the password string and the salt string. Then the MD5 it taken only once from these merged bytes.
But I'm not sure what md5(md5($salt).password) does. Is md5() returning the the hash as base64 string already? Maybe you would have to convert the salt from base64 to bytes[], then get the MD% hash, convert it into a base64 string and then concatenate it with the password string. Then get the bytes from this combined string, do the hash again and convert the result to a base64 string again.
You would have to dig deeper into the source code of MyBB to be sure.

Convert C# to Ruby - HMAC SHA256 function

I'm trying to get the HMAC SHA256 value(str_signature), I followed the Ruby code from this post, although his example was converting code from Java(with a Hex key).
C#
string strRawSignature = "200123123891:12|11231231|GET|just%20test%20value"
// Convert signature to byte array in order to compute the hash
byte[] bSignature = Encoding.UTF8.GetBytes(strRawSignature);
// Convert ApiKey to byte array - for initializing HMACSHA256
byte[] bSecretKey = Convert.FromBase64String(strApiKey);
string strSignature = "";
using (HMACSHA256 hmac = new HMACSHA256(bSecretKey))
{
// Compute signature hash
byte[] bSignatureHash = hmac.ComputeHash(bSignature);
// Convert signature hash to Base64String for transmission
str_signature = Convert.ToBase64String(bSignatureHash);
}
Ruby
require "openssl"
require "base64"
digest = OpenSSL::Digest.new('sha256')
key = [ 'xiIm9FuYhetyijXA2QL58TRlvhuSJ73FtdxiSNU2uHE=' ]
#this is just a dummy signature to show what the possible values are
signature = "200123123891:12|11231231|GET|just%20test%20value"
hmac = OpenSSL::HMAC.digest(digest, key.pack("m*"), signature)
str_signature = Base64.urlsafe_encode64(hmac)
example result: "B0NgX1hhW-rsnadD2_FF-grcw9pWghwMWgG47mU4J94="
Update:
Changed the pack method to output base64 strings.
Edited variable names for concistency
References:
Used hexdigest, has a different ouput string length.
This example uses the digest method, although I'm not sure what value the key parameter has, hopefully it's a base 64 encoded string.
This uses hexdigest again. I am pretty sure that digest is the method to go vs hexdigest, since hexdigest ouput has a longer string compared to the sample HMAC value I have from C# script.
Finally got the monkey of my back!
I don't really need to create a sha256 digest object after all, I just had to put the 'sha256' parameter.
require 'openssl'
require "base64"
#API_KEY = base64 encoded string
key = Base64.decode64(API_KEY)
hash = OpenSSL::HMAC.digest('sha256', key, "Message")
puts Base64.encode64(hash)
thanks to this link

Hashing a string with Sha256

I try to hash a string using SHA256, I'm using the following code:
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;
}
}
However, this code gives significantly different results compared to my friends php, as well as online generators (such as This generator)
Does anyone know what the error is? Different bases?
Encoding.Unicode is Microsoft's misleading name for UTF-16 (a double-wide encoding, used in the Windows world for historical reasons but not used by anyone else). http://msdn.microsoft.com/en-us/library/system.text.encoding.unicode.aspx
If you inspect your bytes array, you'll see that every second byte is 0x00 (because of the double-wide encoding).
You should be using Encoding.UTF8.GetBytes instead.
But also, you will see different results depending on whether or not you consider the terminating '\0' byte to be part of the data you're hashing. Hashing the two bytes "Hi" will give a different result from hashing the three bytes "Hi". You'll have to decide which you want to do. (Presumably you want to do whichever one your friend's PHP code is doing.)
For ASCII text, Encoding.UTF8 will definitely be suitable. If you're aiming for perfect compatibility with your friend's code, even on non-ASCII inputs, you'd better try a few test cases with non-ASCII characters such as é and 家 and see whether your results still match up. If not, you'll have to figure out what encoding your friend is really using; it might be one of the 8-bit "code pages" that used to be popular before the invention of Unicode. (Again, I think Windows is the main reason that anyone still needs to worry about "code pages".)
I also had this problem with another style of implementation but I forgot where I got it since it was 2 years ago.
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;
}
When I input something like abcdefghi2013 for some reason it gives different results and results in errors in my login module.
Then I tried modifying the code the same way as suggested by Quuxplusone and changed the encoding from ASCII to UTF8 then it finally worked!
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();
}
Thanks again Quuxplusone for the wonderful and detailed answer! :)
public static string ComputeSHA256Hash(string text)
{
using (var sha256 = new SHA256Managed())
{
return BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
}
}
The reason why you get different results is because you don't use the same string encoding. The link you put for the on-line web site that computes SHA256 uses UTF8 Encoding, while in your example you used Unicode Encoding. They are two different encodings, so you don't get the same result. With the example above you get the same SHA256 hash of the linked web site. You need to use the same encoding also in PHP.
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/
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));
}
}
New .NET 5+ solution:
If you're using .NET 5 or above, you can use the new Convert.ToHexString method to convert the hash byte array into a hexadecimal string; eliminating the hassle of using string builders and so on.
The following method also uses the using block so that the SHA256 instance gets disposed.
It also turns the password (which is passed in as a string) into a byte array using UTF-8 encoding, which was recommended by the accepted answer. Furthermore, we're also using the new SHA256 class as opposed to the old (now obselete) SHA256Managed class.
public string QuickHash(string secret)
{
using var sha256 = SHA256.Create();
var secretBytes = Encoding.UTF8.GetBytes(secret);
var secretHash = sha256.ComputeHash(secretBytes);
return Convert.ToHexString(secretHash);
}
Note: You should NOT use this method for hashing user passwords. General-purpose hashing functions such as SHA-256 aren't suited for use for passwords anymore, even if you add salts. This is useful for hashing strings that you know have high entropy, such as long randomly generated session tokens and whatnot. For storing passwords, you must look into slower hashing functions that were specifically designed for this purpose, such as Bcrypt, Scrypt, or PBKDF2 (the latter is available natively in .NET — see this)
The shortest and fastest way ever. Only 1 line!
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);
In the PHP version you can send 'true' in the last parameter, but the default is 'false'. The following algorithm is equivalent to the default PHP's hash function when passing 'sha256' as the first parameter:
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;
}
I was looking and testing theses answers, and Visual Studio showed me that SHA256Managed is now Obsolete (here)
So, I used the SHA256 class instead:
Encoding enc = Encoding.UTF8;
var hashBuilder = new StringBuilder();
using var hash = SHA256.Create();
byte[] result = hash.ComputeHash(enc.GetBytes(yourStringToHash));
foreach (var b in result)
hashBuilder.Append(b.ToString("x2"));
string result = hashBuilder.ToString();
This work for me in .NET Core 3.1.
But not in .NET 5 preview 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);
}
}
}
}

C#: comparing the password hash with the user input different sizes , when Authenticating the user

I have made a user registration where I have salted the user password and hashed it using SHA256.
later, when the user needs to log into my system I need to have his password salted and hashed, so I :
1.retrieved the salt "string" from Database
2. converted the salt into bytes
3. created a new byte[] = [inputPassword.length + salt.length]
4. and hashed that.
now the new hash is shorter than Original hash ...(using same hashing functions)
given these information what do you think the problem might be ...
is storing the salt as CHAR on my database wrong , if yes what or how should I save it ?
Note: both hashes are compared on byte level.
Note: all user information are stored in the database password and salt as CHAR
thank you in advance
You could generate a salt from a Guid converted into a base 64 string, then save that in the database as char. I use nvarchar to maximise my options using a .NET string.
Then you can implement something like this for generating the original password hash, and comparing the hash when the user logs in:
public static byte[] GetHash(string password, string salt)
{
byte[] unhashedBytes = Encoding.Unicode.GetBytes(String.Concat(salt, password));
SHA256Managed sha256 = new SHA256Managed();
byte[] hashedBytes = sha256.ComputeHash(unhashedBytes);
return hashedBytes;
}
public static bool CompareHash(string attemptedPassword, byte[] hash, string salt)
{
string base64Hash = Convert.ToBase64String(hash);
string base64AttemptedHash = Convert.ToBase64String(GetHash(attemptedPassword, salt));
return base64Hash == base64AttemptedHash;
}
Usually hash functions return fixed size hash, so if you tell that new hash is shorter I think problem might be in your hash function.

Categories