InCorrect output using Rfc2898DeriveBytes for password hashing - c#

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";

Related

SHA 512 and or ARGON2ID Hashing + Salt For Dovecot in C#

I have tried to generate a salted hash using ARGON2ID and am running in to the same issues. The dovecot password generation utility generates the correct hash but I still cant seem to get things working using C#.
I am using the Konscious.Security.Cryptography.Argon2 Nuget Package at the moment..
Like I said still doesn't work even after playing with the length of the salt
I setup a new mail-server using postfix and dovecot. The password storage I am using is SSHA512 which means SHA512 with a salt. I need to create an ASP.NET page that allows the user to create and change their e-mail password in the DB. I could use the SQL route but I would like to do this in C# so I can use NHibernate which is my ORM.
I found a post online that creates the hash and salt using SQL.
INSERT INTO virtual_users
(id, domain_id, password , email)
VALUES
(NULL, '1', (SELECT REPLACE(TO_BASE64(CONCAT(UNHEX(SHA2(CONCAT('YourPasswordHere', v.salt), 512)), v.salt)), '\n', '') AS salted FROM (SELECT SHA2(RAND(), 512) AS salt) v), 'user#example.com');
UPDATE virtual_users
SET password = (
SELECT REPLACE(TO_BASE64(CONCAT(UNHEX(SHA2(CONCAT('YourPasswordHere', v.salt), 512)), v.salt)), '\n', '') AS salted
FROM (
SELECT SHA2(RAND(), 512) AS salt
) v
)
WHERE email = 'user#example.com';
Here is an explanation of what is going on. I am trying to do the following in C# and am not having any luck..
Generate a random (rather lengthy) salt
Append the salt to the password
Get the SHA512 Hash of #2
Convert the hash to binary
Append the salt to #4
Convert the whole thing to Base64
7 .Remove unwanted newlines added by MySQL's TO_BASE64() function
The result is a 256 byte long string so you may need to update your password field to VARCHAR(256).
Any instruction would be greatly appreciated.
Here is what I have so far but it doesn't seem to work
SH1512 Code:
//My Random Method
public byte[] GenerateRandomCryptographicBytes(int keyLength)
{
var randomBytes = new byte[keyLength];
var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomBytes);
return randomBytes;
}
//Hashing Method:
public PasswordHashResult HashWithSalt(string password, int saltLength, HashAlgorithm hashAlgo)
{
var rng = new Rng();
var saltBytes = rng.GenerateRandomCryptographicBytes(saltLength);
var passwordAsBytes = Encoding.UTF8.GetBytes(password);
var passwordWithSaltBytes = new List<byte>();
passwordWithSaltBytes.AddRange(passwordAsBytes);
passwordWithSaltBytes.AddRange(saltBytes);
var digestBytes = hashAlgo.ComputeHash(passwordWithSaltBytes.ToArray());
return new PasswordHashResult
{
Salt = Convert.ToBase64String(saltBytes),
PasswordHash = Convert.ToBase64String(digestBytes)
};
}
//Result Class:
public sealed class PasswordHashResult
{
public string PasswordHash { get; set; }
public string Salt { get; set; }
public string HashAndSalt { get; set; }
}
Argon2 Code:
public byte[] CreateHash(byte[] password, byte[] salt)
{
using var argon2 = new Argon2id(password);
argon2.Salt = salt;
argon2.DegreeOfParallelism = 8;
argon2.Iterations = 4;
argon2.MemorySize = 1024 * 128;
return argon2.GetBytes(32);
}
public static byte[] GenerateRandomCryptographicBytes()
{
var randomBytes = new byte[32];
var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomBytes);
return randomBytes;
}
var bytes = Encoding.UTF8.GetBytes(plainTextPassword);
var salt = PasswordService.GenerateRandomCryptographicBytes(); // Salt should be stored alongside hash.
var hash = passwordService.CreateHash(bytes, salt);
Here is what the Argon2id hash looks like.
{ARGON2ID}$argon2id$v=19$m=65536,t=3,p=1$UCLtz5ubAeKTzeIVo0pzPw$jDDq5JmNX9LRzmlzknJWsGYKJ0TLiEHG6MGt5jcp6XQ
Nothing Like What I Get returned from the code above.

base64_encode(pack("H*",$sha1Signature)) to C#

