Tuesday, August 30, 2016

C#: How to generate a unique key or password using salting + hashing

1. Introduction


When an application develops, we need to protect user passwords from security attacks. When your application operates on a license key, we need to validate the license.

In today's world providing security to your system is bit crucial. We have to face malicious scripts, security threats and unwanted attacks in out there. Let’s see how we can prevent those kind of attacks.


2. Password Hashing


In hashing, we are going to convert password string into a byte array. Hashing is a one way function, we can’t reverse it. We can see many hashing algorithms available.

Let’s say we hashed the users password and stored in our database. When user second time logs in, we have to verify entered password is correct. What we can do is, we can hash the entered text into password field and compare it with the hashed value from the database. Since Hashing is a one way function, we can’t decrypt and retrieve original text.


2.1. Invalid username or password


When user enters wrong password, System should not let the user know it’s Invalid password , instead of it should tell Invalid username or password . Then if hacker tries to login into your system, he doesn’t know whether username or password got wrong.


2.2. Hash Functions


SHA1, MD5 are popular hashing algorithms. SHA1 got inspired from MD algorithms and created SHA algorithms. From these two algorithms, better to use SHA1, Because it’s high secure than MD5. MD5 is going to convert a string into 128 bits. But SHA1 convert a string into 160 bits. In MD5 less no of operations required to crack the message, when compared to SHA1. But MD is faster than SHA1. However it’s better to use SHA1 algorithm instead of MD5, since MD5 can be broken easily.


2.3. Hashes can be cracked easily


Hackers can guess passwords and simply hash them with a hashing algorithm and try it in your system. It takes few seconds to generate a hash. Within a limited amount of time, your passwords can be cracked easily. These type of guessing password and hashing is called as Dictionary attacks and Brute force attacks.

In dictionary attacks, it uses a file with some words. These words can be extracted from a database or else from set of paragraphs. Most hackers guess passwords from common terms like, hello, hellow, he11o3 etc. They take a string and try to replace letters in it, like hello, he33o, heiio, etc. Then these words are hashed and use against the password hash.

Brute Force attacks going to try out every possible of character combinations to a certain string length. It’s a very expensive computational process, But eventually it’s going to find out your password! That’s why we need lengthier passwords, So it will take long time to crack your passwords.

By hashing your passwords, We can’t prevent Dictionary attacks or Brute force attacks, But we can minimize them.

While Dictionary attacks and Brute force attacks are time consuming, Hackers has another kid in their block, It’s Lookup Table. In Lookup table, It’s going to precompute the hashes of passwords and used to store them in a dictionary file. So hundreds of guesses can be done in a second. It’s a very effective method to crack your passwords.

Rainbow Tables, Beware guys, it can crack any 8 characters lengthy MD5 password. Rainbow table is same as Lookup table. In here hash cracking speed is slower, compared to lookup table. But lookup table size is smaller, so more hashes can be stored in the same space. So Rainbow tables are more effective than Lookup tables.


2.4. Salted Password Hashing


If we use a salt with password hashing, It’s impossible to crack your passwords through Rainbow tables and lookup tables. In lookup tables, hackers are going to hash same list of passwords and try out in your system. Let’s say two users are having same password in your system. When we hash these passwords, It’s same password hash. In Salting, we are adding a random number along with hashed password. So two users never get same salted password hash. And mind you don’t use same salt with different user passwords, don’t repeat the salting. If new user registers into your system or else change password, generate a new salt. Use a new salt for every user password. If hacker is smart enough, He may be able to crack a one or two user passwords, But not more than that. Even though many users have same password, Hacker will not be able to crack their passwords using a lookup table. Don’t use shorter salt, Then hacker can create a lookup table to generate every possible salt.


2.5. Let’s see how we can generate a unique key


























public class AuthenticationValidator
 {
      KeyGeneratorContext context = new KeyGeneratorContext();
      public bool GenerateSubscriptionKey(string userName, int companyCode)
      {
          bool isSuccess = false;
          byte[] salt = GenerateSalt();
          Company company = context.Companies.Where(c => c.Code == companyCode).FirstOrDefault();
          User user = context.Users.Where(u => u.CompanyId == company.Id && u.UserName == userName).FirstOrDefault();
          string[] hashKeys = GenerateHashKey(userName, companyCode).Split(':');
          Rfc2898DeriveBytes value = new Rfc2898DeriveBytes(hashKeys[0] + hashKeys[1], salt);
          byte[] key = value.GetBytes(64);
          user.Subscription = key;
          user.SaltValue = salt;
          isSuccess = context.SaveChanges() > 0;
          return isSuccess;
   }
    private static byte[] GenerateSalt ()
    {
        int saltLength = 32;
        byte[] salt = new byte[saltLength];
        using (var random = new RNGCryptoServiceProvider())
        {
           random.GetNonZeroBytes(salt);
        }
       return salt;
   }
   private string GenerateHashKey(string userName, int companyCode)
   {
      string key = string.Empty;
      Company company = context.Companies.Where(c => c.Code == companyCode).FirstOrDefault();
      User user = context.Users.Where(u => u.CompanyId == company.Id && u.UserName == userName).FirstOrDefault();
      if (company != null && user != null)
         key = company.Name + ":" + user.UserGuid;
      return key;
    }

In this example, we are passing username and company code to generate a license key. Using GenerateSalt method, we can generate a random number. I used to create a 32 bytes length salt number using RNGCryptoServiceProvider class. Using Rfc289DeriveVytes class we can generate a salted hashing value. Along with the generated license key, we used to store salted number as well.


2.6. Let’s see how we can validate the license key



















public bool ValidateSubscriptionKey(string userName, int companyCode)
{
    bool isValid = false;
    byte[] subscription = new byte[64];
    byte[] saltValue = new byte[32];
    Company company = context.Companies.Where(c => c.Code == companyCode).FirstOrDefault();
    if (company != null)
     {
        User user = context.Users.Where(u => u.CompanyId == company.Id && u.UserName == userName).FirstOrDefault();
        if (user != null)
         {
            subscription = user.Subscription;
             saltValue = new byte[32];
          }
        string[] hashKeys = GenerateHashKey(userName, companyCode).Split(':');
        Rfc2898DeriveBytes value = new Rfc2898DeriveBytes(hashKeys[0] + hashKeys[1], saltValue);
        byte[] key = value.GetBytes(64);
        bool result = subscription.SequenceEqual(key);
        if (result && user.ExpiryDate >= DateTime.Now)
          isValid = true;
      }
     return isValid;
   }

I used 64 byte array as a license key and 32 byte array as a salted value. We can use SequenceEqual method to compare entered license key with stored license key as above.


3. Download

3.1. TechNet Gallery




3.2. GitHub




4. Conclusion


This article explains various mechanisms to secure valuable data. If you go through the code sample, It explains how to secure a license key using salted hashing mechanism.


5. References




1 comment:

  1. Hi nice article. Thank you for sharing this information. Please check my website Convert Password to MD5 Online.

    ReplyDelete