Manually Converting Password and Salt to Compare with ASPNETDB Password - c#

I'm trying to manually compare the aspnetdb password field with a manually-hashed password on my end to check for validity (I can't use the default Membership implementation as I'm using a custom one). Anyway, I'm taking the Base64 password salt from the record, and using the following algorithm to get the salted hash:
static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
{
HashAlgorithm algorithm = new SHA256Managed();
byte[] plainTextWithSaltBytes =
new byte[plainText.Length + salt.Length];
for (int i = 0; i < plainText.Length; i++)
{
plainTextWithSaltBytes[i] = plainText[i];
}
for (int i = 0; i < salt.Length; i++)
{
plainTextWithSaltBytes[plainText.Length + i] = salt[i];
}
byte[] hash = algorithm.ComputeHash(plainTextWithSaltBytes);
return hash;
}
I then take those bytes and compare with a Convert.GetBase64Bytes(MembershipUser.Password). There's a disconnect somewhere though as the bytes I get from the Convert method and my computed hash are never the same even when I know the passwords are the same.
Any insight on where I'm going wrong?

In looking at the source for SqlMembershipProvider, it appears that they copy the salt before the password:
static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
{
HashAlgorithm algorithm = new SHA256Managed();
byte[] plainTextWithSaltBytes = new byte[plainText.Length + salt.Length];
salt.CopyTo(plainTextWithSaltBytes, 0);
plainText.CopyTo(plainTextWithSaltBytes, salt.Length);
byte[] hash = algorithm.ComputeHash(plainTextWithSaltBytes);
return hash;
}

Related

Validating HMACSHA256 in C#

