Not a SO question really but incase others find this.
Some do and donts. Home grown sites can be lacking here.
- Don't Store passwords, Store one-way hashes. Encode them if possible.
- Don't Use HMacSHA* as the algorithm, USE BCRPT, SCRYPT or RFC2898DerivedBytes(PBKDF2) for PASSWORDS. SHA is too fast and not suitable for password hashing.
- Use a SALT
- Prefer standard well tested and accepted tools eg in your environment ASP.Net Identity
- Always use SSL when requesting a users password
- If you Must (are you really sure) Encrypt and Decrypt have a machine Key / decryption key policy. The encryption is only as good as the key's safety.
If serious application consider using SCyrpt for c#
The bare minimum in standard c# to encode a password should be something like
the following using Rfc2898.
a) Store the encoded password and random SALT in you database.
b) Use a cost that takes at least 250 msecs.
c) compare the user provided password with stored SALT to the same routine and compare hashes.
private string PasswordEncode(string password, byte[] salt ) {
var deriver2898 = new Rfc2898DeriveBytes(password, salt,64000); // approx 300msecs
byte[] hash = deriver2898.GetBytes(20); //
// return hash;
// If you dont like storing bytes, use a string
return Convert.ToBase64String(hash);
}
// himalayan pink rock salt... the best kind
public byte[] GenerateSalt(int size = 64) {
using (var crypto = new RNGCryptoServiceProvider()) {
var bytes = new byte[size];
crypto.GetBytes(bytes); //get a bucket of very random bytes
return bytes;
}
}
if you at this stage are still convinced you need to Encrypt then use one of the symmetric algorithms from Microsoft. EG AesManaged
/// <summary>
/// Encrypt using preferred provider.
/// </summary>
/// <typeparam name="T">AesManaged,TripleDESCryptoServiceProvider,RijndaelManaged</typeparam>
/// <param name="value">Value to be encrypted</param>
/// <param name="decryptionKey">secret key .. see machine key descryptionKey</param>
/// <param name="salt">salt for process</param>
/// <returns></returns>
public string Encrypt<T>(string value, string salt, string decryptionKey)
where T : SymmetricAlgorithm, new() {
var derivedKey = GenerateKey(decryptionKey, salt);
SymmetricAlgorithm algorithm = new T();
byte[] rgbKey = derivedKey.GetBytes(algorithm.KeySize >> 3);
byte[] rgbIv = derivedKey.GetBytes(algorithm.BlockSize >> 3);
ICryptoTransform transform = algorithm.CreateEncryptor(rgbKey, rgbIv);
using (var buffer = new MemoryStream()) {
using (var stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write)) {
using (var writer = new StreamWriter(stream, Encoding.Unicode)) {
writer.Write(value);
}
}
// before finished with the buffer return, now as the stream is now closed
return Convert.ToBase64String(buffer.ToArray());
}
}
public string Decrypt<T>(string text, string salt, string decryptionKey)
where T : SymmetricAlgorithm, new() {
// could catch errors here, and return a null string. ?
// "CryptographicException: Padding is invalid and cannot be removed"
// can occur if there is a coding problem , such as invalid key or salt passed to this routine.
var derivedKey = GenerateKey(decryptionKey, salt);
SymmetricAlgorithm algorithm = new T();
byte[] rgbKey = derivedKey.GetBytes(algorithm.KeySize >> 3);
byte[] rgbIv = derivedKey.GetBytes(algorithm.BlockSize >> 3);
ICryptoTransform transform = algorithm.CreateDecryptor(rgbKey, rgbIv);
using (var buffer = new MemoryStream(Convert.FromBase64String(text))) {
using (var stream = new CryptoStream(buffer, transform, CryptoStreamMode.Read)) {
using (var reader = new StreamReader(stream, Encoding.Unicode)) {
return reader.ReadToEnd(); // error here implies wrong keys supplied , and code or environment problem.. NASTY issue
}
}
}
}
public DeriveBytes GenerateKey(string salt, string decryptionKey) {
// generate the key from the shared secret and the salt
var saltAsByteArray = salt.UTF8StringToByteArray();
var key = new Rfc2898DeriveBytes(decryptionKey, saltAsByteArray);
return key;
}
Sample calls: of encrypt and decrypt. Plus consider,how to get machine key and use as key here
Encrypt<AesManaged>(password, salt, decryptionKey);
Decrypt<AesManaged>(encryptedPassword, salt, decryptionKey);