Monday, 18 March 2013

Password Salt and Hash Cipher values in C#



public static class SecurityManager
    {
        #region "-- Private --"

        #region "-- Variables --"

        //-- The following constants may be changed without breaking existing hashes.
        public const int SALT_BYTES = 24;
        public const int HASH_BYTES = 24;
        public const int PBKDF2_ITERATIONS = 1000;

        public const int ITERATION_INDEX = 0;
        public const int SALT_INDEX = 1;
        public const int PBKDF2_INDEX = 2;

        #endregion "-- Variables --"

        #region "-- Methods --"
       
        private static string GeByteArrayToString(byte[] inputArray)
        {
            StringBuilder RetVal = new StringBuilder("");

            for (int i = 0; i < inputArray.Length; i++)
            {
                RetVal.Append(inputArray[i].ToString("X2"));
            }

            return RetVal.ToString();
        }

        /// <summary>
        /// Computes the PBKDF2-SHA1 hash of a password.
        /// </summary>
        /// <param name="password">The password to hash.</param>
        /// <param name="salt">The salt.</param>
        /// <param name="iterations">The PBKDF2 iteration count.</param>
        /// <param name="outputBytes">The length of the hash to generate, in bytes.</param>
        /// <returns>A hash of the password.</returns>
        private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)
        {
            Rfc2898DeriveBytes Pbkdf2 = new Rfc2898DeriveBytes(password, salt);
            Pbkdf2.IterationCount = iterations;
            return Pbkdf2.GetBytes(outputBytes);
        }

        #endregion "-- Methods --"

        #endregion "-- Private --"

        #region "-- public --"

        #region "-- Methods --"

        /// <summary>
        /// Sets authentication cookie using form authentication ticket
        /// </summary>
        /// <param name="userNid"></param>
        /// <param name="role"></param>
        public static void SetAuthentication(string userNid, string role)
        {
            FormsAuthenticationTicket AuthTicket = new FormsAuthenticationTicket(
              1,
              userNid,  //user id
              DateTime.Now,
              DateTime.Now.AddMinutes(200),  // expiry
              false,  //do not remember
              role,
              "/");
            HttpCookie Cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
                                               FormsAuthentication.Encrypt(AuthTicket));
            HttpContext.Current.Response.Cookies.Add(Cookie);
        }

        /// <summary>
        /// Returns salt for cryptography
        /// </summary>
        /// <returns></returns>
        public static string GetSalt()
        {
            string RetVal = string.Empty;
            RNGCryptoServiceProvider Csprng = new RNGCryptoServiceProvider();
            byte[] Salt = new byte[SALT_BYTES];
            Csprng.GetBytes(Salt);

            return Convert.ToBase64String(Salt);
        }

        /// <summary>
        /// Returns temp password
        /// </summary>
        /// <returns></returns>
        public static string GetTempPassword()
        {
            return System.Web.Security.Membership.GeneratePassword(8, 3).Replace(".", "-").Replace(@"\", "#");
        }

        /// <summary>
        /// Returns salted hash value
        /// </summary>
        /// <param name="plainPassword"></param>
        /// <param name="salt"></param>
        /// <returns></returns>
        public static string GetSaltedHashValue(string plainPassword, string salt)
        {
            string RetVal = string.Empty;
            byte[] SaltArr = new byte[SALT_BYTES];           

            // Hash the password and encode the parameters

            if (!string.IsNullOrEmpty(plainPassword) && !string.IsNullOrEmpty(salt))
            {
                SaltArr = Convert.FromBase64String(salt);

                byte[] Hash = PBKDF2(plainPassword, SaltArr, PBKDF2_ITERATIONS, HASH_BYTES);

                RetVal = PBKDF2_ITERATIONS + ":" + salt + ":" + Convert.ToBase64String(Hash);
            }

            return RetVal;
        }
       
        /// <summary>
        /// Validates a password/text given a hash of the correct one.
        /// </summary>
        /// <param name="passwordOrText">The password/text to check.</param>
        /// <param name="hashValue">A hash of the correct password/text.</param>
        /// <param name="salt"></param>
        /// <returns>True if the password is correct. False otherwise.</returns>
        public static bool ValidatePasswordOrText(string passwordOrText, string hashValue, string salt)
        {
            bool RetVal = false;
            if (string.IsNullOrEmpty(passwordOrText) || string.IsNullOrEmpty(hashValue) || string.IsNullOrEmpty(salt))
            {
                RetVal = false;
            }
            // compare hash value
            else if (String.Equals(GetSaltedHashValue(passwordOrText, salt), hashValue))
            {
                RetVal = true;
            }
            return RetVal;
        }

        #endregion "-- Methods --"

        #endregion "-- public --"
    }

