Hi there, Im having some trouble implementing a secure login facility to a website im creating. I have managed to be able to register new members onto the database with hash values.


However i cannot seem to logon using the same credentials. I believe it's a problem with the SQL reader not reading the table correctly. I did origianlly have a SQL Stored Procedure setup.


Code Below (Login.aspx.cs);

Code:
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Security.Cryptography;
using System.Data.SqlClient;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    private static string CreateSalt(int size)
    {
        // Generate a cryptographic random number using the cryptographic
        // service provider
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        byte[] buff = new byte[size];
        rng.GetBytes(buff);
        // Return a Base64 string representation of the random number
        return Convert.ToBase64String(buff);
    }

    private static string CreatePasswordHash(string pwd, string salt)
    {
        string saltAndPwd = String.Concat(pwd, salt);
        string hashedPwd =
              FormsAuthentication.HashPasswordForStoringInConfigFile(
                                                   saltAndPwd, "SHA1");
        hashedPwd = String.Concat(hashedPwd, salt);
        return hashedPwd;
    }


    protected void btnRegister_Click(object sender, EventArgs e)
    {
        int saltSize = 5;
        string salt = CreateSalt(saltSize);
        string passwordHash = CreatePasswordHash(txtPassword.Text, salt);
        try
        {
            StoreAccountDetails(txtUserName.Text, passwordHash);
        }
        catch (Exception ex)
        {
            lblMessage.Text = ex.Message;
        }
    }

    private void StoreAccountDetails(string userName, string passwordHash)
{
  // See "How To Use DPAPI (Machine Store) from ASP.NET" for information 
  // about securely storing connection strings.
    ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["ConnectionString"];
        
    SqlConnection conn = new SqlConnection(settings.ConnectionString);

    SqlCommand cmd = new SqlCommand("RegisterUser",conn);
    cmd.CommandType = CommandType.StoredProcedure;

  SqlParameter sqlParam = null;
  //Usage of Sql parameters also helps avoid SQL Injection attacks.
  sqlParam = cmd.Parameters.Add("@userName", SqlDbType.VarChar,
    20);
  sqlParam.Value = userName;

  sqlParam = cmd.Parameters.Add("@passwordHash ", SqlDbType.VarChar,
    40);
  sqlParam.Value = passwordHash;

  try
  {
    conn.Open();
    cmd.ExecuteNonQuery();
  }
  catch( Exception ex )
  {
    // Code to check for primary key violation (duplicate account name)
    // or other database errors omitted for clarity
    throw new Exception("Exception adding account. " + ex.Message);
  }
  finally
  {
    conn.Close();
  } 
}

    private bool VerifyPassword(string suppliedUserName, string suppliedPassword)
{ 
  bool passwordMatch = false;
  // Get the salt and pwd from the database based on the user name.
  // See "How To: Use DPAPI (Machine Store) from ASP.NET," "How To:
  // Use DPAPI (User Store) from Enterprise Services," and "How To:
  // Create a DPAPI Library" for more information about how to use
  // DPAPI to securely store connection strings.
  ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["ConnectionString"];
  SqlConnection conn = new SqlConnection(settings.ConnectionString);
  SqlCommand cmd = new SqlCommand("SELECT PasswordHash FROM Users WHERE UserName = @userName", conn);
  //Usage of Sql parameters also helps avoid SQL Injection attacks.
  cmd.Parameters.Add("@userName", SqlDbType.VarChar, 20);
  cmd.Parameters["@userName"].Value = suppliedUserName;
  try
  {
    conn.Open();
    SqlDataReader reader = cmd.ExecuteReader();
      reader.Read(); // Advance to the one and only row
    // Return output parameters from returned data stream
    string dbPasswordHash = reader.GetString(0);
    int saltSize = 5;
    string salt = dbPasswordHash.Substring(dbPasswordHash.Length - saltSize);
    reader.Close();
    // Now take the password supplied by the user
    // and generate the hash.
    string hashedPasswordAndSalt = CreatePasswordHash(suppliedPassword, salt);
    // Now verify them.
    passwordMatch = hashedPasswordAndSalt.Equals(dbPasswordHash);
  }
  catch (Exception ex)
  {
    throw new Exception("Execption verifying password. " + ex.Message);
  }
  finally
  {
    conn.Close();
  }
  return passwordMatch;
}


    protected void btnLogon_Click(object sender, EventArgs e)
    {
        bool passwordVerified = false;
        try
        {
            passwordVerified = VerifyPassword(txtUserName.Text, txtPassword.Text);
        }
        catch (Exception ex)
        {
            lblMessage.Text = ex.Message;
            return;
        }
        if (passwordVerified == true)
        {
            // The user is authenticated
            // At this point, an authentication ticket is normally created
            // This can subsequently be used to generate a GenericPrincipal
            // object for .NET authorization purposes
            // For details, see "How To: Use Forms authentication with 
            // GenericPrincipal objects
            lblMessage.Text = "Logon successful: User is authenticated";
        }
        else
        {
            lblMessage.Text = "Invalid username or password";
        }
    }
}