Click here to Skip to main content
15,881,715 members
Articles / Web Development / IIS

ViewState Serializer, Compressor & Encrypter

Rate me:
Please Sign up or sign in to vote.
2.75/5 (9 votes)
26 Sep 2009CPOL4 min read 35.1K   554   27  
It's a very complete and robust processor of ViewState, it allows: to select the way of serialization, compression and encryption optionally.
//
//                       Serialization   Deserialization Compression Amount of Data to use   Security    Indicated to:
//ViewState normal:      Good            Bad (binary)    None        Use low Data            *Ninguna    Forms with low controls, Grids with paging
//Serializer normal:     Good            Bad (binary)    Good        Mid proposes            Moderate    Grids with Viewstate turned On Without paging
//Serializer optimized:  Regular         Regular         Regular     Grand Data (DataTable)  Moderade    ViewState with DataTables & Grids with paging or without the Viewstate turned off
//
// V1.1 Compatible with ajax toolkit

using System;
using System.Data;
using System.Web;
using System.IO;
using System.Security.Cryptography;
using System.Collections;
using System.Runtime.Serialization;
using System.Web.UI;

namespace ViewStateSerializer
{
    public class ViewStateSerializer : System.Web.UI.Page
    {
        //2007-2008 ModMa Technoligies

        const string SessionKey = "SerialCryptKey";
        SecurityLevel encriptar = SecurityLevel.None;
        bool optimizar = false;
        System.Web.UI.Page pagina;
        readonly string pHash = getPageHash();

        #region "Class Inits"

        public ViewStateSerializer()
            : base()
        {
            this.Init += initFN;
            initFN(null, null);
        }

        public ViewStateSerializer(SecurityLevel EnCrypt, bool Optimize)
        {
            SetViewStateValues(EnCrypt, Optimize);
            initFN(null, null);
        }

        private void initFN(object sender, EventArgs e)
        {
            try
            {
                pagina = (System.Web.UI.Page)HttpContext.Current.Handler;
            }
            catch
            {
                //the object is not created, ignore it; desinger mode?
            }
        }

        public void SetViewStateValues(SecurityLevel EnCrypt, bool Optimize)
        {
            if ((pagina == null) || (!pagina.IsPostBack))
            {
                // only can set on Get or New :D
                encriptar = EnCrypt;
                optimizar = Optimize;
            }
        }

        #endregion

        #region "Overrides Page: Compression / ViewState Cryptography"

        protected override object LoadPageStateFromPersistenceMedium()
        {
            try
            {
                return this.DeSerializeMedium(this);
            }
            catch (Exception ex)
            {
                //if fails, invoke the base method
                try
                {
                    return base.LoadPageStateFromPersistenceMedium();
                }
                //if fails too, returns the principal exception
                catch                
                {
                    throw ex;
                }
            }
        }

        protected override void SavePageStateToPersistenceMedium(object viewState)
        {
            this.SerializeMedium(this, viewState);
        }

        #endregion

        #region "Functions to Init the Serialization / Deserialization"

        public string Serialize(object viewState)
        {
            if (viewState == null)
            {
                throw new ArgumentNullException("viewState is Nothing", "Serialize params Error");
            }

            //Saves the Current Page Configuration
            Hashtable cfg = getCryptCfg();
            cfg["optSlzEncrypt_" + pHash] = encriptar;
            cfg["optSlzOptimize_" + pHash] = optimizar;

            byte[] bytes;
            if (optimizar)
            {
                //optimize for data option
                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
                    new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                MemoryStream writer = new MemoryStream();
                object transform = cc(viewState);
                formatter.Serialize(writer, transform);
                writer.Position = 0;
                bytes = writer.ToArray();
            }
            else
            {
                //classic mode
                LosFormatter formatter = new LosFormatter();
                StringWriter writer = new StringWriter();
                formatter.Serialize(writer, viewState);
                bytes = Convert.FromBase64String(writer.ToString());
            }

            const string errorCompression = "ViewState Compression Error";
            try
            {
                bytes = GZipCompress(bytes);
                //proceso compresion
            }
            catch (Exception ex)
            {
                throw new Exception(errorCompression + ": " + ex.Message, ex);
            }

            if (!encriptar.Equals(SecurityLevel.None))
            {
                bytes = Cryptos(bytes, this.GetKeys, CryptosMode.EnCrypt);
            }
            return Convert.ToBase64String(bytes);
        }