Data Annotations Remote Validation with AdditionalFields in Action Method : MVC 4



Model:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Web.Mvc;

public class ChangePasswordViewModel
    {
        private int _UserNid = -1;
        [ReadOnly(true)]
        public int UserNid
        {
            get { return this._UserNid; }
            set { this._UserNid = value; }
        }

        private string _OldPassword = string.Empty;       
        [DisplayName("Old Password :")]
        [DataType(DataType.Password)]
        [StringLength(20, ErrorMessage="Must be between 8 and 20 characters", MinimumLength=8)]
        [Required(ErrorMessage="Please enter Old Password")]
        [Remote("ValidateOldPassword", "Account", ErrorMessage="Old password doesn't match")]
        public string OldPassword
        {
            get { return this._OldPassword; }
            set { this._OldPassword = value; }
        }

        private string _NewPassword = string.Empty;
        [DisplayName("New Password :")]
        [DataType(DataType.Password)]
        [StringLength(20, ErrorMessage="Must be between 8 and 20 characters", MinimumLength=8)]
        [Required(ErrorMessage="Please enter New Password")]
        public string NewPassword
        {
            get { return this._NewPassword; }
            set { this._NewPassword = value; }
        }

        private string _ConfirmPassword = string.Empty;
        [DisplayName("Confirm Password :")]
        [DataType(DataType.Password)]
        [StringLength(20, ErrorMessage="Must be between 8 and 20 characters", MinimumLength=8)]
        [Required(ErrorMessage="Please enter Confirm Password")]
        [CompareAttribute("NewPassword", ErrorMessage="The New Password and Confirm New Password fields did not match")]
        public string ConfirmPassword
        {
            get { return this._ConfirmPassword; }
            set { this._ConfirmPassword = value; }
        }
    }


Controller:

public class AccountController : Controller
    {
public JsonResult ValidateOldPassword(string oldPassword)
        {
            bool RetVal = false;
            int UserNid = -1;           
           
            UserNid = Convert.ToInt32(HttpContext.User.Identity.Name);            

            //-- Validate user from db
            RetVal = this._UserRepository.IsPasswordValidated(UserNid, oldPassword);           

            return Json(RetVal, JsonRequestBehavior.AllowGet);
        }
    }



View:

@using GMISRwanda.ViewModel.Account

@model ChangePasswordViewModel

@{
    ViewBag.Title = "Change Password";
    Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
}

@using (Html.BeginForm("ChangePassword", "Account", FormMethod.Post))
{
    <h2>ChangePassword</h2>
   
    @Html.HiddenFor(m => m.UserNid)

    <div>
        @Html.LabelFor(m => m.OldPassword)       
        @Html.PasswordFor(m => m.OldPassword)
        @Html.ValidationMessageFor(m => m.OldPassword)
    </div>
   
    <div>
        @Html.LabelFor(m => m.NewPassword)
        @Html.PasswordFor(m => m.NewPassword)
        @Html.ValidationMessageFor(m => m.NewPassword)
    </div>
   
    <div>
        @Html.LabelFor(m => m.ConfirmPassword)
        @Html.PasswordFor(m => m.ConfirmPassword)
        @Html.ValidationMessageFor(m => m.ConfirmPassword)
    </div>
   
    <div>
        <input type="submit" name="Ok" value="OK" />
        <input type="reset" name="Reset" value="Reset" />
        <input type="button" name="Cancel" value="Cancel" onclick="location.href='@Url.Action("UserDetails", "Admin")'" />
    </div>
}