Sunday, January 08, 2006

Here is a class that handles symmetric encryption and decryption using the .NET Rijndael provider. This works well when passing data that you need to keep encrypted over a URL. Remember to UrlEncode the resulting Base64 string with Server.UrlEncode() if you plan on passing your encrypted string as part of the Query String. I tried to boil it down to its simplest form.

You can download a sample solution:
http://download.binaryocean.com/SymmetricEncryptionSolution.zip

using System;

using System.IO;

using System.Security.Cryptography;

using System.Text;

 

/// <summary>

/// Simple symmetric encryption using the .NET Rijndael provider

/// </summary>

public class SymmetricEncryption

{

    public SymmetricEncryption()

    {

    }

 

    public SymmetricEncryption(string password)

    {

        GenerateKey(password);

    }

 

    public string Password

    {

        set { GenerateKey(value); }

    }

 

    private byte[] Key;

    private byte[] Vector;

 

    private void GenerateKey(string password)

    {

        SHA384Managed sha = new SHA384Managed();

        byte[] b = sha.ComputeHash(new ASCIIEncoding().GetBytes(password));

 

        Key = new byte[32];

        Vector = new byte[16];

 

        Array.Copy(b, 0, Key, 0, 32);

        Array.Copy(b, 32, Vector, 0, 16);

    }

 

    public string Encrypt(string plainText, string password)

    {

        GenerateKey(password);

        return Encrypt(plainText);

    }

 

    public string Encrypt(string plainText)

    {

        if (Key == null)

        {

            throw new InvalidOperationException("Password must be provided or set.");

        }

 

        byte[] data = new ASCIIEncoding().GetBytes(plainText);

 

        RijndaelManaged crypto = new RijndaelManaged();

        ICryptoTransform encryptor = crypto.CreateEncryptor(Key, Vector);

 

        MemoryStream memoryStream = new MemoryStream();

        CryptoStream crptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);

 

        crptoStream.Write(data, 0, data.Length);

        crptoStream.FlushFinalBlock();

 

        crptoStream.Close();

        memoryStream.Close();

 

        return Convert.ToBase64String(memoryStream.ToArray());

    }

 

    public string Decrypt(string encryptedText, string password)

    {

        GenerateKey(password);

        return Decrypt(encryptedText);

    }

 

    public string Decrypt(string encryptedText)

    {

        if (Key == null)

        {

            throw new InvalidOperationException("Password must be provided or set.");

        }

 

        byte[] cipher = Convert.FromBase64String(encryptedText);

 

        RijndaelManaged crypto = new RijndaelManaged();

        ICryptoTransform encryptor = crypto.CreateDecryptor(Key, Vector);

 

        MemoryStream memoryStream = new MemoryStream(cipher);

        CryptoStream crptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Read);

 

        byte[] data = new byte[cipher.Length];

        int dataLength = crptoStream.Read(data, 0, data.Length);

 

        memoryStream.Close();

        crptoStream.Close();

 

        return (new ASCIIEncoding()).GetString(data, 0, dataLength);

    }

}

 

kick it on DotNetKicks.com   Sunday, January 08, 2006 11:46:32 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Saturday, January 07, 2006

A client of mine was having issues while using an image control within a master page. This master page was reference by content pages at a number of different sub directory levels within the application. The browser was unable to resolve or display images located off of the application root when the ulr contained a tilde (~) at the beginning of the path.

<img src="~/Image/AndrewPix.jpg" />

The tilde (~) is a shortcut to the HttpRuntime.AppDomainAppVirtualPath which will normally resolve back to the root of the virtual directory but this shortcut will only be resolved within server side controls. The standard HTML image control (img) is not rendered at the server and will simply pass the tilde on to the client browser. The issue was related to this and not directly to the use of master pages.

The solution is to insure that the control is rendered at the server. This can be done by adding the runat="server" property to the standard HTML image control or by using the ASP.NET image control.

<img src="~/Image/AndrewPix.jpg" runat="server" />

<asp:Image runat="server" ImageUrl="~/Image/AndrewPix.jpg" />

 

kick it on DotNetKicks.com   Saturday, January 07, 2006 6:40:28 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Wednesday, January 04, 2006

NOTE: I have slightly modified this post and the associated source. Make sure to take a look at v2 after reading this post:
http://blog.binaryocean.com/PermaLink,guid,a1ff6cab-dc2d-441c-8557-7dce920d4075.aspx

-Andrew

>>>

In ASP.NET version 1.x, I used the footer on the DataGrid control to insert new rows by placing a series of TextBox, DropDownLists, CheckBoxes etc. controls on it and then handling a save button. This also works with the GridView in version 2.0 but with one major exception: if your data source is empty, the GridView will hide your footer (and header) and only display the EmptyDataText.