I'm pretty new to asp.net/c# and i'm trying to recreate the password validation in C#. I have this hash stored in the database:
U2zdbUmZXCeOLs0OuS9bhg==7hQ60TTq0ZiT/z+eu4bdzpmBcp5uYa70ZDxQPncEG0c=
The password for this hash is 1234567. This works because I can login with this password in the webapplication.
So if I understand correctly. The hash consists of a base64 encoded salt U2zdbUmZXCeOLs0OuS9bhg== and a password hashed with this salt: 7hQ60TTq0ZiT/z+eu4bdzpmBcp5uYa70ZDxQPncEG0c=
But if I use this example I found on the internet. I don't get the same hash result. I already tried playing with the encoding (resulting in different hashes), but no luck. hashAlgorithmType is set to HMACSHA256 in the web.config. What am I doing wrong?
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
var base64Salt = "U2zdbUmZXCeOLs0OuS9bhg==";
var base64Hash = "7hQ60TTq0ZiT/z+eu4bdzpmBcp5uYa70ZDxQPncEG0c=";
// Decode the base64 salt to get the salt byte array
var saltBytes = Convert.FromBase64String(base64Salt);
// Provide the user's plain password
var plaintextPassword = "1234567";
// Salt the plaintext password, prepend to user's provided password, and then hash
try
{
var hmac256 = new HMACSHA256(saltBytes);
var hash = Convert.ToBase64String(hmac256.ComputeHash(Encoding.UTF8.GetBytes(plaintextPassword)));
Console.WriteLine(base64Salt+hash);
if (hash == base64Hash)
{
Console.WriteLine("Success! Both hashes match!");
}
else
{
Console.WriteLine("Passwords do not match.");
}
}
catch (Exception e)
{
Console.WriteLine("Error!", e.Message);
}
}
}
You need to know the right key used and how is the password concatenated with the salt.
Normally, the salt bytes are concatenated with the password bytes, like this:
var password = "1234567";
var passwordBytes = Encoding.UTF8.GetBytes(password);
var salt = "U2zdbUmZXCeOLs0OuS9bhg==";
var saltBytes = Convert.FromBase64String(salt);
var passwordBytesAndSaltBytes = new byte[passwordBytes.Length + saltBytes.Length];
for (int i = 0; i < passwordBytes.Length; i++)
{
passwordBytesAndSaltBytes[i] = passwordBytes[i];
}
for (int i = 0; i < saltBytes.Length; i++)
{
passwordBytesAndSaltBytes[passwordBytes.Length + i] = saltBytes[i];
}
but we don't know what were the rules used.
And the secret is well, kept secret, like this:
var secret = "this must be hidden";
var secretBytes = Encoding.UTF8.GetBytes(secret);
var hmac256 = new HMACSHA256(secretBytes);
var hash = Convert.ToBase64String(hmac256.ComputeHash(passwordBytesAndSaltBytes) );
Without seeing the code, unfortunately, I don't think you will be able to replicate it.
Finally got it to work! I found the answer in the source code of Umbraco (I'm using Umbraco as CMS). I thought it was using the default membershipprovider but it wasn't... Also worth mentioning that the salt was to short for the required key-length, so it was extended.
From the source code I made a working example:
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
var bytes = Encoding.Unicode.GetBytes("1234567");
var saltBytes = Convert.FromBase64String("U2zdbUmZXCeOLs0OuS9bhg==");
byte[] inArray;
var hashAlgorithm = HashAlgorithm.Create("HMACSHA256");
var algorithm = hashAlgorithm as KeyedHashAlgorithm;
var keyedHashAlgorithm = algorithm;
if (keyedHashAlgorithm.Key.Length == saltBytes.Length)
{
//if the salt bytes is the required key length for the algorithm, use it as-is
keyedHashAlgorithm.Key = saltBytes;
Console.WriteLine("length is ok");
}
else if (keyedHashAlgorithm.Key.Length < saltBytes.Length)
{
//if the salt bytes is too long for the required key length for the algorithm, reduce it
var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
Buffer.BlockCopy(saltBytes, 0, numArray2, 0, numArray2.Length);
keyedHashAlgorithm.Key = numArray2;
Console.WriteLine("salt byte too long");
}
else
{
//if the salt bytes is too short for the required key length for the algorithm, extend it
Console.WriteLine("salt byte to short");
var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
var dstOffset = 0;
while (dstOffset < numArray2.Length)
{
var count = Math.Min(saltBytes.Length, numArray2.Length - dstOffset);
Buffer.BlockCopy(saltBytes, 0, numArray2, dstOffset, count);
dstOffset += count;
}
keyedHashAlgorithm.Key = numArray2;
}
inArray = keyedHashAlgorithm.ComputeHash(bytes);
var hash = Convert.ToBase64String(inArray);
Console.WriteLine(hash);
var base64Hash = "7hQ60TTq0ZiT/z+eu4bdzpmBcp5uYa70ZDxQPncEG0c=";
if (hash == base64Hash)
{
Console.WriteLine("Success! Both hashes match!");
}
else
{
Console.WriteLine("Passwords do not match.");
}
}
}

decrypt md5 hash confusion [duplicate]

This question already has answers here:
Is it possible to decrypt MD5 hashes?
(24 answers)
Closed 1 year ago.
I have develop a simple MD5 hash like this:
public static string Hash(string value)
{
byte[] valueBytes = new byte[value.Length * 2];
Encoder encoder = Encoding.Unicode.GetEncoder();
encoder.GetBytes(value.ToCharArray(), 0, value.Length, valueBytes, 0, true);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes("123456"));
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
stringBuilder.Append(hashBytes[i].ToString("x2"));
}
return stringBuilder.ToString();
}
But now, I want to decrypt the result of this code to original text. But I don't know which function I should use?
My hash function is:
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes("123456"));
You don't (can't) decrypt a hash, you perform the encrypt again against a given value to validate that this value is the same as the one the hash pretends to belong to.

Comparing hashed password with salt always fails

