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
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);
}
}
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.
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.
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;
}
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);
}