//
// 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
}
}