0

I'm working on a REST Api using .NET Core on an existing Database that uses .Net Framework Identity authentication.

I have an aspnet_Users table that contains the username and an aspnet_Membership table that contains passwordHash and passwordSalt.

My idea was to verify the password by using the salt to generate the hash and compare to the provided string password on the login request. But...

The passwordHash and passwordSalt on the DB are stored as strings.

My question is: how do I use a string salt to generate the hash. I'm used to byte[] and not string...

2
  • at the end, every string is a sequence of bytes.... Commented May 11, 2018 at 19:51
  • aspnet_Users and aspnet_Membership tables are from ASP.NET Membership Provider. There is no backward comparability between ASP.NET Identity and ASP.NET Membership Provider. In addition, password hash algorithm of each version of ASP.NET Membership provider is slightly different. Commented May 11, 2018 at 20:05

3 Answers 3

2

Thanks to: https://stackoverflow.com/posts/2138588

static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt)
{
  HashAlgorithm algorithm = new SHA256Managed();

  byte[] plainTextWithSaltBytes = 
    new byte[plainText.Length + salt.Length];

  for (int i = 0; i < plainText.Length; i++)
  {
    plainTextWithSaltBytes[i] = plainText[i];
  }
  for (int i = 0; i < salt.Length; i++)
  {
    plainTextWithSaltBytes[plainText.Length + i] = salt[i];
  }

  return algorithm.ComputeHash(plainTextWithSaltBytes);            
}

You can convert text to byte arrays using Encoding.UTF8.GetBytes(string). If you must convert a hash to its string representation you can use Convert.ToBase64String and Convert.FromBase64String to convert it back.

You should note that you cannot use the equality operator on byte arrays, it checks references and so you should simply loop through both arrays checking each byte thus

public static bool CompareByteArrays(byte[] array1, byte[] array2)
{
  if (array1.Length != array2.Length)
  {
    return false;
  }

  for (int i = 0; i < array1.Length; i++)
  {
    if (array1[i] != array2[i])
    {
      return false;
    }
  }

  return true;
}
Sign up to request clarification or add additional context in comments.

3 Comments

GenerateSaltedHash can be simplified to return algorithm.ComputeHash(plainText.Concat(salt).ToArray())
CompareByteArrays can be simplified to return array1.SequenceEqual(array2)
Missing using or Dispose for SHA256Managed
1

Well you didn't say what kind of hashing you want ;) So here's one I always use for my projects. Its written by my ex-staff engineer.

I reviewed this chunk of code before. You can try debugging to see if the mutex object is really needed. I doubt so but its always safer to provide you the original code so you can play with it.

public class AES256Hash
    {
        private static byte[] key;
        private static Object mutex = new Object();

        private const int KEY_SIZE = 256;
        private const int BLOCK_SIZE = 128;
        private const int IV_KEY_SIZE = 16; // 16 byte

        private AES256Hash()
        {
        }

        /// <summary>
        /// Create a new randomized cipher on each startup
        /// </summary>
        private static void InitializeCipherKey()
        {
            lock (mutex)
            {
                if (key == null)
                {
                    key = new byte[32];

                    var cr = new System.Security.Cryptography.RNGCryptoServiceProvider();
                    cr.GetBytes(key, 0, key.Length);
                }
            }
        }

        /// <summary>
        /// Converts the input data into an IV key
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        private static byte[] CreateIvKey(string Data)
        {
            byte[] IvKey = new UTF8Encoding().GetBytes(Data);
            if (IvKey.Length != IV_KEY_SIZE)
            {
                byte[] NewTruncatedIvKey = new byte[IV_KEY_SIZE];
                Buffer.BlockCopy(IvKey, 0, NewTruncatedIvKey, 0, Math.Min(IV_KEY_SIZE, IvKey.Length)); // the rest of the bytes are 0 padded

                return NewTruncatedIvKey;
            }
            return IvKey;
        }

        /// <summary>
        /// Encrypts a string with AES256 with the given key and string data
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Data"></param>
        /// <returns></returns>
        public static string EncryptString(string Key, string Data)
        {
            InitializeCipherKey();

            byte[] IvKey = CreateIvKey(Key);
            byte[] dataB = new UTF8Encoding().GetBytes(Data);

            using (AesCryptoServiceProvider csp = new AesCryptoServiceProvider())
            {
                csp.Padding = PaddingMode.PKCS7;
                csp.Mode = CipherMode.ECB;

                csp.KeySize = KEY_SIZE;
                csp.BlockSize = BLOCK_SIZE;
                csp.Key = key;
                csp.IV = IvKey;
                ICryptoTransform encrypter = csp.CreateEncryptor();

                return Convert.ToBase64String(encrypter.TransformFinalBlock(dataB, 0, dataB.Length));
            }
        }

        /// <summary>
        /// Decrypts a string with AES256 with the given key and string data
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Data"></param>
        /// <returns></returns>
        public static string DecryptString(string Key, string Data)
        {
            InitializeCipherKey();

            byte[] IvKey = CreateIvKey(Key);
            byte[] dataB = Convert.FromBase64String(Data);

            using (AesCryptoServiceProvider csp = new AesCryptoServiceProvider())
            {
                csp.Padding = PaddingMode.PKCS7;
                csp.Mode = CipherMode.ECB;

                csp.KeySize = KEY_SIZE;
                csp.BlockSize = BLOCK_SIZE;
                csp.Key = key;
                csp.IV = IvKey;
                ICryptoTransform decrypter = csp.CreateDecryptor();

                return new UTF8Encoding().GetString( decrypter.TransformFinalBlock(dataB, 0, dataB.Length) );
            }
        }
    }

Comments

1

To answer your question, the easiest way to convert a string to a byte[] is to use System.Text.Encoding.UTF8.GetBytes().

That being said, you should investigate whether you want to be rolling your own password validating code, since the common sentiment is that you should avoid it. There a libraries out there that can do this for you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.