Tuesday, November 5, 2024

ASP.NET: machineKey Generator

Whenever you make use of ViewState, Session, Forms authentication, or other encrypted and/or secured values, ASP.NET uses a set of keys to do the encryption and decryption.

Normally, these keys are hidden and automatically generated by ASP.NET every time your application recycles, but there are times when you want to specify a particular, persistent key. This article will explain why hard-coded machineKeys are good, and how to generate random ones for ASP.NET 1.1 or 2.0.

Why you Want a Persistent Key

There are two keys that ASP.NET uses to encrypt, decrypt, and validate data in ViewState, Forms Authetication tickets, and out-of-process Session data. The decryptionKey is used for encryption and decryption of authentication tickets and encrypted ViewState information. The validationKey is used to validate ViewState and ensure it hasn’t been tampered with, and generate unique application-specific session IDs for out-of-process session storage.

You can run into problems if the key changes between postbacks, e.g., if the keys used to generate the ViewState information are different from one page to the next. If that happens, the ViewState validation will fail (because the validationKey after postback will be different from what was used pre-postback) and the user will get an Invalid_ViewState error. Different keys across postback can also mean that your Forms Authentication tickets will fail, requiring users to log back into your application; out-of-process session information will be lost (since the session IDs will change); and encrypted ViewState information will be unable to be decrypted and read.

Lastly, if you want to use encrypted passwords with ASP.NET 2.0’s Membership provider, you have to provide a static key, or else you’ll get a You must specify a non-autogenerated machine key to store passwords in the encrypted format error.

Why Would the Key Change?

Keys can change across postbacks more often than you’d expect. One way is if you’re running a web farm. By default, the validationKey used to create the MAC is randomly generated by ASP.NET when the application pool starts up. This ensures that the validation key is unique and changes periodically. However, since the key is different from server to server, if you’re viewing a page on Server A and post it to Server B, when Server B generates a MAC based on the viewstate data, that value won’t match the MAC value when the page was initially served by Server A. Thus, you’ll get an Invalid_Viewstate error.

Another reason the validationKey and decryptionKey (and thus the MAC) will be different is if you cross application pools. If you view a page running in pool A, and post to another page on pool B (e.g., through Server.Transfer), the keys will be different & you’ll get a mismatch.

Lastly, the validationKey and decryptionKey can change mid-session for users if the application pool restarts. Assume some user is viewing a page on your site and filling out a form. While they’re doing that, some sysadmin restarts the pool, thus generating a new key. When the user posts the page, they’ll get an Invalid_Viewstate error. The application can also restart if it is set to shut down while idle (the default is to shut down pools that have been idle for 20 minutes). Imagine a user who views a page on your site, goes away for 10 minutes, then maybe spends 20 minutes filling in the form on the page. Meanwhile, no one else is on your site, so the application pool times out & shuts down. When the user finally posts the form (30 minutes after viewing it), the application pool will start back up, create a new validation key, generate a new MAC, notice that the MAC values don’t match, and reward your user’s diligence with an Invalid_Viewstate error.

Where to Put the Key

So if you want to create a static set of keys, you’ll need to put it in the machineKey block in either the machine.config or web.config. You can read more about the machineKey element on MSDN.

The validationKeys are a maximum of 64 bytes long. In ASP.NET 1.1, the encryption algorithm was 3DES along with a maximum 24 byte decryptionKey. ASP.NET 2.0 provided an attribute called decryption allowing the user to specify the hashing algorithm used for decrypting data. The AES algorithm is the best choice, and accommodates a 32 byte decryptionKey.

Please note that the below examples contain […] to indicate that some characters were removed for readability. The actual values of the keys are long, unbroken hex-encoded strings. Don’t copy & paste the below examples verbatim into your

  • web.config
  • — instead, download and run the sample project, or generate random keys via the online demo.

    ASP.NET 1.1 version:

    <machineKey
    validationKey="C3BB96E9C96[...]3F7ACCB7E7DEA"
    decryptionKey="E5E046B77ED2C[...]C13205DBA8E3ECEC4BDE346"
    validation="SHA1"
    />

    ASP.NET 2.0 version:

    <machineKey
    validationKey="3DC93913A3E7998AE43[...]C519574359262"
    decryptionKey="9470AD8F914387CBE0[...]ABA8A0DB762"
    validation="SHA1" decryption="AES"
    />

    Generating Random Keys

    Now that we’ve discussed the keys in the machineKey section, let’s take a quick peek at some code to randomly generate keys. It’s pretty simple.

    The below function accepts a number of bytes, uses the .NET Crypto library to generate a byte array of random numbers, and StringBuilder to build and return a hex-encoded string. Since the random numbers are hex-encoded, a 24 byte random key will produce a 48 character hex-encoded string.

    public string getRandomKey(int bytelength)
    {
          byte[] buff = new byte[bytelength];
          RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        rng.GetBytes(buff);
          StringBuilder sb = new StringBuilder(bytelength * 2);
          for (int i = 0; i

    Now we have two simple functions that use the getRandomKey function to build and return a complete machineKey section that can be pasted into a web.config.

    public string getASPNET20machinekey()
    {
          StringBuilder aspnet20machinekey = new StringBuilder();
          string key64byte = getRandomKey(64);
          string key32byte = getRandomKey(32);
          aspnet20machinekey.Append("n");
          return aspnet20machinekey.ToString();
    }

    public string getASPNET11machinekey()
    {
          StringBuilder aspnet11machinekey = new StringBuilder();
          string key64byte = getRandomKey(64);
          string key24byte = getRandomKey(24);

          aspnet11machinekey.Append("n");
          return aspnet11machinekey.ToString();
    }

    Now all we have to do is call the getASPNET11machinekey or getASPNET20machinekey functions to get back a random machineKey section for the environment of our choice.

    The Sample Application

    The sample application is an ASP.NET 1.1 Web Project that contains a machineKey.aspx file demonstrating the above functionality. You can also see a live demo here if you want to generate keys yourself or just see how it works.

    Conclusion

    We've discussed the decryptionKey and validationKey, reviewed how they can change and what problems that can cause, and discussed how to generate and deploy static keys to avoid problems. Hopefully that will help you next time you run into ViewState or other related problems.

    Related Reading

    You can read more about machineKey and its impact at:

    Tag:

    Add to Del.icio.us | Digg | Reddit | Furl

    Bookmark Murdok:

    Ben Strackany is a freelance IT Consultant specializing in Microsoft development and web site planning, scalability, and hosting. He is the head of DevelopmentNow, a Portland-based technology consulting company.

    Related Articles

    LEAVE A REPLY

    Please enter your comment!
    Please enter your name here

    Latest Articles