I have a password which I hash with SHA256. Then I have a salt which looks like that:
AAAAAAAAAAAAAAAAAAAAAA==
At the end of the process both of them are byte-arrays which I then merge into a new byte-array.
My PROBLEM is that while merging the password with the salt, my hashed password gets shorter by one character at the end.
Expected output:
uIxnpgdBQpSPJrqwYucIOeyOyqyCv7HbBfd74ovoxjI=AAAAAAAAAAAAAAAAAAAAAAAAA==
Output:
uIxnpgdBQpSPJrqwYucIOeyOyqyCv7HbBfd74ovoxjIAAAAAAAAAAAAAAAAAAAAAAAAA==
As you can see there is a = missing after the l.
My Method:
public static byte[] HashPassword(byte[] passwordToHash)
{
byte[] hInput;
byte[] hSalt = GetSalt();
using(SHA256 sh = SHA256.Create())
{
hInput = sh.ComputeHash(passwordToHash);
}
byte[] SaltedPw = new byte[(hInput.Length+ 1 ) + (hSalt.Length + 3)];
Array.Copy(hInput,0, SaltedPw, 0,hInput.Length);
Array.Copy(hSalt, 0, SaltedPw, hInput.Length, hSalt.Length);
return SaltedPw;
}
public static byte[] GetSalt()
{
byte[] salt = new byte[16];
return salt;
}
How can I prevent the shortening of my password?
You are doing it wrong. You must not add the salt to the hashed password. You must add the salt to the plain password and then hash. The point is to make the hash of a current or short password unrecognizable.
The base 64 encoding is only applied to the final result to allow storing the password hash as string. Therefore, you will never have to merge base 64 strings. Base 64 strings are padded with = at the end to get a length which is a multiple of 4. Therefore you will never see a = in the middle.
public static string GetHashedPassword(string plainPassword, byte[] salt)
{
byte[] passwordBytes = GetBytes(plainPassword);
// Merge the password bytes and the salt bytes
var mergedBytes = new byte[passwordBytes.Length + salt.Length];
Array.Copy(passwordBytes, mergedBytes, passwordBytes.Length);
Array.Copy(salt, 0, mergedBytes, passwordBytes.Length, salt.Length);
// Now hash password + salt
byte[] hash;
using (var sha = SHA256.Create()) {
hash = sha.ComputeHash(mergedBytes);
}
return Base64Encode(hash);
}
You will also need this:
public static string Base64Encode(byte[] bytes)
{
return System.Convert.ToBase64String(bytes);
}
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
Create random salt bytes for each password and store the salt as a separate piece of information together with the hashed password. Like this, every password gets a different salt. This makes Pre-computed dictionary attack/Rainbow table attack infeasible. The salt does not need to be encrypted. You probably will want to store it as base 64 string as well. To get the salt bytes again you will need Convert.FromBase64String().
Related
I want to encrypt password using both in CryptoJS and C#. Unfortunately, my C# code fails to generate the proper value. This is my code
internal static byte[] ComputeSha256(this byte[] value)
{
using (SHA256 sha256Hash = SHA256.Create())
return sha256Hash.ComputeHash(value);
}
internal static byte[] ComputeSha256(this string value) => ComputeSha256(Encoding.UTF8.GetBytes(value));
internal static byte[] ComputeMD5(this byte[] value)
{
using (MD5 md5 = MD5.Create())
return md5.ComputeHash(value);
}
internal static byte[] ComputeMD5(this string value) => ComputeMD5(Encoding.UTF8.GetBytes(value));
internal static byte[] CombineByteArray(byte[] first, byte[] second)
{
byte[] bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
internal static string EncryptPassword()
{
using (AesManaged aes = new AesManaged())
{
//CLIENT SIDE PASSWORD HASH
/*
var password = '12345';
var passwordMd5 = CryptoJS.MD5(password);
var passwordKey = CryptoJS.SHA256(CryptoJS.SHA256(passwordMd5 + '12345678') + '01234567890123456');
var encryptedPassword = CryptoJS.AES.encrypt(passwordMd5, passwordKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });
encryptedPassword = CryptoJS.enc.Base64.parse(encryptedPassword.toString()).toString(CryptoJS.enc.Hex);
//encryptedPassword result is c3de82e9e8a28a4caded8c2ef0d49c80
*/
var y1 = Encoding.UTF8.GetBytes("12345678");
var y2 = Encoding.UTF8.GetBytes("01234567890123456");
var password = "12345";
var passwordMd5 = ComputeMD5(password);
var xkey = CombineByteArray(ComputeSha256(CombineByteArray(passwordMd5, y1)), y2);
var passwordKey = ComputeSha256(xkey);
aes.Key = passwordKey;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
ICryptoTransform crypt = aes.CreateEncryptor();
byte[] cipher = crypt.TransformFinalBlock(passwordMd5, 0, passwordMd5.Length);
var encryptedPassword = BitConverter.ToString(cipher).Replace("-", "").ToLower();
return encryptedPassword; //e969b60e87339625c32f805f17e6f993
}
}
The result of the C# code above is e969b60e87339625c32f805f17e6f993. It should be the same with CryptoJS c3de82e9e8a28a4caded8c2ef0d49c80. What is wrong here?
In the CryptoJS code hashes (in the form of WordArrays) and strings are added in several places. Thereby the WordArray is implicitly encoded with toString() into a hex string with lowercase letters. This is missing in the C# code.
In the C# code the addition is done with CombineByteArray(), where the hash is passed in the parameter first as byte[]. Therefore this parameter must first be converted to a hex encoded string with lowercase letters and then UTF8 encoded, e.g.:
internal static byte[] CombineByteArray(byte[] first, byte[] second)
{
// Hex encode (with lowercase letters) and Utf8 encode
string hex = ByteArrayToString(first).ToLower();
first = Encoding.UTF8.GetBytes(hex);
byte[] bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
where ByteArrayToString() is from here.
With this change, the C# code gives the same result as the CryptoJS code.
I am not quite clear about the purpose of the CryptoJS code. Usually plaintext and key are independent, i.e. are not derived from the same password.
Perhaps this is supposed to implement a custom password-based key derivation function. If so, and unless a custom implementation is mandatory for compatibility reasons, it is more secure to use a proven algorithm such as Argon2 or PBKDF2. In particular, the lack of a salt/work factor is insecure.
Having the hardest time trying to create a PBKDF2 valid password checker. The PBKDF2 code comes from a SharpHash project; https://github.com/ron4fun/SharpHash. The Class is: SharpHash/SharpHash.Tests/KDF/PBKDF2_HMACTests.cs
The example shows how to implement it but does not have any examples on how to verify the hash afterwards. I managed to tried several different "IsValidPassword" is one of the methods, but none of them seem to work. Each and every one of them the result is false no matter what values I add to the PBKDF2 or IsValidPassword methods. I also tried changing to a hex and also base64 but got the same results; it failed.
I even replaced Rfc2898DeriveBytes.
Does anyone have any experience with PBKDF2 password verification. This would be application based, not website based. IDE environment Visual Studios 2019 - C#.
Thank you.
public void TestOne()
{
IPBKDF2_HMAC PBKDF2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash, Password, Salt, 100000);
byte[] Key = PBKDF2.GetBytes(64);
PBKDF2.Clear();
string ActualString = Converters.ConvertBytesToHexString(Key, false);
Assert.AreEqual(ExpectedString, ActualString);
}
public bool IsValidPassword(string password, string hashPass)
{
bool result = false;
// Extract the bytes
byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
// Get the salt
byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
Array.Copy(hashBytes, 0, salt, 0, 20);// Doesn't matter what values and here; same issue… False
// Compute the hash on the password the user entered
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
byte[] hash = pbkdf2.GetBytes(64);
// compare the results
for (int i = 0; i < 20; i++) // If I go to 64 I get an error
{
if (hashBytes[i + 20] != hash[i])
{
return false;
}
}
return true;
}
// Replaced Rfc2898DeriveBytes
public bool IsValidPassword(string password, string hashPass)
{
bool result = false;
IHash hash1 = HashFactory.Crypto.CreateSHA1();
// Extract the bytes
byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
byte[] Password = Encoding.ASCII.GetBytes(password);
// Get the salt
byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
Array.Copy(hashBytes, 0, salt, 0, 20); // Doesn't matter what values and here; same issue… False
// Compute the hash on the password the user entered
var pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, 100000); // Replaced Rfc2898DeriveBytes
byte[] Key = pbkdf2.GetBytes(64);
pbkdf2.Clear();
string test = Converters.ConvertBytesToHexString(Key, false); // Taking a peek
string test2 = Encoding.ASCII.GetString(hashBytes); // Taking a peek
// compare the results
for (int i = 0; i < 20; i++)
{
if (hashBytes[i + 20] != Key[i])
{
return false;
}
}
return true;
}
taking a quick look at this, the issue you are encountering is simply a misrepresentation of the data contents involved.
password is a base256 based data while hashPass is a base16 based data.
You have to use the appropriate conversion routine when converting hashPass to byte[].
To do that, simply use this method found in the Converters class too.
byte[] hashBytes = Converters.ConvertHexStringToBytes(hashPass);
Do note that I assumed that your password is in base256 (since you didn't specify this in the question) so you can leave it as it is.
The only change you need to make is the one I described above.
Well, I suppose this will help someone else down the road apiece. I had to do some serious searching and banging my head up against the wall because this was my first attempt at PBKDF2 to come up with a functional verification method that isn't included with the SharpHash project. Just so that anyone reading this would understand what my problem was. The code generated the password with no issues whatsoever. However, there was no function in the code project that actually verified the password using the salt, generated password and iteration.
The code provided is the simple version because I have also added overloads to the methods that I don't need to post. This method has default settings whereas one of the overloads allows for complete customization of the hash algorithm, salt and iteration. Each of these I had tested and they worked as expected.
Hope this helps someone. :-)
private static Int32 Salt256bit { get; } = 256 / 8; // 256 bits = 32 Bytes
public static string GetHashPBKDF2Password(string password)
{
// Notes:
// Create a 32-byte secure salt and the same size of the key. This 32-byte produces 256 bits key output.
// Add the same 32-byte size to the pbkdf2.GetBytes.
// KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC hashes these values using the crypto SHA presented.
// Double the hashBytes bytes then add the salt and pbkdf2.GetBytes value.
// Copy each of the 32-bytes to the hashBytes bytes from the hashed salt and hashed value.
//
byte[] Password = Encoding.Unicode.GetBytes(password);
string hashPass = string.Empty;
// Create the salt value with a cryptographic PRNG
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[ByteSize]); // 32 Bytes = 256 bits.
// Create the KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC and get the hash value using the SHA presented.
// I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
IHash sha = HashFactory.Crypto.CreateSHA1();
IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(sha, Password, salt, Pbkdf2Iterations);
byte[] hash = pbkdf2.GetBytes(ByteSize); // 32 Bytes = 256 bits.
// Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
Int32 g = hash.Length + salt.Length;
byte[] hashBytes = new byte[g];
// Combine the salt and password bytes.
Array.Copy(salt, 0, hashBytes, 0, ByteSize);
Array.Copy(hash, 0, hashBytes, ByteSize, ByteSize);
// Turn the combined salt+hash into a string for storage
hashPass = Convert.ToBase64String(hashBytes);
return hashPass;
}
public static bool ValidatePBKDF2Password(string password, string hashPass)
{
try
{
byte[] Password = Encoding.Unicode.GetBytes(password);
bool result = true;
// Extract the bytes
byte[] hashBytes = Convert.FromBase64String(hashPass);
// Get the salt
byte[] salt = new byte[32];
Array.Copy(hashBytes, 0, salt, 0, 32);
// Compute the hash on the password that user entered.
// I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
IHash hash1 = HashFactory.Crypto.CreateSHA1();
IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, Pbkdf2Iterations);
byte[] hash = pbkdf2.GetBytes(32);
// compare the results
for (int i = 0; i < 32; i++)
{
if (hashBytes[i + 32] != hash[i])
{
result = false;
}
}
return result;
}
catch (Exception)
{
return false;
}
}
How to use: string GeneratedHash = PBKDF2Helper.GetHashPBKDF2Password("password");
Results: hv6t8N4rrVSKYFm80cCoVUEiUk2o11xLBc6lJb5kBXKTcwcKwl9dZwSdce01X0bi8BBhJY/QGGnNVAcR7ZhSvQ==
Verify Paword: Boolean tester = PBKDF2Helper.ValidatePBKDF2Password("password", GeneratedHash);
txtVerificationResults.Text = tester.ToString();
I am having some issues trying to match an MD5 encryption with salt from Lua to C#. Meaning, I have it hashing and salting the account password in Lua, but I need to match that exact same encryption in C# as I am developing a website in C# that needs to use the same database and accounts as the Lua script.
I've tried for quite some time now trying to match them, but no matter what I do I can't seem to get it right.
Lua hash:
if (string.len(cpypassword) ~= 64) then
password = md5(Newsalt .. password)
local result = mysql:query("SELECT username FROM accounts WHERE username='" .. username .. "'")
if (mysql:num_rows(result)>0) then
local insertid = mysql:query_insert_free("UPDATE accounts SET password='" .. mysql:escape_string(password) .. "' WHERE username='".. mysql:escape_string(username) .."'")
triggerClientEvent(client, "accounts:login:attempt", client, 1, "Password changed!\nThank you." )
end
end
I've tried a variety of different ways to do MD5 hash in C#, but none of them matches, so here I am now, asking you for suggestions.
Thank you in advance.
EDIT:
Lua function generates this as an example:
CFA62AA942A84781B1C101D6D583B641
Same example generated in C# with the C# hashing:
DSqwG/W1LNbHsCEkHNAUpg==
C# code (just one of the things I tried, I found much simpler ones, but this is the latest one I tried, just copied out of a tutorial)
public class Encryption
{
public static string EncryptorDecrypt(string securityCode, string key, bool encrypt)
{
byte[] toEncryptorDecryptArray;
ICryptoTransform cTransform;
// Transform the specified region of bytes array to resultArray
MD5CryptoServiceProvider md5Hasing = new MD5CryptoServiceProvider();
byte[] keyArrays = md5Hasing.ComputeHash(Encoding.UTF8.GetBytes(securityCode));
md5Hasing.Clear();
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider() { Key = keyArrays, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
if (encrypt == true)
{
toEncryptorDecryptArray = Encoding.UTF8.GetBytes(key);
cTransform = tdes.CreateEncryptor();
}
else
{
toEncryptorDecryptArray = Convert.FromBase64String(key.Replace(' ', '+'));
cTransform = tdes.CreateDecryptor();
}
byte[] resultsArray = cTransform.TransformFinalBlock(toEncryptorDecryptArray, 0, toEncryptorDecryptArray.Length);
tdes.Clear();
if (encrypt)
{ //if encrypt we need to return encrypted string
return Convert.ToBase64String(resultsArray, 0, resultsArray.Length);
}
//else we need to return decrypted string
return Encoding.UTF8.GetString(resultsArray);
}
}
The code you provided for C# is not generating an MD5 hash; instead it is hashing the securityCode and using it as a key for TripleDES.
Take a look at this blog post (copied relevant bits out):
public string CalculateMD5Hash(string input)
{
// step 1, calculate MD5 hash from input
MD5 md5 = System.Security.Cryptography.MD5.Create();
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hash = md5.ComputeHash(inputBytes);
// step 2, convert byte array to hex string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
}
I am using rijndaelmanaged algoritham for password encryption
Is there a way to restrict the size of the encrypted text key?
eg:1, ABC - Encrypted key size 10
2, ABCDHGF - Encrypted key size 10
Means Fixed size !!
If you do not need to have password back from encrypted data, you can use hash algorithms. First compute the hash value for the password and then encrypt this hash value. Since hash values have fixed length your encrypted data will have a fixed length. When you need to check a password, decrypt the encrypted value and recalculate hash value from entered password then check if they match.
For example on a sign up page
var encryptedPwd = Encrypt(ComputeHash(txtPassword.Text));
Save(txtUsername.Text, encryptedPwd);
And on a login page
var encryptedPwd = SelectPwd(txtUsername.Text);
var pwdHash1 = Decrypt(encryptedPwd);
var pwdHash2 = ComputeHash(txtPassword.Text);
if (AreEqual(pwdHash1, pwdHash2))
// Login OK!
else
// Login fail
Another option could be making a custom padding. Say your passwords will have max length of 16 characters. Then you can pad every password to 16 chars with some fixed char. Then encrypt this padded password. It would be easier for validation but using hash is a bit more secure.
Sign up
var encryptedPwd = Encrypt(txtPassword.Text.PadRight(16, 'X'));
Save(txtUsername.Text, encryptedPwd);
Login
var encryptedPwd = SelectPwd(txtUsername.Text);
var pwd1 = Decrypt(encryptedPwd);
var pwd2 = txtPassword.Text.PadRight(16, 'X');
if (AreEqual(pwd1, pwd2))
// Login OK!
else
// Login fail
Instead of using a simple hash, it's suggested to use a password-strengthening algorithm, like the one specified in Rfc2898
string password = "P#$$w0rd";
byte[] salt = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }; // this is fixed... It would be better you used something different for each user
// You can raise 1000 to greater numbers... more cycles = more security. Try
// balancing speed with security.
Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes(password, salt, 1000);
// generate key and iv
byte[] key = pwdGen.GetBytes(16);
byte[] iv = pwdGen.GetBytes(16);
byte[] encrypted;
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Key = key;
rijndaelCipher.IV = iv;
// Or your data
byte[] data = System.Text.Encoding.UTF8.GetBytes("hello world");
var encryptor = rijndaelCipher.CreateEncryptor();
encrypted = encryptor.TransformFinalBlock(data, 0, data.Length);
}
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Key = key;
rijndaelCipher.IV = iv;
var decryptor = rijndaelCipher.CreateDecryptor();
byte[] decrypted = decryptor.TransformFinalBlock(encrypted, 0, encrypted.Length);
// this if you are encrypting text, otherwise decrypted is already your data
string text = System.Text.Encoding.UTF8.GetString(decrypted);
}
My company uses the following algorithm to hash passwords before store it in the database:
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(valueBytes);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
stringBuilder.Append(hashBytes[i].ToString("x2"));
}
return stringBuilder.ToString();
}
To me it sounds like a trivial md5 hash, but when I tried to match a password (123456) the algorithm gives me ce0bfd15059b68d67688884d7a3d3e8c, and when I use a standard md5 hash it gives me e10adc3949ba59abbe56e057f20f883e.
A iOS version of the site is being build, and the users needs to login, the password will be hashed before sent. I told the iOS team to use a standard md5 hash, but of course it don't worked out.
I can't unhash the password and hash it again using the standard md5 (of course), and I don't know what exactly tell to the iOS team, in order to get the same hash.
Any help?
You need to use the same encoding on both ends (probably UTF8).
If you replace your code with
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes("123456"));
, you'll get e10adc3949ba59abbe56e057f20f883e.
You need to use UTF8 instead of Unicode. The following code works exactly like the PHP md5() function:
public static string md5(string value)
{
byte[] encoded = ASCIIEncoding.UTF8.GetBytes(value);
MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();
byte[] hashCode = md5Provider.ComputeHash(encoded);
string ret = "";
foreach (byte a in hashCode)
ret += String.Format("{0:x2}", a);
return ret;
}