Conventional wisdom says that you should use a DetailsView to insert rows, but I don't want to shift the user between two different controls. You can edit and delete rows with a GridView so why not Insert?

Here is the solution. I am using an ObjectDataSource control to wire up my data access class. If the DataTable coming back from my data class is empty, I add a null row. This works fine but now you get blank rows in the GridView. To handle that, I hide the rows during the RowCreated event. Lastly, I clear the controls off of the now hidden row so that there are no binding errors as would be the case with a cast boolean value which I do to set the value of a CheckBox in my GridView.

You can download the entire solution and sample database here:
http://download.binaryocean.com/GridViewInsertSolution.zip

private bool LoadDataEmpty

{

    //  some controls that are used within a GridView,

    //  such as the calendar control, can cuase post backs.

    //  we need to preserve LoadDataEmpty across post backs.

    get { return (bool)(ViewState["LoadDataEmpty"] ?? false); }

    set { ViewState["LoadDataEmpty"] = value; }

}

protected void ObjectDataSourceMain_Selected(object sender, ObjectDataSourceStatusEventArgs e)
{
    //  bubble exceptions before we touch e.ReturnValue
   
if (e.Exception != null)
   
{
       
throw e.Exception;
    
}

    // get the DataTable from the ODS select mothod
    DataTable dataTable = (DataTable)e.ReturnValue;

    // if rows=0 then add a dummy (null) row and set the LoadDataEmpty flag.

    if (dataTable.Rows.Count == 0)

    {

        dataTable.Rows.Add(dataTable.NewRow());

        LoadDataEmpty = true;

    }

    else

    {

        LoadDataEmpty = false;

    }

}

 

protected void GridViewMain_RowCreated(object sender, GridViewRowEventArgs e)

{

    // when binding a row, look for a zero row condition based on the flag.

    // if we have zero data rows (but a dummy row), hide the grid view row

    // and clear the controls off of that row so they don't cause binding errors

    if (LoadDataEmpty && e.Row.RowType == DataControlRowType.DataRow)

    {

        e.Row.Visible = false;

        e.Row.Controls.Clear();

    }

}

 

protected void GridViewMain_RowCommand(object sender, GridViewCommandEventArgs e)

{

    // handle the save button on the footer row. this is the only manual data operation

    // that must be done. update and delete are handled by the gridview and ODS

    if (e.CommandName == "Save")

    {

        string code = ((TextBox)GridViewMain.FooterRow.FindControl("TextBoxCode")).Text;

        string description = ((TextBox)GridViewMain.FooterRow.FindControl("TextBoxDescription")).Text;

        bool isActive = ((CheckBox)GridViewMain.FooterRow.FindControl("CheckBoxIsActive")).Checked;

 

        new LocationData().Insert(code, description, isActive);

 

        GridViewMain.DataBind();

    }

}

 

 

kick it on DotNetKicks.com   Wednesday, January 04, 2006 11:02:54 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [16]  | 
 Tuesday, January 03, 2006

I wrote previously about a pattern using the coalescing operator (??) that I use to wrap ViewState:

public string Title
{
   
get { return (string)ViewState["title"] ?? "Default title"; }
   
set { ViewState["title"] = value; }
}

and

public string TitleID
{
    get { return (int?)ViewState["titleID"] ?? -1; }
   
set { ViewState["titleID"] = value; }
}

In the latter case, int is a value type and I thought the only way you could get things to work was to cast the ViewState to a nullable int type (int?), but there is a better solution:

public string TitleID
{
   
get { return (int)(ViewState["titleID"] ?? -1); }
   
set { ViewState["titleID"] = value; }
}

The difference is subtle but ViewState is an object type and you can apply the coalescing operator directly to it. Then cast that result to an int.

-Andrew

kick it on DotNetKicks.com   Tuesday, January 03, 2006 6:21:23 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

I just came across these. Three good articles by Dino Esposito on building custom controls:

Building New Controls from the Ground Up:
http://msdn.microsoft.com/library/en-us/dnaspp/html/ContCrshCrsNew.asp

Building Data-Bound Controls:
http://msdn.microsoft.com/library/en-us/dnaspp/html/ContCrshCrsDB.asp

Deriving New Controls from Existing Classes:
http://msdn.microsoft.com/library/en-us/dnaspp/html/controlscrashcourse-deriving.asp

kick it on DotNetKicks.com   Tuesday, January 03, 2006 5:54:46 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Sunday, January 01, 2006
My new blog.
kick it on DotNetKicks.com   Sunday, January 01, 2006 6:10:22 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |