Digital Signature and the identical passwords - c#

If Two users enter the same password, The values in the DB will be the same.
Any advice , please
#region "Digital Signature"
public static String EncryptDS (this String dataToEncrypt)
{
//Convert dataToEncrypt to byte array
byte[] plainTextBytes = Encoding.Unicode.GetBytes(dataToEncrypt);
//===================================
//Create Crypto Service provides params (24 allows SHA256 hashing alogrithm)
CspParameters cspParams = new CspParameters();
cspParams = new CspParameters(24);
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.KeyContainerName = "TT_KEY";
//====================================
//Generate asymmetric key
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspParams);
//=====================================
//hash and then encrypt the hash(digitally sign)
byte[] sig = rsa.SignData(plainTextBytes, "SHA256");
//=====================================
//Return signed encrypt the hash (digitally sign)
return Convert.ToBase64String(sig);
}

Best practise for password hashing includes creating a reasonably long salt (random sequence of characters) which is stored in the database beside the password.
When hashing the password, concatenate it with the salt first. If two users have the same password, they will have different salts so the hashes will be different. This also helps prevent rainbow tables/mass brute forcing.

Related

What would be the best way to implement SHA512 into my code?

So I need to store passwords in a SQL database and it would be insecure to store them in plain text. For a variety of reasons, I chose SHA512 to hash the passwords prior to storage. I, for the life of me, can not identify how to take data from a Secure string gained from user input, and hash it using SHA512 (which also means I haven't been able to look into salting it either).
I have seen online that you call a new instance of SHA512 but that it has to be managed (?) but when I try it shows that it is obsolete. Looking further, the wise internet suggested the create method of SHA512... which is also obsolete.
Any help into how I can hash and salt a secure string would be great.
Here is a basic hash and salt method using SHA512 and a random salt prepended to the hash.
private static string HashAndSalt(string plaintext)
{
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
using var hasher = SHA512.Create();
var random = new Random();
var ciphertextBytes = hasher.ComputeHash(Encoding.UTF8.GetBytes(plaintext));
var ciphertextB64 = Convert.ToBase64String(ciphertextBytes);
var salt = new string(Enumerable.Repeat(chars, 8).Select(s => s[random.Next(s.Length)]).ToArray());
var ciphertext = salt + ':' + ciphertextB64;
return ciphertext;
}
You will need to convert your SecureString to a normal string. Using a SecureString is obsolete and should not be used anymore. As the comments mentioned you should look into a dedicated password hashing algorithm such as:
PBKDF2
Argon2
Bcrypt
Scrypt
Here is an example using Bcrypt - it is not too complicated.
First, grab this NuGet package: BCrypt.Net-Next
private static string BcryptHash(string plaintext)
{
var ciphertext = BCrypt.Net.BCrypt.HashPassword(plaintext, 12);
return ciphertext;
}
You can't however compare the hashes when a user tries to authenticate as you can with SHA512. You need to use Bcrypts 'Verify' function.
private static bool BcryptVerify(string plaintext)
{
return BCrypt.Net.BCrypt.Verify(plaintext, hashedPassword);
}
Where the plaintext is the plaintext password you receive from user input and the hashedPassword being the hash you retrieved from the database where the initial ciphertext was stored.
Hope this helps.
I think this is what you are asking?
public static string GetHash(string s)
{
using var h = SHA512.Create();
return Convert.ToBase64String(h.ComputeHash(Encoding.UTF8.GetBytes(s)));
}
Have a look here for some other inspirations of how to do this:
https://github.com/aspnet/AspNetWebStack/blob/main/src/System.Web.Helpers/Crypto.cs

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.

C# use SHA1 to hash string into byte array

I'm using SHA1 to encrypt some values like password.
This is my code:
String passwd = Membership.GeneratePassword(10, 2);
SHA1 sha = new SHA1CryptoServiceProvider();
byte [] password = sha.ComputeHash(passwd);
But VS returns error, because passwd is a string.
I have to store the password in a byte array, so is there a way to solve this?
String passwd = Membership.GeneratePassword(10, 2);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(passwd);
SHA1 sha = new SHA1CryptoServiceProvider();
byte [] password = sha.ComputeHash(bytes);
Note that SHA1 does not encrypt data but hash them instead. Encrypted data can be decrypted. Hash algorithms are one way.
Use an Encoding to convert the string to a byte array
var bytes= Encoding.UTF8.GetBytes(passwd);
var password = sha.ComputeHash(bytes);

Hashing and GetString/GetBytes issue

I have the below code to hash/store/retrieve data for passwords but my first unit test and it fails.
I beleive its the Encoding causing the problem because when GetBytes is called it returns byte[38], byte[36] when it should be 20 I think.
I have to convert to string as I'm storing it in a database.
Any ideas? Thanks
[Fact]
public void EncryptDecryptPasswordShouldMatch()
{
string password = "password";
string passwordKey = string.Empty;
string passwordSalt = string.Empty;
Helpers.CreatePasswordHash(password, out passwordSalt, out passwordKey);
Assert.True(Helpers.PasswordsMatch(passwordSalt, passwordKey, password));
}
public static bool PasswordsMatch(string passwordSalt, string passwordKey, string password)
{
byte[] salt = Encoding.UTF8.GetBytes(passwordSalt);
byte[] key = Encoding.UTF8.GetBytes(passwordKey);
using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
{
byte[] newKey = deriveBytes.GetBytes(20); // derive a 20-byte key
if (!newKey.SequenceEqual(key))
return false;
}
return true;
}
public static void CreatePasswordHash(string password, out string passwordSalt, out string passwordKey)
{
// specify that we want to randomly generate a 20-byte salt
using (var deriveBytes = new Rfc2898DeriveBytes(password, 20))
{
byte[] salt = deriveBytes.Salt;
byte[] key = deriveBytes.GetBytes(20); // derive a 20-byte key
passwordSalt = Encoding.UTF8.GetString(salt);
passwordKey = Encoding.UTF8.GetString(key);
}
}
Use Base64 to encode binary values to string, it can deal with arbitrary byte sequences. UTF-8 is for transforming between unicode text and bytes and not every valid sequence of bytes is valid for UTF-8. Use Utf-8 to turn the password(which is text) to bytes, but use Base64 for salt and hash.
Convert.ToBase64String and Convert.FromBase64String should do the trick.
Some additional notes:
Your terminology is really weird, don't call the hash key, call it hash.
I'd concatenate the hash and salt in your CreatePasswordHash function, so the caller doesn't have to bother with having two separate values.
Something like return Base64Encode(salt)+"$"+Base64Encode(hash) then use string.Split in the verification function.
It's recommended to use a constant time comparison to verify, but it seems unlikely your timing side-channel can actually be exploited.
Your iteration count is pretty low. I recommend increasing it to 10000.
Modify your code to use the Convert.FromBase64String method:
byte[] salt = Convert.FromBase64String(passwordSalt);
byte[] key = Convert.FromBase64String(passwordKey);
Modify your code to use the Convert.ToBase64String method:
passwordSalt = Convert.ToBase64String(salt);
passwordKey = Convert.ToBase64String(key);
UTF8 is not a way to turn any random bytes into a string. It is for encoding text; not just any bytes are valid UTF8 encoded values. You could use Base64 to and from conversions. Note that base64-encoded strings will take up ~4/3 times the characters of the raw bytes. Here's an example:
byte[] salt = deriveBytes.Salt;
byte[] key = deriveBytes.GetBytes(20); // derive a 20-byte key
passwordSalt = Convert.ToBase64String(salt);
passwordKey = Convert.ToBase64String(key);
And later:
byte[] salt = Convert.FromBase64String(passwordSalt);
byte[] key = Convert.FromBase64String(passwordKey);

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