How to hash a password stored in a securestring? - c#

My C# console app actually prompts the user for a password and process the password as a string:
Console.Write("Please enter your password: ");
string password = GetPassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
Is there a way to modify the code to use a secure password (reducing the risk of having the password be exposed throught a memory dump)?
I would like something like:
Console.Write("Please enter your password: ");
SecureString password = GetSecurePassword();
securePassword = GetSecurePassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
password.Dispose();
Below a complete sample the compiles under Visual Studio 2015:
using System;
using System.Security;
using System.Security.Cryptography;
using System.Text;
namespace PasswordTest
{
internal class Program
{
public static byte[] GenerateSalt()
{
const int saltLength = 32;
using (var randomNumberGenerator = new RNGCryptoServiceProvider())
{
var randomNumber = new byte[saltLength];
randomNumberGenerator.GetBytes(randomNumber);
return randomNumber;
}
}
private static byte[] Combine(byte[] first, byte[] second)
{
var ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static byte[] HashPasswordWithSalt(byte[] toBeHashed, byte[] salt)
{
using (var sha256 = SHA256.Create())
{
return sha256.ComputeHash(Combine(toBeHashed, salt));
}
}
private static string GetPassword()
{
// skip here the code that prompts the password
// to make simple test that just return a password
return "mypa55w0rd";
}
// Here the code that would normally be used to prompt for a password
// and return a SecureString
public static SecureString GetSecurePassword()
{
var password = new SecureString();
// get the first character of the password
var nextKey = Console.ReadKey(true);
while (nextKey.Key != ConsoleKey.Enter)
{
if (nextKey.Key == ConsoleKey.Backspace)
{
if (password.Length > 0)
{
password.RemoveAt(password.Length - 1);
// erase the last * as well
Console.Write(nextKey.KeyChar);
Console.Write(" ");
Console.Write(nextKey.KeyChar);
}
}
else
{
password.AppendChar(nextKey.KeyChar);
Console.Write("*");
}
nextKey = Console.ReadKey(true);
}
Console.WriteLine();
// lock the password down
password.MakeReadOnly();
return password;
}
private static void Main(string[] args)
{
// Generate the salt
var salt = GenerateSalt();
// Hash a password stored in a string variable
// Issue: can be exposed through a memory dump
var password = GetPassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
// How to hash a password stored in a securestring so that the code could become
// something like:
/*
Console.Write("Please enter your password: ");
SecureString password = GetSecurePassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
password.Dispose();
*/
Console.WriteLine("Hashed Password = " + Convert.ToBase64String(hashedPassword));
Console.ReadLine();
}
}
}

Related

Bouncy Castle Encrypt and Decrypt Password - Org.BouncyCastle.Crypto.InvalidCipherTextException: 'block incorrect'

NOTE: This is NOT for encrypting user passwords. Use a Hash to store user passwords. You can also throw some salt on those passwords similarly to salt bae.
I need to store one of my passwords to log into a program. I can encrypt the password but when I decrypt I get the error:
Org.BouncyCastle.Crypto.InvalidCipherTextException: 'block incorrect'
I am going to use a different private key so I'm not worried about posting this one here.
Program Class:
var input = "TestPassword123!";
Encryption EC = new Encryption();
var Key = "-----BEGIN RSA PRIVATE KEY-----MIICXAIBAAKBgQCr874y+f3cYtz8hOuxamwHYUK7WS03c3t7LHMuCpmiR9wOz1FHEbBk6ZdvhY74I9sE+lIs0bUZVh08nKIS/txeECZ/d3WYDm8gW+mFjg0xD7LgQ9Prchsvrb2Q9c6FK5Us8i0iqOhDmrZdCYO+/zhyrYnP/Wy1EZTWPvMrI/GVWwIDAQABAoGBAJW5Sg8HIKYKq5NxhezY1rHiXYHWV4nmE0ogXTZk4nusiI+Ys5PncimcQFFKAUBNsEL4WB9QhplGI3UIihNyOZx68waSB6NiAi+tz8i7edTxDfwWkM271roOJxD3RzUJuQkyc57ZxrD61UyodRatmRoJkPhLBlEvIr9/dCwYBl0pAkEA5ucK8CFdxr4DWIAutZLeeeQx9oe1tCi/iUvEpSDOHKAvaELLBiD4bIInN4nx/Gj1gwHqICg8L/gs0x23cQWz3QJBAL6kYaRQFvCmVajTaTJ3ef3gYwynPsmfT5Urx0DWW5rXtbtZQ7vScDKRU4YbmBb3tD5eT/Fl/dIakPGv+hxllpcCQA3o5TPKDsgmMjVmo2m0y0o0gP4E9uE/gypWMiZwb3ox/CkfvPNFkTGYNZyW5sj52bBmsR/2zIlnPewJ4j8gxNECQDOT/8LX1E0Jg5VolUaNIpk4UzQCL6+XBY4DSSZWmHQdNlLYxMWVGrvjq6hV4OEd5UsWcySJhpGIlHx/Mvjp10MCQAon0SLhRI8QNrWfp/dLuFdqTy4b4y64j3HvooOu6GDZwCLwjqvwML3rimxUi52EQspssIOlLjpQjvIJvYBKBys=-----END RSA PRIVATE KEY-----";
var encrypted = EC.RsaEncrypt(input, Key);
string password = EC.RsaDecrypt(encrypted, Key);
Encryption Class
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;
namespace TemarkNamespace
{
internal class Encryption
{
public string RsaEncrypt(string clearText, string Key)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
var encryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(Key))
{
var keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyPair.Private);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
}
// Decryption:
public string RsaDecrypt(string pass, string Key)
{
var bytesToDecrypt = Convert.FromBase64String(pass);
AsymmetricCipherKeyPair keyPair;
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(Key))
{
keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
decryptEngine.Init(false, keyPair.Private);
}
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
return decrypted;
}
}
}
I found this code and it seems to work fine
using System.Security.Cryptography;
public string Encrypt(string s)
{
if (String.IsNullOrEmpty(s))
{
return s;
}
else
{
var encoding = new UTF8Encoding();
byte[] plain = encoding.GetBytes(s);
byte[] secret = ProtectedData.Protect(plain, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(secret);
}
}
public string Decrypt(string s)
{
if (String.IsNullOrEmpty(s))
{
return s;
}
else
{
byte[] secret = Convert.FromBase64String(s);
byte[] plain = ProtectedData.Unprotect(secret, null, DataProtectionScope.CurrentUser);
var encoding = new UTF8Encoding();
return encoding.GetString(plain);
}
}

C# & NodeJS PBKDF Encryption

im making a website and a desktop application, they both will use the same database but i need them to generate the same hashes, how can i do this with NodeJS and C#?
const crypto = require('crypto');
const key = crypto.pbkdf2Sync('pass', 'saltsaltsaltsalt', 100000, 64, 'sha256').toString('base64');
console.log(key);
Output: lLRnCThEdyMUjj6QazuIBvI/hRv7bT4r9Z5FKJ39NOOmFvI+5V5YhqTxInWCi3D8rOB1eMGV/Yn71m597/bzWw==
using System;
using System.Security.Cryptography;
public class Program
{
private const int HashIterations = 100000;
public static void Main()
{
var password = "pass";
var salt = "saltsaltsaltsalt";
var xx = GeneratePasswordHash(password, salt);
Console.WriteLine(xx);
}
public static string GeneratePasswordHash(string password, string salt, int hashIterations = HashIterations)
{
byte[] hash;
using (var crypto = new Rfc2898DeriveBytes(password, Convert.FromBase64String(salt), hashIterations))
{
hash = crypto.GetBytes(64);
}
return Convert.ToBase64String(hash);
}
}
Output: 0Nj5NJWM8dtAFFUU8T3UUQhmdNIbKTLndqjqXzkulEXRVsjlIb28C00daPi96pDk+evKY3CLbkLOGVZUuNiimQ==
what im expecting and need is both these programs to output the same hash.
According to this answer there is a nuget package that enables the use of PBKDF2 in c#.
According to the documentation you can use it like this:
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
var password = "pass";
var salt = "saltsaltsaltsalt";
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: Convert.FromBase64String(salt),
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 64));
Console.WriteLine(hashed);
I can currently not verify that this gives you the same hash as your NodeJS code.