$sha1Signature = sha1($toEncrypt)
//This will give b14dcc7842a53f1ec7a621e77c106dfbe8283779
$base64Sha1Signature = base64_encode(pack("H*",$sha1Signature));
//This will give sU3MeEKlPx7HpiHnfBBt++goN3k=
My code is calculating SHA1 and displaying the required :
b14dcc7842a53f1ec7a621e77c106dfbe8283779
but Base 64 Encode Result is different. When I send
"b14dcc7842a53f1ec7a621e77c106dfbe8283779" to the following function it gives
"ZmVkMzg5ZjJlNjM0ZmE2YjYyYmRmYmZhZmQwNWJlNzYxMTc2Y2VlOQ==" but I want it to be
"sU3MeEKlPx7HpiHnfBBt++goN3k=".
my code :
string toEncrypt = password+merchantID.Value.ToString()+acquirerID.Value.ToString()+orderID.Value.ToString()+formattedPurchaseAmt1+currency.Value.ToString();
SHA1 sha1Hash = SHA1.Create();
//From String to byte array
byte[] sourceBytes = Encoding.UTF8.GetBytes(toEncrypt);
byte[] hashBytes = sha1Hash.ComputeHash(sourceBytes);
string hash = BitConverter.ToString(hashBytes).Replace("-", String.Empty);
SHA1 sha = new SHA1Managed();
string base64Sha1Signature1 = Convert.ToBase64String(Encoding.UTF8.GetBytes(hash));

Generating Hashed Passwords in C#

I don't know if this is the correct place to ask, but I am having an issue hashing passwords for MySql Backend. I am running mosquitto 1.4.3 broker and I have the mosquitto-auth-plugin working on the same server. But I want to move the auth-plugin to a new server. So I created a admin program in C# to add users and access controls however I cant seem to the get the correct hash code for the password.
Has anyone implemented this or is there some resoucres available to create the correct hash?
I have tried this Hash It Right
private const int SaltByteLength = 12;
private const int DerivedKeyLength = 24;
public string CreatePasswordHash(string password)
{
var salt = GenerateRandomSalt();
var iterationCount = GetIterationCount();
var hashValue = GenerateHashValue(password, salt, iterationCount);
var iterationCountBtyeArr = BitConverter.GetBytes(iterationCount);
var valueToSave = new byte[SaltByteLength + DerivedKeyLength + iterationCountBtyeArr.Length];
Buffer.BlockCopy(salt, 0, valueToSave, 0, SaltByteLength);
Buffer.BlockCopy(hashValue, 0, valueToSave, SaltByteLength, DerivedKeyLength);
Buffer.BlockCopy(iterationCountBtyeArr, 0, valueToSave, salt.Length + hashValue.Length, iterationCountBtyeArr.Length);
return Convert.ToBase64String(valueToSave);
}
private int GetIterationCount()
{
return 901;
}
private static byte[] GenerateRandomSalt()
{
var csprng = new RNGCryptoServiceProvider();
var salt = new byte[SaltByteLength];
csprng.GetBytes(salt);
return salt;
}
private static byte[] GenerateHashValue(string password, byte[] salt, int iterationCount)
{
byte[] hashValue;
var valueToHash = string.IsNullOrEmpty(password) ? string.Empty : password;
using (var pbkdf2 = new Rfc2898DeriveBytes(valueToHash, salt, iterationCount))
{
hashValue = pbkdf2.GetBytes(DerivedKeyLength);
}
return hashValue;
}
will this make it easier for you?
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
class Class1
{
static void Main(string[] args)
{
byte[] HashValue;
string MessageString = "This is the original message!";
//Create a new instance of the UnicodeEncoding class to
//convert the string into an array of Unicode bytes.
UnicodeEncoding UE = new UnicodeEncoding();
//Convert the string into an array of bytes.
byte[] MessageBytes = UE.GetBytes(MessageString);
//Create a new instance of the SHA1Managed class to create
//the hash value.
SHA1Managed SHhash = new SHA1Managed();
//Create the hash value from the array of bytes.
HashValue = SHhash.ComputeHash(MessageBytes);
//Display the hash value to the console.
foreach(byte b in HashValue)
{
Console.Write("{0} ", b);
}
}

Always return false on authentication with hashed password

Working with hashing password with sha256 and salt, my validate method always return false. While debugging I had noticed that on return it compare first 32 bite arrays with 64. And I don't know where I did mistake.
public static byte[] Hash(string value, byte[] salt)
{
salt = new byte[64];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(salt);
}
return Hash(Encoding.UTF8.GetBytes(value), salt);
}
public static byte[] Hash(byte[] value, byte[] salt)
{
var saltedValue = value.Concat(salt).ToArray();
return new SHA256Managed().ComputeHash(saltedValue);
}
public bool ConfirmPassword(string password)
{
var passwordSalt = new byte[64];
using (var context = new DbConnection())
{
context.Connection.Open();
context.SqlCommand.Connection = context.Connection;
context.SqlCommand.CommandText = "select salt from users where name='test'";
var reader = context.SqlCommand.ExecuteReader();
while (reader.Read())
{
passwordSalt = reader["salt"] as byte[];
}
}
var passwordHash = Hash(password, passwordSalt);
return passwordHash.SequenceEqual(passwordSalt);
}
UPDATE
So if I get it right:
public bool ConfirmPassword(string password)
{
var userSalt = new byte[64];
var temp = new byte[64];
using (var context = new DbConnection())
{
context.Connection.Open();
context.SqlCommand.Connection = context.Connection;
context.SqlCommand.CommandText = "select password from users where name='test'";
var reader = context.SqlCommand.ExecuteReader();
while (reader.Read())
{
temp = reader["password"] as byte[];
}
}
var passwordHash = Hash(password, userSalt);
return passwordHash.SequenceEqual(temp);
}
But it also return me false
Lets assume your password is "Hello" and your name="test".
Storing
You need to generate a random salt. Than you can use the salt and the plain password ("Hello") with your Hash function. To save now the result in your database you MUST store the generated salt and the hashed password.
Checking
Load the salt and hashed password from the database. Use again your Hash function with the user input (the plain password you want to check) and the salt from the database. Afterwards you can compare the result with the hashed password from the database. And thats it ...

