Link to home
Start Free TrialLog in
Avatar of Gordon Saxby
Gordon SaxbyFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Secure passwords in a ASP.NET 2.0 Custom Membership Provider

I am writing a custom membership provider which gets logon info from a 3rd party database.

I have it working using plain text passwords but I want to secure the passwords - ideally, so that I can retain the "forgot password" feature but if I have to change it to "reset password" functionality then I could.

Basically, I just need a clear explanation of how I go about encrypting or hashing the password when it is first created and how to validate a user logon. Also, how to retrieve or reset the password, depending on which method is used (encrypt or hash?)
ASKER CERTIFIED SOLUTION
Avatar of urir10
urir10

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ok when your user sign up you should has the password using hash algorithms, recommend SHA-256 using this code

public static String HashPassword(String Pass)
    {
        System.Security.Cryptography.SHA256Cng sha = new System.Security.Cryptography.SHA256Cng();
        UnicodeEncoding uniEnc = new UnicodeEncoding();
        Byte[] hashedPassword = sha.ComputeHash(uniEnc.GetBytes(Pass));
        String password = "";
        foreach (Byte item in hashedPassword)
        {
            password += String.Format("{0:x2}", item);

        }
        //End of Computation
        return password;
    }

then after hashing the password store it in the database insted of the clear password

and each time your user login you should hash the entered password by the user and compare it by the hashed password that you stored it in the database.


check this http://www.codeproject.com/KB/recipes/StoringPasswords.aspx for more info
Well with hash the user won't be able to retrieve the current password because hash is one-way encryption, if you want to give them the retrieve current password possibility, you could use this utility to encrypt and decrypt your passwords with a symmetric method.
public class Symetric
{
    public static string Encrypt(string plainText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
    {
        byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
        byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

        PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations);

        byte[] keyBytes = password.GetBytes(keySize / 8);

        RijndaelManaged symmetricKey = new RijndaelManaged();
        ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
        MemoryStream memoryStream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);

        cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
        cryptoStream.FlushFinalBlock();

        byte[] cipherTextBytes = memoryStream.ToArray();

        memoryStream.Close();
        memoryStream.Dispose();
        cryptoStream.Close();
        cryptoStream.Dispose();
        string cipherText = Convert.ToBase64String(cipherTextBytes);

        return cipherText;
    }
    public static string Decrypt(string cipherText, string passPhrase, string saltValue, string hashAlgorithm, int passwordIterations, string initVector, int keySize)
    {
        byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
        byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
        byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

        PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, passwordIterations);

        byte[] keyBytes = password.GetBytes(keySize / 8);

        RijndaelManaged symmetricKey = new RijndaelManaged();
        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
        MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
        CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

        byte[] plainTextBytes = new byte[cipherTextBytes.Length];
        int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);

        memoryStream.Close();
        memoryStream.Dispose();
        cryptoStream.Close();
        cryptoStream.Dispose();
        string plainText = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);

        return plainText;
    }
}

Open in new window

You will want to invoke some cryptographic measures as others have described. Specifically you should be looking at a cryptographic hash algorithm such as SHA-256. You can find an article about both cryptographic hash functions and specifically SHA-256 on Wikipedia.

One thing you will want to make sure to prevent against is dictionary attacks, you can do this by creating a unique salt value for each password. Make sure that the salts are generated using a cryptographically secure random number generator.

http://en.wikipedia.org/wiki/Cryptographic_hash_function
http://en.wikipedia.org/wiki/Sha256#SHA-2_family
http://en.wikipedia.org/wiki/Cryptographic_salt
http://en.wikipedia.org/wiki/CSPRNG

If you are paranoid as well, you can resist against brute force attacks and length extension attacks by using HMAC on top of SHA-256.

http://en.wikipedia.org/wiki/HMAC

Enjoy.
Avatar of Gordon Saxby

ASKER

After reading more about this, it seems that the best solution is the one-way hash. If the user forgets their password, issue them a new one.

Mahone7 - your solution looks very simple to implement, but I can't quite figure out (yet ... still looking!) whether there is "salting" involved!

urir10 - the example on the page you linked to looks good as well, although it is built to handle multiple options which I wouldn't need. It seems as though you do not need to store the salt value as the routine "VerifyHash" works out what the salt value was ... am I reading that right?
the first solution dose not use salt in the implementation " this is simple one way hashing" and its enough for small to mid size web application you don't need to  Add Salt to your plain text password but any way check this for more info : http://www.obviex.com/samples/hash.aspx you may need use salt

this is modified version that use salt

public static String HashPassword(String Pass,String Salt)
    {
        System.Security.Cryptography.SHA256Cng sha = new System.Security.Cryptography.SHA256Cng();
        UnicodeEncoding uniEnc = new UnicodeEncoding();
        Byte[] hashedPassword = sha.ComputeHash(uniEnc.GetBytes(Pass) + uniEnc.GetBytes(Salt));
        String password = "";
        foreach (Byte item in hashedPassword)
        {
            password += String.Format("{0:x2}", item);

        }
        //End of Computation
        return password;
    }
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of urir10
urir10

Im not exactly sure 100% what VerifyHash does, but there is no way to decrypt the hashed value. The salt is only an additional security, you can go fine without it.
urir10 first gave the link to a useable solution. Mahone7 provided another solution which I used in combination to produce my required solution.