Changing Storage of Password to Storing of Hashes in C# Forms

My current project is using Microsoft's Membership to manage user's credentials.
For now, it stores passwords into SQL DB in plain-text.
I want to change it store hashes instead, or to encrypt/hide password column.
Current code:
protected void BtnSubmit_Click(object sender, EventArgs e)
{
Page.Validate();
if (Page.IsValid)
{
NAME_OF_TABLE_Provider provider = new NAME_OF_TABLE_Provider();
string userName = SPContext.Current.Web.CurrentUser.Name;
MembershipUserCollection userMembership = Membership.GetAllUsers();
MembershipUser membershipUser = userMembership[userName];
if (membershipUser != null)
{
try
{
membershipUser.ChangePassword(OldPassword.Text, NewPassword.Text);
ConfirmPanel.Visible = true;
InvalidPanel.Visible = false;
Message.InnerText = "The password is changed successfully";
}
catch (Exception ex)
{
ConfirmPanel.Visible = false;
InvalidPanel.Visible = true;
InvalidPassword.InnerText = "The password is not strong. Please verify.";
}
}
}
}
#1 Possible(?) Solution: HashBytes
INSERT INTO <tbl> (..., passwd) values (...., HashBytes('SHA1', #password))
SELECT HashBytes('SHA1', #password);
#2 Possible(?) Solution: C# Storing of Hashes
static void Main(string[] args)
{
//Store a password hash:
PasswordHash hash = new PasswordHash("password");
byte[] hashBytes = hash.ToArray();
//For testing purposes
Console.WriteLine(Convert.ToBase64String(hashBytes));
//Check password against a stored hash
byte[] hashBytes2 = hashBytes;//read from store.
PasswordHash hash2 = new PasswordHash(hashBytes2);
if (!hash.Verify("password"))
{
throw new System.UnauthorizedAccessException();
}
else
{
Console.WriteLine("True");
}
Console.ReadLine();
}
}
public sealed class PasswordHash
{
const int SaltSize = 16, HashSize = 20, HashIter = 10000;
readonly byte[] _salt, _hash;
public PasswordHash(string password)
{
new RNGCryptoServiceProvider().GetBytes(_salt = new byte[SaltSize]);
_hash = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
}
public PasswordHash(byte[] hashBytes)
{
Array.Copy(hashBytes, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hashBytes, SaltSize, _hash = new byte[HashSize], 0, HashSize);
}
public PasswordHash(byte[] salt, byte[] hash)
{
Array.Copy(salt, 0, _salt = new byte[SaltSize], 0, SaltSize);
Array.Copy(hash, 0, _hash = new byte[HashSize], 0, HashSize);
}
public byte[] ToArray()
{
byte[] hashBytes = new byte[SaltSize + HashSize];
Array.Copy(_salt, 0, hashBytes, 0, SaltSize);
Array.Copy(_hash, 0, hashBytes, SaltSize, HashSize);
return hashBytes;
}
public byte[] Salt { get { return (byte[])_salt.Clone(); } }
public byte[] Hash { get { return (byte[])_hash.Clone(); } }
public bool Verify(string password)
{
byte[] test = new Rfc2898DeriveBytes(password, _salt, HashIter).GetBytes(HashSize);
for (int i = 0; i < HashSize; i++)
if (test[i] != _hash[i])
return false;
return true;
}
#3 Possible(?) Solution: Transact-SQL to encrypt single column
https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/encrypt-a-column-of-data?view=sql-server-2017#TsqlProcedure
May I know what is the correct approach? Thank you.
It goes without saying that any solution is better than the current one.
The first solution looks solid, but I could not find out if a salt is used. If not, that's a disadvantage. You also are now dependent on this database vendor and cannot switch.
The second solution looks good, but I didn't look too closely, you should probably head over to CodeReview, they do reviews of working code to look for improvements.
The last solution is not really a solution. Passwords are hashed not exncrypted for a reason. If you can decrypt them, the attacker who gets access to the system and steals them will have the same access to the decryption means as you do. So it's a layer of inconvenience, not security.
So pick number two and let somebody look over it for weaknesses or bugs.

The right way to compare hashed passwords

I have a field in my database which is binary(32) for storing SHA-256 passwords. Since MSSQL store the hash in upper case and with 0x prefix, I've done this:
public static string getHashSha256(string text)
{
byte[] bytes = Encoding.UTF8.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 "0x" + hashString.ToUpper();
}
Is this acceptable or there is a more appropriate way to do this?
public static string ConvertToHash(string dataToComputeHash)
{
var hash = "";
try
{
var keyByte = encoding.GetBytes(key);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
hmacsha256.ComputeHash(encoding.GetBytes(dataToComputeHash));
hash = ByteToString(hmacsha256.Hash);
}
}
catch (Exception ex)
{
}
return hash;
}

Encrypted password ans salt does not match when I validate them

I use the following code to Encrypt and protect the password and I add salt to it, but when I try to validate it when the user login they don't match, I don't know why.
public static class Encrypt
{
public static string saltValue { get; set; }
public static string hashValue { get; set; }
public static void SecurePassword(string password)
{
// Create a truly random salt using RNGCryptoServiceProvider.
RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
byte[] salt = new byte[32];
csprng.GetBytes(salt);
// Get the salt value
saltValue = Convert.ToBase64String(salt);
// Salt the password
byte[] saltedPassword = Encoding.UTF8.GetBytes(saltValue + password);
// Hash the salted password using SHA256
SHA512Managed hashstring = new SHA512Managed();
byte[] hash = hashstring.ComputeHash(saltedPassword);
// Save both the salt and the hash in the user's database record.
saltValue = Convert.ToBase64String(salt);
hashValue = Convert.ToBase64String(hash);
}
public static void ValidateLogin(string password, string username)
{
// Read the user's salt value from the database
string saltValueFromDB = saltValue;
// Read the user's hash value from the database
string hashValueFromDB = hashValue;
byte[] saltedPassword = Encoding.UTF8.GetBytes(saltValueFromDB + password);
// Hash the salted password using SHA256
SHA512Managed hashstring = new SHA512Managed();
byte[] hash = hashstring.ComputeHash(saltedPassword);
string hashToCompare = Convert.ToBase64String(hash);
if (hashValueFromDB.Equals(hashToCompare))
Console.WriteLine("User Validated.");
else
Console.WriteLine("Login credentials incorrect. User not validated.");
}
}
Please advise. Thank you in advance
Changed your code a bit but this works:
public class Encrypt
{
public HashedCredential SecurePassword(string password, string salt = "")
{
var saltValue = salt;
if (string.IsNullOrEmpty(salt))
{
saltValue = GenertateSalt();
}
// Salt the password
byte[] saltedPassword = Encoding.UTF8.GetBytes(saltValue + password);
// Hash the salted password using SHA256
SHA512Managed hashstring = new SHA512Managed();
byte[] hash = hashstring.ComputeHash(saltedPassword);
return new HashedCredential(saltValue, Convert.ToBase64String(hash));
}
private string GenertateSalt()
{
RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();
byte[] salt = new byte[32];
csprng.GetBytes(salt);
return Convert.ToBase64String(salt);
}
}
public class HashedCredential
{
public string SaltValue { get; }
public string HashValue { get; }
public HashedCredential(string saltValue, string hashValue)
{
SaltValue = saltValue;
HashValue = hashValue;
}
}
[TestMethod]
public void GenerateSalt()
{
// Arrange
var sut = new Encrypt();
// Act
var result = sut.SecurePassword("Test");
var resultB = sut.SecurePassword("Test", result.SaltValue);
// Assert
Console.WriteLine($"resultA:'{result.HashValue}'");
Console.WriteLine($"resultB:'{resultB.HashValue}'");
Assert.AreEqual(result.HashValue, resultB.HashValue);
}

Categories

Resources