Only Able to Decrypt Encrypted Data First Time

I have the following code to Encrypt/Decrypt PII data.
public static class Encryption
{
public static readonly int KeyLengthBits = 256; //AES Key Length in bits
public static readonly int SaltLength = 8; //Salt length in bytes
private static readonly RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes
private const int PBKDF2SubkeyLength = 256 / 8; // 256 bits
private const int SaltSize = 128 / 8; // 128 bits
public static string DecryptString(string ciphertext, string passphrase)
{
var inputs = ciphertext.Split(":".ToCharArray(), 3);
var iv = Convert.FromBase64String(inputs[0]); // Extract the IV
var salt = Convert.FromBase64String(inputs[1]); // Extract the salt
var ciphertextBytes = Convert.FromBase64String(inputs[2]); // Extract the ciphertext
// Derive the key from the supplied passphrase and extracted salt
byte[] key = DeriveKeyFromPassphrase(passphrase, salt);
// Decrypt
byte[] plaintext = DoCryptoOperation(ciphertextBytes, key, iv, false);
// Return the decrypted string
return Encoding.UTF8.GetString(plaintext);
}
public static string EncryptString(string plaintext, string passphrase)
{
var salt = GenerateRandomBytes(SaltLength); // Random salt
var iv = GenerateRandomBytes(16); // AES is always a 128-bit block size
var key = DeriveKeyFromPassphrase(passphrase, salt); // Derive the key from the passphrase
// Encrypt
var ciphertext = DoCryptoOperation(Encoding.UTF8.GetBytes(plaintext), key, iv, true);
// Return the formatted string
return String.Format("{0}:{1}:{2}", Convert.ToBase64String(iv), Convert.ToBase64String(salt), Convert.ToBase64String(ciphertext));
}
private static byte[] DeriveKeyFromPassphrase(string passphrase, byte[] salt, int iterationCount = 2000)
{
var keyDerivationFunction = new Rfc2898DeriveBytes(passphrase, salt, iterationCount); //PBKDF2
return keyDerivationFunction.GetBytes(KeyLengthBits / 8);
}
private static byte[] GenerateRandomBytes(int lengthBytes)
{
var bytes = new byte[lengthBytes];
rng.GetBytes(bytes);
return bytes;
}
// This function does both encryption and decryption, depending on the value of the "encrypt" parameter
private static byte[] DoCryptoOperation(byte[] inputData, byte[] key, byte[] iv, bool encrypt)
{
byte[] output;
using (var aes = new AesCryptoServiceProvider())
using (var ms = new MemoryStream())
{
var cryptoTransform = encrypt ? aes.CreateEncryptor(key, iv) : aes.CreateDecryptor(key, iv);
using (var cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
cs.Write(inputData, 0, inputData.Length);
output = ms.ToArray();
}
return output;
}
}
If I publish this code and encrypt data and then decrypt it, there's no problem. I correctly receive the unencrypted data. If I rebuild my application without making any changes, then publish it again without re-encrypting the data, (it's already encrypted at this point), I'm unable to decrypt it. It just returns the encrypted strings.
Why would re-publishing to the same server to this? Should I be using a different approach? If so, please recommend that different approach.
Thanks

Categories