        public void SerializeMedium(System.Web.UI.Page thePage, Object viewState)
        {
            Type myHack = typeof(System.Web.UI.Page);
            //System.Reflection.FieldInfo instx = myHack.GetField("_clientState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
            System.Reflection.PropertyInfo inst = myHack.GetProperty("ClientState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

            String vState = this.Serialize(viewState);
            inst.SetValue(thePage, vState, null);
        }

        public object DeSerialize(string viewState)
        {
            if (viewState == null || viewState.Equals(string.Empty))
            {
                throw new ArgumentNullException("viewState is Nothing or Empty", "DeSerialize params Error");
            }

            //Loads the Current Page Configuration
            Hashtable cfg = getCryptCfg();
            if (cfg["optSlzEncrypt_" + pHash] != null)
            {
                encriptar = (SecurityLevel)cfg["optSlzEncrypt_" + pHash];
            }
            if (cfg["optSlzOptimize_" + pHash] != null)
            {
                optimizar = (bool)cfg["optSlzOptimize_" + pHash];
            }

            byte[] bytes = Convert.FromBase64String(viewState);
            if (!encriptar.Equals(SecurityLevel.None))
            {
                bytes = Cryptos(bytes, this.GetKeys, CryptosMode.DeCrypt);
            }

            try
            {
                bytes = GZipDecompress(bytes);
            }
            catch (Exception ex)
            {
                throw new Exception("ViewState Data Error: " + ex.Message, ex);
            }

            if (optimizar)
            {
                //optimize for data option
                System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
                    new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                MemoryStream writer = new MemoryStream(bytes);
                writer.Position = 0;
                object transform = formatter.Deserialize(writer);
                return cc2(transform);
            }
            else
            {
                //classic mode
                LosFormatter formatter = new LosFormatter();
                return formatter.Deserialize(Convert.ToBase64String(bytes));
            }
        }

        public Object DeSerializeMedium(Page thePage)
        {
            String requestViewStateString = String.Empty;
            System.Collections.Specialized.NameValueCollection requestValueCollection = null;

            Type myHack = typeof(System.Web.UI.Page);
            System.Reflection.PropertyInfo inst1 = myHack.GetProperty("RequestViewStateString", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
            System.Reflection.PropertyInfo inst2 = myHack.GetProperty("RequestValueCollection", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

            try
            {
                requestValueCollection = (System.Collections.Specialized.NameValueCollection)inst2.GetValue(thePage, null);
                requestViewStateString = (string)inst1.GetValue(thePage, null);

                if (requestValueCollection != null)
                {
                    if (!string.IsNullOrEmpty(requestViewStateString))
                    {
                        return this.DeSerialize(requestViewStateString);
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex.InnerException is ViewStateException)
                {
                    throw ex;
                }
                throw new Exception(requestViewStateString, ex);
            }
            return null;
        }

        #endregion

        #region "Functions to manage the compression (only Framework 2.0)"

        //based in a code of Dario Solera: http://www.codeproject.com/aspnet/ViewStateCompression.asp
        public byte[] GZipCompress(byte[] data)
        {
            MemoryStream output = new MemoryStream();
            System.IO.Compression.GZipStream gzip = new System.IO.Compression.GZipStream(output, System.IO.Compression.CompressionMode.Compress, true);
            gzip.Write(data, 0, data.Length);
            gzip.Close();
            return output.ToArray();
        }

        //based in a code of Dario Solera: http://www.codeproject.com/aspnet/ViewStateCompression.asp
        public byte[] GZipDecompress(byte[] data)
        {
            MemoryStream input = new MemoryStream(data);
            input.Position = 0;
            System.IO.Compression.GZipStream gzip = new System.IO.Compression.GZipStream(input, System.IO.Compression.CompressionMode.Decompress, true);
            MemoryStream output = new MemoryStream();
            byte[] buff = new byte[4096];
            int read = -1;

            read = gzip.Read(buff, 0, buff.Length);
            while (read > 0)
            {
                output.Write(buff, 0, read);
                read = gzip.Read(buff, 0, buff.Length);
            }
            gzip.Close();
            return output.ToArray();
        }

        //based in a code of Dario Solera: http://www.codeproject.com/aspnet/HttpCompressionQnD.asp
        public void CompressPage(System.Web.UI.Page pag)
        { //compress the page
            string Accept_encoding = pag.Request.Headers["Accept-encoding"];
            if (Accept_encoding == null) Accept_encoding = string.Empty;

            if (pag.Request.UserAgent.ToLower().IndexOf("konqueror") == -1)
            {
                if (Accept_encoding.IndexOf("gzip") != -1)
                {
                    pag.Response.Filter =
                        new System.IO.Compression.GZipStream(pag.Response.Filter,
                        System.IO.Compression.CompressionMode.Compress, true);
                    pag.Response.AppendHeader("Content-encoding", "gzip");
                }
            }
            else
            {
                if (Accept_encoding.IndexOf("deflate") != -1)
                {
                    pag.Response.Filter =
                        new System.IO.Compression.DeflateStream(pag.Response.Filter,
                        System.IO.Compression.CompressionMode.Compress, true);
                    pag.Response.AppendHeader("Content-encoding", "deflate");
                }
            }
        }

        #endregion

        #region "Functions to manage the Cryptography"

        public static byte[] Cryptos(byte[] data, Hashtable keys, CryptosMode modo)
        {
            if ((keys == null) || (keys["Key"] == null) || (keys["IV"] == null) || (data == null))
            {
                throw new ArgumentNullException("data or keys is Nothing", "Cryptos params Error");
            }

            MemoryStream output = new MemoryStream();
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            CryptoStream cs = new CryptoStream(output, (modo == CryptosMode.EnCrypt ?
                des.CreateEncryptor((byte[])keys["Key"], (byte[])keys["IV"]) :
                des.CreateDecryptor((byte[])keys["Key"], (byte[])keys["IV"])), CryptoStreamMode.Write);
            cs.Write(data, 0, data.Length);
            cs.FlushFinalBlock();
            cs.Close();
            return output.ToArray();
        }

        public enum CryptosMode
        {
            DeCrypt = 0,
            EnCrypt = 1
        }

        public enum SecurityLevel
        {
            None = 0,
            Low = 1,
            High = 2
        }

        //gets the Cryptography config (all pages get this)
        public Hashtable GetKeys
        {
            get
            {
                Hashtable Config = getCryptCfg();
                //go to create the keys
                byte[] key;
                byte[] IV;
                if (Config["Key1"] == null || Config["IV1"] == null)
                {   // bost security (more aleatory)
                    RNGCryptoServiceProvider randObj = new RNGCryptoServiceProvider();
                    IV = new byte[8];
                    randObj.GetNonZeroBytes(IV);
                    key = BitConverter.GetBytes(System.DateTime.Now.Ticks);
                    //END arrays generation
                    Config.Add("Key1", key); // save the key
                    Config.Add("IV1", IV);// save initial value
                }
                if (Config["Key2"] == null || Config["IV2"] == null)
                {   // moderate security (starting days + actual day)
                    MD5 hash = MD5.Create();
                    System.Text.ASCIIEncoding encoder = new System.Text.ASCIIEncoding();
                    byte[] tValue;
                    //START IV: server starting time XOR server host name
                    TimeSpan ts = new TimeSpan(0, 0, 0, 0, System.Environment.TickCount); //you can use this, or aplication start time...
                    ts = new TimeSpan(ts.Days, ((int)(ts.TotalDays * 10) % 10 >= 5) ? 12 : 0, 0, 0); // days /2
                    tValue = hash.ComputeHash(encoder.GetBytes(HttpContext.Current.Server.MachineName.ToLower()));
                    IV = BitConverter.GetBytes(ts.Ticks);
                    IV[0] = (byte)(IV[0] ^ tValue[0] ^ tValue[8]);
                    IV[1] = (byte)(IV[1] ^ tValue[1] ^ tValue[9]);
                    IV[2] = (byte)(IV[2] ^ tValue[2] ^ tValue[10]);
                    IV[3] = (byte)(IV[3] ^ tValue[3] ^ tValue[11]);
                    IV[4] = (byte)(IV[4] ^ tValue[4] ^ tValue[12]);
                    IV[5] = (byte)(IV[5] ^ tValue[5] ^ tValue[13]);
                    IV[6] = (byte)(IV[6] ^ tValue[6] ^ tValue[14]);
                    IV[7] = (byte)(IV[7] ^ tValue[7] ^ tValue[15]);
                    //START key: actual day XOR this assembly physical location
                    key = BitConverter.GetBytes(new DateTime( // only actual day
                       DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day).Ticks);
                    tValue = hash.ComputeHash(encoder.GetBytes(
                       System.Reflection.Assembly.GetExecutingAssembly().CodeBase.ToLower())); //assembly physical location
                    key[0] = (byte)(key[0] ^ tValue[0] ^ tValue[8]);
                    key[1] = (byte)(key[1] ^ tValue[1] ^ tValue[9]);
                    key[2] = (byte)(key[2] ^ tValue[2] ^ tValue[10]);
                    key[3] = (byte)(key[3] ^ tValue[3] ^ tValue[11]);
                    key[4] = (byte)(key[4] ^ tValue[4] ^ tValue[12]);
                    key[5] = (byte)(key[5] ^ tValue[5] ^ tValue[13]);
                    key[6] = (byte)(key[6] ^ tValue[6] ^ tValue[14]);
                    key[7] = (byte)(key[7] ^ tValue[7] ^ tValue[15]);
                    //END arrays generation
                    Config.Add("Key2", key); // save the key
                    Config.Add("IV2", IV);// save initial value
                }
                //set the output configuration
                Hashtable Salida = new Hashtable();
                switch (encriptar)
                {
                    case SecurityLevel.None:
                        Salida.Add("Key", null);
                        Salida.Add("IV", null);
                        break;
                    case SecurityLevel.Low:
                        Salida.Add("Key", Config["Key2"]);
                        Salida.Add("IV", Config["IV2"]);
                        break;
                    default:
                        Salida.Add("Key", Config["Key1"]);
                        Salida.Add("IV", Config["IV1"]);
                        break;
                }
                return Salida;
            }
        }

        //bad name: aux function to create the session config
        private Hashtable getCryptCfg()
        {
            Hashtable salida;
            if ((HttpContext.Current.Session[SessionKey] == null) || !(HttpContext.Current.Session[SessionKey] is Hashtable))
            {
                salida = new Hashtable();
                HttpContext.Current.Session.Add(SessionKey, salida);
            }
            else
            {
                salida = (Hashtable)HttpContext.Current.Session[SessionKey];
            }
            return salida;
        }

        //returns the page string MD5 hash
        public static string getPageHash()
        {
            try
            { // compacted to be 100% static
                return Convert.ToBase64String(
                    MD5.Create().ComputeHash((new System.Text.ASCIIEncoding()).
                    GetBytes(HttpContext.Current.Request.FilePath.ToLower())));
            }
            catch
            {
                //the object is not created, ignore it; desinger mode?
                return string.Empty;
            }
        }

        #endregion

        #region "Functions to prepare the binary serialization, replace of Triplet and Pair"

        private object cc(object tipo)
        {
            if (tipo == null) return null;
            if (esConocido(tipo))
            {
                return tipo;
            }
            else if (tipo is System.Web.UI.Triplet)
            {
                System.Web.UI.Triplet triple = (System.Web.UI.Triplet)tipo;
                return new seriable3(cc(triple.First), cc(triple.Second), cc(triple.Third));
            }
            else if (tipo is System.Web.UI.Pair)
            {
                System.Web.UI.Pair par = (System.Web.UI.Pair)tipo;
                return new seriable2(cc(par.First), cc(par.Second));
            }
            else if (tipo is ArrayList)
            {
                ArrayList trans = (ArrayList)tipo;
                ArrayList salida = new ArrayList(trans.Count);
                foreach (object x in trans)
                {
                    salida.Add(cc(x));
                }
                return salida;
            }
            else if (tipo is Array)
            {
                Array trans = (Array)tipo;
                Array salida = Array.CreateInstance(tipo.GetType().GetElementType(), trans.Length);
                for (int x = 0; x < trans.Length; x++)
                {
                    salida.SetValue(cc(trans.GetValue(x)), x);
                }
                return salida;
            }
            else if (tipo is Hashtable)
            {
                IDictionaryEnumerator enumerator = ((Hashtable)tipo).GetEnumerator();
                Hashtable salida = new Hashtable();
                while (enumerator.MoveNext())
                {
                    salida.Add(cc(enumerator.Key), cc(enumerator.Value));
                }
                return salida;
            }
            else
            {
                Type valueType = tipo.GetType();
                Type destinationType = typeof(string);
                bool flag;
                bool flag2;
                System.ComponentModel.TypeConverter converter = System.ComponentModel.TypeDescriptor.GetConverter(valueType);
                if (((converter == null) || converter is System.ComponentModel.ReferenceConverter))
                {
                    flag = false;
                    flag2 = false;
                }
                else
                {
                    flag = converter.CanConvertTo(destinationType);
                    flag2 = converter.CanConvertFrom(destinationType);
                }
                if ((flag && flag2))
                {
                    return new generalCnv(valueType, converter.ConvertToInvariantString(tipo));
                }
                else
                {
                    return tipo;
                    //Salida General
                }
            }
        }

        private object cc2(object tipo)
        {
            if (tipo == null) return null;
            if (tipo is seriable3)
            {
                seriable3 trans = (seriable3)tipo;
                return new System.Web.UI.Triplet(cc2(trans.First), cc2(trans.Second), cc2(trans.Third));
            }
            else if (tipo is seriable2)
            {
                seriable2 trans = (seriable2)tipo;
                return new System.Web.UI.Pair(cc2(trans.First), cc2(trans.Second));
            }
            else if (tipo is ArrayList)
            {
                ArrayList salida = (ArrayList)tipo;
                for (int x = 0; x < salida.Count; x++)
                {
                    salida[x] = cc2(salida[x]);
                }
                return salida;
            }
            else if (tipo is Array)
            {
                Array salida = (Array)tipo;
                for (int x = 0; x < salida.Length; x++)
                {
                    salida.SetValue(cc2(salida.GetValue(x)), x);
                }
                return salida;
            }
            else if (tipo is Hashtable)
            {
                IDictionaryEnumerator enumerator = ((Hashtable)tipo).GetEnumerator();
                Hashtable salida = new Hashtable();
                while (enumerator.MoveNext())
                {
                    salida.Add(cc2(enumerator.Key), cc2(enumerator.Value));
                }
                return salida;
            }
            else if (tipo is generalCnv)
            {
                generalCnv datos = (generalCnv)tipo;
                System.ComponentModel.TypeConverter converter = System.ComponentModel.TypeDescriptor.GetConverter(datos.bTipo);
                return converter.ConvertFromInvariantString(datos.bString);
            }
            else
            {
                return tipo;
                //Salida General
            }
        }

        private static bool esConocido(object elemento)
        {
            if ((elemento.GetType().IsSealed) && !(elemento is ArrayList) && !(elemento is Array) && !(elemento is Hashtable)
                && !(elemento is System.Web.UI.Pair) && !(elemento is System.Web.UI.Triplet))
            {
                return elemento.GetType().IsSerializable;
            }
            return false;
        }

        #endregion

        #region "Aux Objects, replace of Triplet and Pair"

        [Serializable()]
        private class seriable3
        {
            public object First;
            public object Second;
            public object Third;

            public seriable3()
            {
            }
            public seriable3(object i_First, object i_Second, object i_Third)
            {
                First = i_First;
                Second = i_Second;
                Third = i_Third;
            }
        }

        [Serializable()]
        private class seriable2
        {
            public object First;
            public object Second;

            public seriable2()
            {
            }
            public seriable2(object i_First, object i_Second)
            {
                First = i_First;
                Second = i_Second;
            }
        }

        [Serializable()]
        private class generalCnv
        {
            public System.Type bTipo;
            public string bString;

            public generalCnv()
            {
            }
            public generalCnv(System.Type i_bTipo, string i_bString)
            {
                bTipo = i_bTipo;
                bString = i_bString;
            }
        }

        #endregion

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Sermicro
Spain Spain
My life in programming has been long, begins from the 6 years of age with Basic, I have knowledge of C++, Javascript, ASP .NET, Cisco CCNA, among others.

One of my pastimes in the programming, is cryptology and systems security

One of my recognized works is P2PFire, other smaller projects like utilities for Chats

Comments and Discussions