I saw multiple questions about password hashing and salt but they all seem to fail for me. I use this function to hash/salt it and then put it to database:
public string HashPassword(string password)
{
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
var pbkdf2 = new Rfc2898DeriveBytes(PasswordTextbox.Password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
string savedPasswordHash = Convert.ToBase64String(hashBytes);
return savedPasswordHash;
}
and then I try to compare it with user input using this function:
public static void UnhashPassword(string hashedPassword, string hashedPasswordFromDatabase)
{
byte[] hashBytes = Convert.FromBase64String(hashedPasswordFromDatabase);
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
var pbkdf2 = new Rfc2898DeriveBytes(hashedPassword, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
for (int i = 0; i < 20; i++)
if (hashBytes[i + 16] != hash[i])
throw new UnauthorizedAccessException();
}
the second function always throws exception. Not sure what is the reason since this answer seemed to work for everybody in other question.
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem, it's impossible to say for sure what is wrong. However, the following code example does work exactly as expected (i.e. the value of result is true after it's been initialized by the call to ValidatePassword():
static void Main(string[] args)
{
string password = "password";
string hashedPassword = HashPassword(password);
bool result = ValidatePassword(password, hashedPassword);
}
static string HashPassword(string password)
{
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
byte[] hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
string savedPasswordHash = Convert.ToBase64String(hashBytes);
return savedPasswordHash;
}
static bool ValidatePassword(string password, string hashedPasswordFromDatabase)
{
byte[] hashBytes = Convert.FromBase64String(hashedPasswordFromDatabase);
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
byte[] hash = pbkdf2.GetBytes(20);
for (int i = 0; i < 20; i++)
{
if (hashBytes[i + 16] != hash[i])
{
return false;
}
}
return true;
}
The only material change between the above and your original code is that the HashPassword() method uses the password value passed in, rather than PasswordTextbox.Password.
Based on that observation, I can only surmise that in your own scenario, you are not really hashing the same password as you are validating later. Whether this is because PasswordTextbox.Password never did have the right password, or you are passing a different password later, I can't say.
If the above code example does not adequately point you in the right direction so that you can get your code working, please improve your question so that it includes a good MCVE.

How to make a password protected file in C#

I know this question has been asked before but the answer simply wasn't what I needed. I need to create a password protected text file. I don't mean to encrypt but just create a file with a simple text password. It would also be nice to find out how to open this file in C# as well.
Create a plain-text password protected file and open this file at a later time. All in C#.
Password protecting a text file that is not encrypted is not really possible. However, you can validate if your text file has been modified, so that you are aware it has been altered by someone.
The concept is simple. Use a password to encrypt or obfuscate a hash of the data (text file). You can the later check this hash against the current data and determine whether or not it matches. You will need to store this signature (encrypted hash) somewhere maybe in a file called (textfile.sig).
You can use the SHA1Managed .NET class to create the hash, and the TripleDESCryptoServiceProvider class to encrypt the resulting hash.
something like...
public string GetSignature(string text, string password)
{
byte[] key;
byte[] key2;
GetKeys(password, out key, out key2);
string sig = encryptstring(GetSHA1(text), key, key2);
}
public void GetKeys(string password, out byte[] key, out byte[] key2)
{
byte[] data = StringToByte(password);
SHA1 sha = new SHA1CryptoServiceProvider();
MD5 md5 = new MD5CryptoServiceProvider();
byte[] hash1 = sha.ComputeHash(data);
byte[] hash2 = md5.ComputeHash(data);
// Generate some key data based on the supplied password;
byte[] key = new byte[24];
for (int i = 0; i < 20; i++)
{
key[i] = hash1[i];
}
for (int i = 0; i < 4; i++)
{
key[i + 20] = hash2[i];
}
byte[] key2 = new byte[8];
for (int i = 0; i < 8; i++)
{
key2[i] = hash2[i+4];
}
}
public string GetSHA1(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
SHA1Managed hashString = new SHA1Managed();
string hex = "";
hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
public string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
public string encryptstring(string instr, byte[] key, byte[] key2)
{
TripleDES threedes = new TripleDESCryptoServiceProvider();
threedes.Key = key;
threedes.IV = key2;
ICryptoTransform encryptor = threedes.CreateEncryptor(key, key2);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
// Write all data to the crypto stream and flush it.
csEncrypt.Write(StringToByte(instr), 0, StringToByte(instr).Length);
csEncrypt.FlushFinalBlock();
return ByteToString(msEncrypt.ToArray());
}
public string decryptstring(string instr, byte[] key, byte[] key2)
{
if (string.IsNullOrEmpty(instr)) return "";
TripleDES threedes = new TripleDESCryptoServiceProvider();
threedes.Key = key;
threedes.IV = key2;
ICryptoTransform decryptor = threedes.CreateDecryptor(key, key2);
// Now decrypt the previously encrypted message using the decryptor
MemoryStream msDecrypt = new MemoryStream(HexStringToByte(instr));
CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
try
{
return ByteToString(csDecrypt);
}
catch (CryptographicException)
{
return "";
}
}
Otherwise, you will if you want to hide the data from people, you need to encrypt the data in the text file. You can do this by calling encryptstring() with the full text data instead of the hash of the data.
Whole point of the question was to not have to encrypt the data manually but let Windows deal with it so I decided to go with Hans Passant's suggestion and simply use a password protected zip file with the needed information (txt files) inside it. This can be easily done with DotNetZip.

InCorrect output using Rfc2898DeriveBytes for password hashing

I am certainly sure i am doing something wrong here. Using .net implementation of the algorithm i hash the password to store in database along with the salt used to hash. On validating the same password with the existing hash does not match.Here is my code
New Entry
byte[] SALT = GetRandomKey();
string password = Convert.ToBase64String((new Rfc2898DeriveBytes(txtPassword.Text, SALT)).GetBytes(20));
Session["test"] = password;
Session["salt"] = Convert.ToBase64String(SALT);
Validating
string HASHEDPASSWORD = Session["test"];
string SALT = Session["salt"];
string ouput = Convert.ToBase64String((new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(SALT))).GetBytes(20));
ltrResult.Text = HASHEDPASSWORD.Equals(ouput) ? "EQUAL" : "NOT EQUAL";
Get RandomKey method
byte[] GetRandomKey()
{
byte[] secretkey = new Byte[64];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(secretkey);
return secretkey;
}
Or Convert.FromBase64String instead of Encoding.Unicode.GetBytes.
You use Convert.ToBase64String when adding items, and Encoding.Unicode.GetBytes when retrieving it...
Use Encoding.Unicode.GetString when adding a new entry and your code should work, eg:
private static string GetString(byte[] bytes)
{
return Encoding.Unicode.GetString(bytes);
}
private static byte[] GetBytes(string value)
{
return Encoding.Unicode.GetBytes(value);
}
Adding
byte[] salt = GetRandomKey();
byte[] hash = new Rfc2898DeriveBytes(txtPassword.Text, salt)).GetBytes(20);
Session["test"] = GetString(hash);
Session["salt"] = GetString(salt);
Checking
byte[] hash = GetBytes(Session["test"]);
byte[] salt = GetBytes(Session["salt"]);
byte[] output = new Rfc2898DeriveBytes(password, salt).GetBytes(20);
ltrResult.Text = GetString(hash).Equals(GetString(output)) ? "EQUAL" : "NOT EQUAL";
You could store the salt as array of bytes in the session so that you don't get any differences of encoding when converting between strings and bytes:
byte[] SALT = GetRandomKey();
string password = Convert.ToBase64String((new Rfc2898DeriveBytes(txtPassword.Text, SALT)).GetBytes(20));
Session["test"] = password;
Session["salt"] = SALT;
and then to verify that a given password matches the hash you repeat the same procedure:
string HASHEDPASSWORD = Session["test"];
byte[] SALT = Session["salt"] as byte[];
string ouput = Convert.ToBase64String((new Rfc2898DeriveBytes(password, SALT)).GetBytes(20));
ltrResult.Text = HASHEDPASSWORD.Equals(ouput) ? "EQUAL" : "NOT EQUAL";

Categories